A Thread s Execution Times

[Previous] [Next]

Sometimes you want to time how long it takes a thread to perform a particular task. What many people do is write code similar to the following:

 // Get the current time (start time). DWORD dwStartTime = GetTickCount(); // Perform complex algorithm here. // Subtract start time from current time to get duration. DWORD dwElapsedTime = GetTickCount() - dwStartTime; 

This code makes a simple assumption: it won't be interrupted. However, in a preemptive operating system, you never know when your thread will be scheduled CPU time. When CPU time is taken away from your thread, it becomes more difficult to time how long it takes your thread to perform various tasks. What we need is a function that returns the amount of CPU time that the thread has received. Fortunately, Windows offers a function called GetThreadTimes that returns this information:

 BOOL GetThreadTimes( HANDLE hThread, PFILETIME pftCreationTime, PFILETIME pftExitTime, PFILETIME pftKernelTime, PFILETIME pftUserTime); 

GetThreadTimes returns four different time values, as shown in the following table.

Time Value Meaning
Creation time An absolute value expressed in 100-nanosecond intervals past midnight on January 1, 1601, at Greenwich, England, indicating when the thread was created.
Exit time An absolute value expressed in 100-nanosecond intervals past midnight on January 1, 1601, at Greenwich, England, indicating when the thread exited. If the thread is still running, the exit time is undefined.
Kernel time A relative value indicating how many 100-nanosecond intervals of CPU time the thread has spent executing operating system code.
User time A relative value indicating how many 100-nanosecond intervals of CPU time the thread has spent executing application code.

Using this function, you can determine the amount of time needed to execute a complex algorithm by using code such as this:

 _ _int64 FileTimeToQuadWord (PFILETIME pft) { return(Int64ShllMod32(pft->dwHighDateTime, 32) | pft->dwLowDateTime); } void PerformLongOperation () { FILETIME ftKernelTimeStart, ftKernelTimeEnd; FILETIME ftUserTimeStart, ftUserTimeEnd; FILETIME ftDummy; _ _int64 qwKernelTimeElapsed, qwUserTimeElapsed, qwTotalTimeElapsed; // Get starting times. GetThreadTimes(GetCurrentThread(), &ftDummy, &ftDummy, &ftKernelTimeStart, &ftUserTimeStart); // Perform complex algorithm here. // Get ending times. GetThreadTimes(GetCurrentThread(), &ftDummy, &ftDummy, &ftKernelTimeEnd, &ftUserTimeEnd); // Get the elapsed kernel and user times by converting the start // and end times from FILETIMEs to quad words, and then subtract // the start times from the end times. qwKernelTimeElapsed = FileTimeToQuadWord(&ftKernelTimeEnd) - FileTimeToQuadWord(&ftKernelTimeStart); qwUserTimeElapsed = FileTimeToQuadWord(&ftUserTimeEnd) - FileTimeToQuadWord(&ftUserTimeStart); // Get total time duration by adding the kernel and user times. qwTotalTimeElapsed = qwKernelTimeElapsed + qwUserTimeElapsed; // The total elapsed time is in qwTotalTimeElapsed. } 

Note that GetProcessTimes, a function similar to GetThreadTimes, applies to all of the threads in a process:

 BOOL GetProcessTimes( HANDLE hProcess, PFILETIME pftCreationTime, PFILETIME pftExitTime, PFILETIME pftKernelTime, PFILETIME pftUserTime); 

GetProcessTimes returns times that apply to all the threads in a specified process (even threads that have terminated). For example, the kernel time returned is the sum of all the elapsed times that all of the process's threads have spent in kernel code.

Windows 98
Unfortunately, the GetThreadTimes and GetProcessTimes functions are not functional in Windows 98. Under Windows 98, there is no reliable mechanism for an application to determine how much CPU time a thread or process has used.

For high-resolution profiling, the GetThreadTimes function is not good enough. Windows does offer these high-resolution performance functions:

 BOOL QueryPerformanceFrequency(LARGE_INTEGER* pliFrequency); BOOL QueryPerformanceCounter(LARGE_INTEGER* pliCount); 

These functions assume that the executing thread does not get preempted, but most high-resolution profiling is done for short-lived blocks of code anyway. To make working with these functions a little easier, I have created the following C++ class:

 class CStopwatch { public: CStopwatch() { QueryPerformanceFrequency(&m_liPerfFreq); Start(); } void Start() { QueryPerformanceCounter(&m_liPerfStart); } _ _int64 Now() const { // Returns # of milliseconds since Start was called LARGE_INTEGER liPerfNow; QueryPerformanceCounter(&liPerfNow); return(((liPerfNow.QuadPart - m_liPerfStart.QuadPart) * 1000) / m_liPerfFreq.QuadPart); } private: LARGE_INTEGER m_liPerfFreq; // Counts per second LARGE_INTEGER m_liPerfStart; // Starting count }; 

I use this class as follows:

 // Create a stopwatch timer (which defaults to the current time). CStopwatch stopwatch; // Execute the code I want to profile here. // Get how much time has elapsed up to now. _ _int64 qwElapsedTime = stopwatch.Now(); // qwElapsedTime indicates how long the profiled code // executed in milliseconds. 



Programming Applications for Microsoft Windows
Programming Applications for Microsoft Windows (Microsoft Programming Series)
ISBN: 1572319968
EAN: 2147483647
Year: 1999
Pages: 193

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net