Created
July 24, 2021 09:20
-
-
Save MBulli/622109e22324227a1af4af7de80834c5 to your computer and use it in GitHub Desktop.
Questionable benchmark code for pipline communication with gnuplot on windows (see https://sourceforge.net/p/gnuplot/bugs/2204/)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// GnuPlotPerf.cpp : This file contains the 'main' function. Program execution begins and ends there. | |
// | |
#define NOMINMAX | |
#include <Windows.h> | |
#undef near | |
#undef far | |
#include <iostream> | |
#include <sstream> | |
#include <random> | |
#include <chrono> | |
#define BUFSIZE 4096 | |
PROCESS_INFORMATION piProcInfo; | |
HANDLE hChildStdInRx; // read | |
HANDLE hChildStdInTx; // write | |
HANDLE hChildStdOutRx; | |
HANDLE hChildStdOutTx; | |
std::default_random_engine gen; | |
std::uniform_real_distribution<double> distribution(-200.0, 200.0); | |
void ThrowWin32Exception(const std::string& message) | |
{ | |
DWORD dw = GetLastError(); | |
std::stringstream what; | |
LPSTR errorText = NULL; | |
FormatMessageA( | |
FORMAT_MESSAGE_ALLOCATE_BUFFER | | |
FORMAT_MESSAGE_FROM_SYSTEM | | |
FORMAT_MESSAGE_IGNORE_INSERTS, | |
NULL, | |
dw, | |
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | |
(LPSTR)&errorText, | |
0, NULL); | |
what << message << " " << errorText; | |
LocalFree(errorText); | |
throw std::exception(what.str().c_str()); | |
} | |
void startGnuPlotProcess(int version) | |
{ | |
// Set the bInheritHandle flag so pipe handles are inherited. | |
SECURITY_ATTRIBUTES saAttr; | |
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); | |
saAttr.bInheritHandle = TRUE; | |
saAttr.lpSecurityDescriptor = NULL; | |
// Create a pipe for the child process's STDOUT. | |
if (!CreatePipe(&hChildStdOutRx, &hChildStdOutTx, &saAttr, 0)) { | |
ThrowWin32Exception("Failed to create pipe for stdout."); | |
} | |
// Ensure the read handle to the pipe for STDOUT is not inherited. | |
if (!SetHandleInformation(hChildStdOutRx, HANDLE_FLAG_INHERIT, 0)) { | |
ThrowWin32Exception("Failed to disable handle inheritance for stdout pipe."); | |
} | |
// Create a pipe for the child process's STDIN. | |
if (!CreatePipe(&hChildStdInRx, &hChildStdInTx, &saAttr, 0)) { | |
ThrowWin32Exception("Failed to create Pipe for stdin."); | |
} | |
// Ensure the write handle to the pipe for STDIN is not inherited. | |
if (!SetHandleInformation(hChildStdInTx, HANDLE_FLAG_INHERIT, 0)) { | |
ThrowWin32Exception("Failed to disable handle inheritance for stdin pipe."); | |
} | |
// Create the child process. | |
STARTUPINFOA siStartInfo = {}; | |
siStartInfo.cb = sizeof(STARTUPINFOA); | |
siStartInfo.hStdError = hChildStdOutTx; | |
siStartInfo.hStdOutput = hChildStdOutTx; | |
siStartInfo.hStdInput = hChildStdInRx; | |
siStartInfo.dwFlags |= STARTF_USESTDHANDLES; | |
// build command line | |
std::string cmd = ""; | |
switch (version) | |
{ | |
case 528: cmd = "D:\\Temp\\gnuplot\\528\\bin\\gnuplot.exe"; std::cout << "Gnuplot 5.2 patchlevel 8\n"; break; | |
case 540: cmd = "D:\\Temp\\gnuplot\\540\\bin\\gnuplot.exe"; std::cout << "Gnuplot 5.4 patchlevel 0\n"; break; | |
case 541: cmd = "D:\\Temp\\gnuplot\\541\\bin\\gnuplot.exe"; std::cout << "Gnuplot 5.4 patchlevel 1\n"; break; | |
case 542: cmd = "D:\\Temp\\gnuplot\\542\\bin\\gnuplot.exe"; std::cout << "Gnuplot 5.4 patchlevel 2\n"; break; | |
case 550: cmd = "D:\\Temp\\gnuplot\\550\\bin\\gnuplot.exe"; std::cout << "Version 5.5 patchlevel 0 last modified 2021-07-12\n"; break; | |
default: | |
break; | |
} | |
// Use ACSII to not to deal with std::string LPTSTR conversion | |
BOOL bSuccess = CreateProcessA( | |
cmd.c_str(), // binary | |
NULL, // command line | |
NULL, // process security attributes | |
NULL, // primary thread security attributes | |
TRUE, // handles are inherited | |
0, // creation flags | |
NULL, // use parent's environment | |
NULL, // use parent's current directory | |
&siStartInfo, // STARTUPINFO pointer | |
&piProcInfo); // receives PROCESS_INFORMATION | |
if (!bSuccess) { | |
ThrowWin32Exception("Failed to create gnuplot process."); | |
} | |
} | |
void sendData() | |
{ | |
const int N = 500000; | |
std::cout << "N = " << N << std::endl; | |
std::stringstream data; | |
data << "set terminal qt" << std::endl; | |
data << "splot '-' with points" << std::endl; | |
for (size_t i = 0; i < N; i++) | |
{ | |
double x = distribution(gen); | |
double y = distribution(gen); | |
double z = distribution(gen); | |
data << x << " " << y << " " << z << std::endl; | |
} | |
data << "e" << std::endl; | |
data << "unset multiplot" << std::endl; | |
DWORD dwRead = data.str().size(); | |
DWORD dwWritten; | |
BOOL bSuccess = FALSE; | |
std::cout << "Writing " << dwRead << " bytes" << std::endl; | |
auto start = std::chrono::high_resolution_clock::now(); | |
if (!WriteFile(hChildStdInTx, data.str().c_str(), dwRead, &dwWritten, NULL)) { | |
ThrowWin32Exception("Failed to write to stdin."); | |
} | |
auto finish = std::chrono::high_resolution_clock::now(); | |
std::chrono::duration<double> elapsed = finish - start; | |
std::cout << "Elapsed time: " << elapsed.count() << " s\n"; | |
std::cout << "Wrote " << dwWritten << " bytes" << std::endl; | |
} | |
void readData() | |
{ | |
DWORD dwRead, dwWritten; | |
CHAR chBuf[BUFSIZE]; | |
BOOL bSuccess = FALSE; | |
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE); | |
for (;;) | |
{ | |
bSuccess = ReadFile(hChildStdOutRx, chBuf, BUFSIZE, &dwRead, NULL); | |
if (!bSuccess || dwRead == 0) break; | |
bSuccess = WriteFile(hParentStdOut, chBuf, | |
dwRead, &dwWritten, NULL); | |
if (!bSuccess) break; | |
} | |
} | |
int main() | |
{ | |
auto versions = std::vector<int>{ 528, 540, 541, 542, 550 }; | |
std::cout << "Start.\n"; | |
startGnuPlotProcess(versions[4]); | |
for (int i = 0; i < 1; i++) | |
{ | |
sendData(); | |
Sleep(500); | |
} | |
std::cout << "Done.\n"; | |
readData(); | |
while (1) | |
{ | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
On AMD Ryzen 7 7735HS it takes about 22 seconds (!)
Average CPU speed during tests is ~4.5 GHz
Gnuplot version 6.0 patchlevel rc1