Abstract: This article discusses in detail several functions, gettickcount, queryperformancecounter, and rdtsc, that measure the running time of a program on a Windows platform, and provides sample code.
There are many evaluation indicators for the Quality of algorithms. One of the important indicators is the time complexity. If the two programs complete the same task, that is, they have the same functions and the same data, the shorter running time is better. The operating system and library functions generally provide time measurement functions. Such functions generally return a value representing the current time, call a time function before running a program or code segment to obtain a value. Call the time function again after the program or code segment is run to obtain another value, if the latter is subtracted from the former, it is the running time of the program.
On the windwos platform (windwow95 and later versions, the same below), there are three common functions or methods used to measure time: 1. API function gettickcount or C function clock, 2. API function queryperformancecounter, 3: Assembly command rdstc
1. API function gettickcount:
Function prototype: DWORD gettickcount (void );
This function is used to retrieve the number of milliseconds that have elapsed since the computer was started. That is, the unit of each time is 1 ms. Its resolution is relatively low. It is often used for a long period of measurement. If your program uses more than 100 milliseconds, you can use this function. another function similar to gettickcount is clock. The unit of the return time of the function is clocks_per_sec. in Windows 95/2000, the value is 1000, that is, on windows, these two functions have almost identical functions.
2. API function queryperformancecounter:
Function prototype: BOOL QueryPerformanceCounter (LARGE_INTEGER * lpPerformanceCount); this function retrieves the current high-resolution performance counter, represented by a 64bit number. If your hardware does not support high-resolution performance counters, the system may add zero. Unlike GetTickCount, each counter Unit represents a fixed time of 1 ms. To know the exact execution time of the program, you need to call the QueryPerformanceFrequency function to obtain the number of performance counters per second, that is, the frequency.
3. Assembly command RDTSC:
The RDTSC command reads the "TimeStamp" in the CPU, which is a 64-bit unsigned number counter. After this command is executed, it is stored in the EDX: EAX storage. This command was introduced from intel Pentium CPU. Some old-fashioned CPUs do not support this command. Later CPU, including amd cpu, support this command. Similar to QueryPerformanceCounter, to know the actual execution time of a program, you must know the CPU frequency, that is, the CPU clock speed. Unfortunately, there is no ready-made function to get the CPU frequency. One feasible method is to delay a specified period of time. The time measurement can be done using QueryPerformanceCounter. During this period, RDTSC is called at the beginning and end to get the difference in the number of clocks, then, divide it by the number of seconds.
The following code uses three function encapsulation and test code. The code that uses the RDTSC command for timing refers to the source code of a Ticktest, which is unknown to the author.
GetTime1: Use GetTickCount to return a value indicating the current time, in seconds.
GetTime2, similar to getTime1, has a higher precision.
GetTime3 returns a 64-bit counter. to convert it to seconds, you need to divide it by the CPU frequency. For the sample code, see test3.
# Include "stdafx. h"
# Include "windows. h"
# Include "tchar. h"
Double getTime1 ()
{
DWORD t = GetTickCount ();
Return (double) t/1000.00;
}
Double getTime2 () // use a high-precision timer
{
Static LARGE_INTEGER s_freq;
LARGE_INTEGER performanceCount;
Double t;
If (s_freq.QuadPart = 0)
{
If (! QueryPerformanceFrequency (& s_freq ))
Return 0;
}
QueryPerformanceCounter (& performanceCount );
T = (double) performanceCount. QuadPart/(double) s_freq.QuadPart;
Return t;
}
Void test1 ()
{
Double t1, t2;
T1 = getTime1 ();
Sleep (1000 );
T2 = getTime1 ()-t1;
Printf ("It take %. 8f second/n", t2 );
}
Void test2 ()
{
Double t1, t2;
T1 = getTime2 ();
Sleep (1000 );
T2 = getTime2 ()-t1;
Printf ("It take %. 8f second/n", t2 );
}
Inline BOOL isNTOS () // check whether it is running in the NT Operating System
{
Typedef BOOL (WINAPI * lpfnGetVersionEx) (LPOSVERSIONINFO );
Static int bIsNT =-1;
If (bIsNT! = 1)
Return (BOOL) bIsNT;
// Get Kernel handle
HMODULE hKernel32 = GetModuleHandle (_ T ("KERNEL32.DLL "));
If (hKernel32 = NULL)
Return FALSE;
# Ifdef _ UNICODE
LpfnGetVersionEx lpGetVersionEx = (lpfnGetVersionEx) GetProcAddress (hKernel32, _ T ("GetVersionExW "));
# Else
LpfnGetVersionEx lpGetVersionEx = (lpfnGetVersionEx) GetProcAddress (hKernel32, _ T ("GetVersionExA "));
# Endif
If (lpGetVersionEx)
{
OSVERSIONINFO osvi;
Memset (& osvi, 0, sizeof (OSVERSIONINFO ));
Osvi. dwOSVersionInfoSize = sizeof (OSVERSIONINFO );
If (! LpGetVersionEx (& osvi ))
BIsNT = FALSE;
Else
BIsNT = (osvi. dwPlatformId = VER_PLATFORM_WIN32_NT );
}
Else
{
// Since GetVersionEx is not available we known that
// We are running on NT 3.1 as all modern versions of NT and
// Any version of Windows 95/98 support GetVersionEx
BIsNT = TRUE;
}
Return bIsNT;
}
Inline static BOOL checkRDSTC () // checks whether the CPU supports RDSTC commands
{
Static int bHasRDSTC =-1;
SYSTEM_INFO sys_info;
If (bHasRDSTC! =-1)
Return (BOOL) bHasRDSTC;
GetSystemInfo (& sys_info );
If (sys_info.dwProcessorType = PROCESSOR_INTEL_PENTIUM)
{
Try
{
_ Asm
{
_ Emit 0x0f; rdtsc
_ Emit 0x31
}
}
Catch (...) // Check to see if the opcode is defined.
{
BHasRDSTC = FALSE; return FALSE;
}
// Check to see if the instruction ticks accesses something that changes.
Volatile ULARGE_INTEGER ts1, ts2;
_ Asm
{
Xor eax, eax
_ Emit 0x0f; cpuid
_ Emit 0xa2
_ Emit 0x0f; rdtsc
_ Emit 0x31
Mov ts1.HighPart, edx
Mov ts1.LowPart, eax
Xor eax, eax
_ Emit 0x0f; cpuid
_ Emit 0xa2
_ Emit 0x0f; rdtsc
_ Emit 0x31
Mov ts2.HighPart, edx
Mov ts2.LowPart, eax
}
// If we return true then there's a very good chance it's a real RDTSC instruction!
If (ts2.HighPart> ts1.HighPart)
BHasRDSTC = TRUE;
Else if (ts2.HighPart = ts1.HighPart & ts2.LowPart> ts1.LowPart)
BHasRDSTC = TRUE;
Else
{
Printf ("RDTSC instruction NOT present./n ");
BHasRDSTC = FALSE;
}
}
Else
BHasRDSTC = FALSE;
Return bHasRDSTC;
}
//************************************** *********
Void getTime3 (LARGE_INTEGER * pTime) // returns the internal counter of the current CPU.
{
If (checkRDSTC ())
{
Volatile ULARGE_INTEGER ts;
// On NT don't bother disabling interrupts as doing
// So will generate a priviledge instruction exception
If (! IsNTOS ())
_ Asm cli
//----------------
_ Asm
{
Xor eax, eax
// ------------- Save rigister
Push ebx
Push ecx
_ Emit 0x0f; cpuid-serialise the processor
_ Emit 0xa2
//------------
_ Emit 0x0f; rdtsc
_ Emit 0x31
MoV ts. highpart, EDX
MoV ts. lowpart, eax
Pop ecx
Pop ebx
}
//-----------------
If (! IsNTOS ())
_ Asm sti
//---------
PTime-> QuadPart = ts. QuadPart;
}
Else
PTime-> QuadPart = 0;
}
// MaxDetermainTime: maximum measurement time, in milliseconds. When this function is called for the first time,
// It Takes maxDetermineTime to determine the CPU frequency. In future calls, the value of static variables will be directly added.
Double GetCPUFrequency (DWORD maxDetermineTime)
{
Static double CPU_freq;
LARGE_INTEGER period, t1, t2;
Register LARGE_INTEGER goal, current;
If (CPU_freq> 1000) // this value have been initilization
Return CPU_freq;
If (! QueryPerformanceFrequency (& period) |! CheckRDSTC ())
{
CPU_freq =-1.00;
Return CPU_freq;
}
QueryPerformanceCounter (& goal );
Goal. QuadPart + = period. QuadPart * maxDetermineTime/1000;
GetTime3 (& t1); // start timing
Do // delay maxDetermineTime millisecond
{
QueryPerformanceCounter (& current );
} While (current. QuadPart <goal. QuadPart );
GetTime3 (& t2); // End Time
CPU_freq = double (t2.QuadPart-t1.QuadPart) * 1000/maxDetermineTime );
Char buff [100];
Sprintf (buff, "Estimated the processor clock frequency = % gHz/n", CPU_freq );
: MessageBox (NULL, buff, "", MB_ OK );
Return CPU_freq;
}
Void test3 ()
{
LARGE_INTEGER t, t1, t2;
Double f1, f2;
GetCPUFrequency (100); // takes 0.1 seconds to calculate the CPU frequency
F1 = getTime2 ();
GetTime3 (& t1 );
Sleep (1000 );
GetTime3 (& t2 );
F2 = getTime2 ();
T. QuadPart = t2.QuadPart-t1.QuadPart;
Printf ("It take %. 8f second by gettime3/N", (double) T. quadpart/getcpufrequency (100 ));
Printf ("It take %. 8f second by gettime2/N", f2-f1 );
}
Int main (INT argc, char * argv [])
{
Test1 ();
Test2 ();
Test3 ();
Return 0;
}
This article from the csdn blog, reproduced please indicate the source: http://blog.csdn.net/wzdworld001/archive/2007/06/08/1644111.aspx