// // SingBench.c - A benchmark program // // Copyright Microsoft Corporation. // All rights reserved. // #include #include #include LARGE_INTEGER frequency; int perfLoopCount = 10000; HANDLE waitPerfEvent1, waitPerfEvent2; ULONG64 startCount1, endCount1; ULONG64 startCount2, endCount2; void ErrorExit(char *function) { VOID *message; DWORD error = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &message, 0, NULL ); printf("%s failed with error %d: %s", function, error, message); LocalFree(message); ExitProcess(error); } // // Read the processor's time stamp counter register // #define GetCycleCount() __rdtsc() ULONG64 __rdtsc(VOID); #pragma intrinsic(__rdtsc) // // Measure GetCycleCount call. // void DoGetCCPerf() { ULONG64 before, after, elapsed; ULONG64 now; int loop; printf("\nMeasuring GetCycleCount call\n"); before = GetCycleCount(); for (loop = 0; loop < perfLoopCount; loop++) { now = GetCycleCount(); } after = GetCycleCount(); elapsed = after - before; printf(" Elapsed cycles = %d\n", elapsed); printf(" cycles/iteration = %d\n", elapsed / perfLoopCount); } // // Measure ABI call. // void DoABIPerf() { ULONG64 before, after, elapsed; int loop; DWORD dummy; printf("\nMeasuring ABI call using GetProcessId\n"); before = GetCycleCount(); for (loop = 0; loop < perfLoopCount; loop++) { dummy = GetProcessId(GetCurrentProcess()); } after = GetCycleCount(); elapsed = after - before; printf(" Elapsed cycles = %d\n", elapsed); printf(" cycles/iteration = %d\n", elapsed / perfLoopCount); } // // Measure ABI call. // void DoABIPerf2() { ULONG64 before, after, elapsed; int loop; LARGE_INTEGER dummy; printf("\nMeasuring ABI call using QueryPerformanceFrequency\n"); before = GetCycleCount(); for (loop = 0; loop < perfLoopCount; loop++) { (void)QueryPerformanceFrequency(&dummy); } after = GetCycleCount(); elapsed = after - before; printf(" Elapsed cycles = %d\n", elapsed); printf(" cycles/iteration = %d\n", elapsed / perfLoopCount); } void DoABIPerf3() { HANDLE hFile; LPCSTR lpFileName; char tempBuffer[TMP_MAX]; ULONG64 before, after, elapsed; int loop; printf("\nMeasuring ABI call using SetFilePointer\n"); lpFileName = tmpnam(tempBuffer); hFile = CreateFile(lpFileName, 0, FILE_SHARE_DELETE, NULL, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); if (hFile == INVALID_HANDLE_VALUE) { ErrorExit("CreateFile"); return; } before = GetCycleCount(); for (loop = 0; loop < perfLoopCount; loop++) { SetFilePointer(hFile, 0, NULL, FILE_BEGIN); } after = GetCycleCount(); elapsed = after - before; printf(" Elapsed cycles = %d\n", elapsed); printf(" cycles/iteration = %d\n", elapsed / perfLoopCount); } void DoABIPerf4() { HANDLE hFile; LPCSTR lpFileName; char tempBuffer[TMP_MAX]; ULONG64 before, after, elapsed; int loop; printf("\nMeasuring ABI call using GetFilePointer\n"); lpFileName = tmpnam(tempBuffer); hFile = CreateFile(lpFileName, 0, FILE_SHARE_DELETE, NULL, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); if (hFile == INVALID_HANDLE_VALUE) { ErrorExit("CreateFile"); return; } before = GetCycleCount(); for (loop = 0; loop < perfLoopCount; loop++) { // Returns file pointer position SetFilePointer(hFile, 0, NULL, FILE_CURRENT); } after = GetCycleCount(); elapsed = after - before; printf(" Elapsed cycles = %d\n", elapsed); printf(" cycles/iteration = %d\n", elapsed / perfLoopCount); } static volatile int g_threads = 0; typedef struct s_YieldStats { ULONG64 start; ULONG64 end; UINT32 calls; } YieldStats; static DWORD WINAPI ThreadYieldTestMain(LPVOID lpParameter) { YieldStats* stats = (YieldStats*)lpParameter; g_threads++; while (g_threads != 2) { Sleep(0); } stats->start = GetCycleCount(); while (stats->calls++ < 10000) { Sleep(0); } stats->end = GetCycleCount(); g_threads--; while (g_threads != 0) { Sleep(0); } return 0; } // // Measure thread yield. // void DoYieldPerf() { YieldStats stats1, stats2; HANDLE thread1; printf("\nYield performance\n"); ZeroMemory(&stats1, sizeof(YieldStats)); ZeroMemory(&stats2, sizeof(YieldStats)); thread1 = CreateThread(NULL, 0, &ThreadYieldTestMain, (LPVOID) &stats1, 0, NULL); if (thread1 == INVALID_HANDLE_VALUE) { ErrorExit("CreateThread(thread1)"); } ThreadYieldTestMain((LPVOID) &stats2); CloseHandle(thread1); printf("Thread1 yield perf = %I64u / (2 * %u) = %I64u\n", stats1.end - stats1.start, stats1.calls, (stats1.end - stats1.start) / (2 * stats1.calls)); printf("Thread2 yield perf = %I64u / (2 * %u) = %I64u\n", stats2.end - stats2.start, stats2.calls, (stats2.end - stats2.start) / (2 * stats2.calls)); } // // Part of DoWaitPerf. // DWORD WINAPI WaitPerfThread1(void *foo) { int loop; startCount1 = GetCycleCount(); for (loop = 0; loop < perfLoopCount; loop++) { SetEvent(waitPerfEvent2); WaitForSingleObject(waitPerfEvent1, INFINITE); } endCount1 = GetCycleCount(); return 0; } // // Another part of DoWaitPerf. // DWORD WINAPI WaitPerfThread2(void *foo) { int loop; startCount2 = GetCycleCount(); for (loop = 0; loop < perfLoopCount; loop++) { WaitForSingleObject(waitPerfEvent2, INFINITE); SetEvent(waitPerfEvent1); } SetEvent(waitPerfEvent1); endCount2 = GetCycleCount(); return 0; } // // Measure waiting/signaling of auto-reset events. // void DoWaitPerf() { HANDLE thread1, thread2; // // Create the auto-reset events. // waitPerfEvent1 = CreateEvent(NULL, FALSE, FALSE, NULL); if (waitPerfEvent1 == NULL) { ErrorExit("CreateEvent"); } waitPerfEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL); if (waitPerfEvent2 == NULL) { ErrorExit("CreateEvent"); } printf("\nStarting WaitTest threads\n"); // // Create the threads. // thread1 = CreateThread(NULL, 0, WaitPerfThread1, NULL, CREATE_SUSPENDED, NULL); if (thread1 == NULL) { ErrorExit("CreateThread"); } thread2 = CreateThread(NULL, 0, WaitPerfThread2, NULL, CREATE_SUSPENDED, NULL); if (thread2 == NULL) { ErrorExit("CreateThread"); } // // Start the threads running. // if (ResumeThread(thread2) == -1) { ErrorExit("ResumeThread"); } if (ResumeThread(thread1) == -1) { ErrorExit("ResumeThread"); } // // Output results after both threads have finished. // printf("Waiting for threads...\n"); WaitForSingleObject(thread2, INFINITE); printf("Thread 2 finished\n"); printf(" Elapsed cycles = %d\n", endCount2 - startCount2); printf(" cycles/iteration = %d\n", (endCount2 - startCount2) / perfLoopCount); WaitForSingleObject(thread1, INFINITE); printf("Thread 1 finished\n"); printf(" Elapsed cycles = %d\n", endCount1 - startCount1); printf(" cycles/iteration = %d\n", (endCount1 - startCount1) / perfLoopCount); (void)CloseHandle(waitPerfEvent1); (void)CloseHandle(waitPerfEvent2); } // // Client process for DoPong. // void DoPing(bytes) { HANDLE pipe; DWORD mode; char *buffer; int loop; int got, wrote; // // Allocate some buffer space. // buffer = malloc(bytes); // // Establish connection to server. // pipe = CreateFile("\\\\.\\pipe\\SingBench", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (pipe == INVALID_HANDLE_VALUE) { ErrorExit("CreateFile"); } // // We want message, not byte-stream mode. // mode = PIPE_READMODE_MESSAGE; if (!SetNamedPipeHandleState(pipe, &mode, NULL, NULL)) { ErrorExit("SetNamedPipeHandleState"); } // // Time the exchange of messages. // for (loop = 0; loop < perfLoopCount; loop++) { // // Write the message to the server. // if (!WriteFile(pipe, buffer, bytes, &wrote, NULL) || wrote != bytes) { ErrorExit("WriteFile"); } // // Read the message back. // if (!ReadFile(pipe, buffer, bytes, &got, NULL) || got != bytes) { ErrorExit("ReadFile"); } } CloseHandle(pipe); } // // Test sending messages back and forth between processes. // void DoPong(int bytes) { char szCmdLine[256]; char *buffer; HANDLE pipe; BOOL created, connected; STARTUPINFO startInfo; PROCESS_INFORMATION procInfo; ULONG64 before, after, delta; int loop; int got, wrote; printf("\nStarting ping-pong test with %d byte messages\n", bytes); // // Allocate some buffer space. // buffer = malloc(bytes); // // We use named pipes instead of anonymous pipes because anonymous // pipes are only one-way, while named pipes can be duplex. // // Named pipes can either be byte-stream or message-based. We're // using message-based (closer match to Singularity Channels). // We also set our pipe to blocking mode. // pipe = CreateNamedPipe("\\\\.\\pipe\\SingBench", // Pipe name. PIPE_ACCESS_DUPLEX, // Bi-directional. PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, // Pipe mode. 1, // MaxInstances. bytes, // OutBufferSize. bytes, // InBufferSize. INFINITE, // DefaultTimeOut. NULL); // SecurityAttributes. if (pipe == INVALID_HANDLE_VALUE) { free(buffer); ErrorExit("CreateNamedPipe"); } // // Spawn child (client) process. // sprintf(szCmdLine, "SingBench %d", bytes); printf("Launching child process: %s\n", szCmdLine); ZeroMemory(&startInfo, sizeof(startInfo)); startInfo.cb = sizeof(startInfo); created = CreateProcess("SingBench.exe", // Application name to start. szCmdLine, // Command line for new process. NULL, // ? child process can inherit this process. NULL, // ? child threads can inherit this process. FALSE, // New process inherits our handles? 0, // Process creation flags. NULL, // Environment. NULL, // Current directory. &startInfo, // Startup info. &procInfo); // Out: Process info. if (!created) { // // Failed to create process. // free(buffer); ErrorExit("CreateProcess"); } // // Wait for child to phone home (if it hasn't already). // connected = ConnectNamedPipe(pipe, NULL); if (!connected && GetLastError() != ERROR_PIPE_CONNECTED) { free(buffer); ErrorExit("ConnectNamedPipe"); } // // Time the exchange of messages. // before = GetCycleCount(); for (loop = 0; loop < perfLoopCount; loop++) { // // Read the message from the client. // if (!ReadFile(pipe, buffer, bytes, &got, NULL) || got != bytes) { ErrorExit("ReadFile"); } // // Write the message back. // if (!WriteFile(pipe, buffer, bytes, &wrote, NULL) || wrote != bytes) { ErrorExit("WriteFile"); } } after = GetCycleCount(); delta = (after - before) / 2; FlushFileBuffers(pipe); DisconnectNamedPipe(pipe); CloseHandle(pipe); free(buffer); printf("Done playing ping-pong with %d byte messages\n", bytes); printf(" Elapsed cycles = %d\n", delta); printf(" cycles/iteration = %d\n", delta / perfLoopCount); } int __cdecl main(int argc, char **argv) { int i; if (argc > 1) { // // If called with an argument, we're the DoPong client. // DoPing(atoi(argv[1])); ExitProcess(0); } printf("\nSingBench\n\n"); // // The frequency of the high-resolution performance counter cannot change // while the system is running. // if (QueryPerformanceFrequency(&frequency) == 0) { ErrorExit("QueryPerformanceFrequency"); } printf("Performance Counter Frequency = %I64u counts/second\n", frequency); DoGetCCPerf(); DoABIPerf(); DoABIPerf2(); DoABIPerf3(); DoABIPerf4(); DoYieldPerf(); DoWaitPerf(); for (i = 1; i <= 65536; i *= 2) { DoPong(i); } }