/******************************Module*Header*******************************\ * Module Name: Perf.c * * Performance counter functions. Uses the Pentium performance counters * if they are available, otherwise falls back to the system QueryPerformance * api's. * * InitPerfCounter MUST be called before using the QUERY_PERFORMANCE_XXX macros * as it initializes the two global functions pointers. * * * * Created: 13-10-95 * Author: Stephen Estrop [StephenE] * * Copyright (c) 1994 - 1995 Microsoft Corporation. All Rights Reserved. \**************************************************************************/ #include #include "Perf.h" PERFFUNCTION lpQueryPerfCounter; PERFFUNCTION lpQueryPerfFreqency; void GetFrequencyEstimate( LARGE_INTEGER *li ); #ifdef TEST #include /******************************Public*Routine******************************\ * main * * Program entry point. * * History: * dd-mm-95 - StephenE - Created * \**************************************************************************/ int __cdecl main( void ) { LARGE_INTEGER liP1; LARGE_INTEGER liP2; LARGE_INTEGER liPf; InitPerfCounter(); QUERY_PERFORMANCE_FREQUENCY(&liPf); // Time a 50 milli second sleep QUERY_PERFORMANCE_COUNTER(&liP1); Sleep(50); QUERY_PERFORMANCE_COUNTER(&liP2); printf("Pentium counter frequency = %u\n", liPf.LowPart ); printf("Pentium counter %#X%X - %#X%X = %u\n", liP2.HighPart, liP2.LowPart, liP1.HighPart, liP1.LowPart, liP2.LowPart - liP1.LowPart ); printf("Time taken = %6.6f seconds\n", (double)(liP2.LowPart - liP1.LowPart) / (double)liPf.QuadPart); return 0; } #endif /******************************Public*Routine******************************\ * InitPerfCounter * * Determine (at runtime) if it is possible to use the Pentium performance * counter. If it is not fall back to the system performance counter. * * History: * dd-mm-95 - StephenE - Created * \**************************************************************************/ void InitPerfCounter( void ) { SYSTEM_INFO sysInfo; GetSystemInfo(&sysInfo); if (sysInfo.dwProcessorType == PROCESSOR_INTEL_PENTIUM) { lpQueryPerfFreqency = QueryPerfFrequency; lpQueryPerfCounter = QueryPerfCounter; } else { lpQueryPerfFreqency = (PERFFUNCTION)QueryPerformanceFrequency; lpQueryPerfCounter = (PERFFUNCTION)QueryPerformanceCounter; } } /******************************Public*Routine******************************\ * QueryPerfFrequency * * Determines the clock frequency of a (Pentium) microprocessor. Takes an * averaged estimate of the clk frequency and then matches it to known * Pentium clock frequencies. Returns the estimate if a match is not found. * * This is an expensive call in cpu terms as it takes at least 16 milli seconds * just to calculate an averaged estimate of the clock speed. You only need * to call this function once, make sure you don't call it more times. * * History: * 13-10-95 - StephenE - Created * \**************************************************************************/ void WINAPI QueryPerfFrequency( LARGE_INTEGER *li ) { #ifdef _X86_ #define SAMPLE_SIZE 8 LARGE_INTEGER est; int i; li->QuadPart = 0; for (i = 0; i < SAMPLE_SIZE; i++) { GetFrequencyEstimate(&est); li->QuadPart += est.QuadPart; } li->QuadPart /= SAMPLE_SIZE; // // At the moment Pentiums come in 60, 66, 75, 90, 100, 120 and 133 MHz // clock speeds. So use the above estimation of the clock frequency // to determine the real clock frequency. // // 59Mhz to 61Mhz assume its a 60 Mhz if (li->QuadPart >= 59000000 && li->QuadPart < 61000000) { li->QuadPart = 60000000; } // 65Mhz to 67Mhz assume its a 66 Mhz else if (li->QuadPart >= 65000000 && li->QuadPart < 67000000) { li->QuadPart = 66000000; } // 74Mhz to 76Mhz assume its a 75 Mhz else if (li->QuadPart >= 74000000 && li->QuadPart < 76000000) { li->QuadPart = 75000000; } // 89Mhz to 91Mhz assume its a 90 Mhz else if (li->QuadPart >= 89000000 && li->QuadPart < 91000000) { li->QuadPart = 90000000; } // 99Mhz to 101Mhz assume its a 100 Mhz else if (li->QuadPart >= 99000000 && li->QuadPart < 101000000) { li->QuadPart = 100000000; } // 119Mhz to 121Mhz assume its a 120 Mhz else if (li->QuadPart >= 119000000 && li->QuadPart < 121000000) { li->QuadPart = 120000000; } // 132Mhz to 134Mhz assume its a 133 Mhz else if (li->QuadPart >= 132000000 && li->QuadPart < 134000000) { li->QuadPart = 133000000; } // if use our estimate. #else li->QuadPart = -1; #endif } /*****************************Private*Routine******************************\ * GetFrequencyEstimate * * Uses the system QueryPerformance counter to estimate the Pentium * cpu clock * frequency * * History: * 13-10-95 - StephenE - Created * \**************************************************************************/ void GetFrequencyEstimate( LARGE_INTEGER *li ) { LARGE_INTEGER liP1; // Pentium clk start LARGE_INTEGER liP2; // Pentium clk end LARGE_INTEGER liS1; // System clk end LARGE_INTEGER liS2; // System clk end LARGE_INTEGER liSf; // System clk frequency QueryPerformanceFrequency(&liSf); QueryPerformanceCounter(&liS1); QueryPerfCounter(&liP1); Sleep(2); // Sleep for approx 2 milli- seconds QueryPerfCounter(&liP2); QueryPerformanceCounter(&liS2); // // Determine the time recorded by both clocks. // liP2.QuadPart = liP2.QuadPart - liP1.QuadPart; liS2.QuadPart = liS2.QuadPart - liS1.QuadPart; li->QuadPart = (liP2.QuadPart * liSf.QuadPart) / liS2.QuadPart; } /******************************Public*Routine******************************\ * QueryPerfCounter * * Query the internal clock counter on the Pentium, uses the undocumented * rdtsc instruction, which copies the current 64 bit clock count into * edx:eax. * * History: * 13-10-95 - StephenE - Created * \**************************************************************************/ void WINAPI QueryPerfCounter( LARGE_INTEGER *li ) { #ifdef _X86_ _asm mov ecx, dword ptr li // copy li pointer value to ecx _asm _emit 0x0f // opcode 0x0F31 is rdtsc _asm _emit 0x31 _asm mov dword ptr [ecx], eax // save result in li->LowPart _asm mov dword ptr [ecx+4], edx // and li->HighPart #else ; #endif }