High Performance Timing under Windows - ' Measuring Elapsed Time ' (
Page 2 of 3 )
If you're doing any kind of animation or running a process that needs to poll another process or a particular piece of hardware on a regular basis, you need an accurate timer. Depending on your application, the timer might need to be accurate to within one second, or within a fraction of a millisecond. If your timer's resolution is too coarse or its margin of error is too large, your animations will appear jerky or uneven, and your program that's collecting data from custom hardware will miss data or will fail altogether.
Windows has two primary methods of measuring elapsed time, and two ways to provide periodic events.
ADVERTISEMENT
Periodic Events
Standard Windows timers, created by calling the SetTimer function, periodically pass a WM_TIMER message to your window procedure or call a specified TimerProc. Windows timers are easy to use, but are somewhat limited in resolution. In general, you cannot depend on a Windows timer to have accuracy better than 55 milliseconds, and very often the best you'll be able to do is 100 milliseconds: 1/10 second. The nice thing about Windows timers is that they've been around since Windows 3.1 or before and they're quite easy to use. They are not, though, suitable for high performance timing.
Multimedia timers are much more accurate than the standard system timers, and only slightly more complicated to use. On a modern computer that's not overloaded, multimedia timers can provide periodic timer events as often as once every 10 milliseconds, with almost perfect resolution. Getting resolutions of less than 10 milliseconds is possible, but accuracy suffers and the better resolution takes a heavy toll on processor cycles. In theory, you should be able to get one-millisecond resolution with the multimedia timers, but in practice I've found that 10 milliseconds is a practical limit.
Measuring Elapsed Time
The timeGetTime API function, which is part of the multimedia timers API, is perhaps the most-used way of obtaining the current system time. This function returns the time elapsed since Windows was started, and is measured in milliseconds. It has true one-millisecond resolution, in that it can (and will) return successive values that differ by just one millisecond. You can set the minimum resolution by calling the timeBeginPeriod function. Depending on your version of Windows, the default resolution is between 1 and 10 milliseconds. There doesn't appear to be a way to determine what the resolution is, however; if you care, you should go ahead and set it yourself.
To determine how long it takes for some code to execute, simply call timeGetTime to obtain the current time at the start of the run, and then call timeGetTime again to obtain the time when the code in question is finished executing. In C, it looks like this:
DWORD startTime, endTime;
DWORD elapsedTime;
startTime = timeGetTime();
// run some code that takes many milliseconds
endTime = timeGetTime();
elapsedTime = endTime - startTime();
One oddity here is that, because it's a DWORD (32-bit) value, the system time can roll over after approximately 49.71 days. If that rollover happens between the start and end times, the computed elapsedTime value is going to be very large indeed. Many programs—especially games—ignore the possibility, assuming that nobody keeps their machine on for 50 days straight. In most cases, the single timing error—if it happens—will have a negligible effect on the program's operation. If it's a concern, you can write special code to handle the case of the endTime value being smaller than the startTime value.
Windows does have a high-resolution timer that provides sub-millisecond accuracy. You can obtain the timer frequency by calling the QueryPerformanceFrequency function. The resolution of the timer varies, but it's sufficient to provide—in theory—sub-microsecond timing. On my system, for example, QueryPerformanceFrequency returns 3579545, which means that each timer tick is 1/3579545 second or about 0.28 microseconds. But you won't be able to measure time with sub-microsecond accuracy.
You obtain the current value of the high-resolution timer by calling QueryPerformanceCounter. The returned value is a 64-bit integer. Using the high-resolution timer is very similar to using the multimedia timer: get the starting value, run the code that is to be timed, and then get the ending value. Subtracting the starting value from the ending value will tell you how many timer ticks elapsed, and you can divide by the performance frequency to obtain the number of seconds elapsed.
Although the timer itself has a sub-microsecond time base, you can't do sub-microsecond timing because it takes time to obtain the current value of the timer. On a 2 GHz processor, a call to QueryPerformanceCounter takes about five microseconds. Still, that's much better than what you can get from timeGetTime, and quite useful for games and other high-resolution timing needs.
There is one other high-resolution timer that some older programs depended on. That's the processor's time stamp counter, obtained by executing the RDTSC processor instruction. The returned value is the number of processor clock ticks, and is used in much the same way as the value returned by QueryPerformanceCounter. The hard part is determining the processor frequency so that you know which number to divide by. Done right, this can be a very accurate timer, but it's difficult to get right and not many programs need that kind of accuracy. And, like most hardware tricks, it's fragile. Recent technology advances have made the use of RDTSC questionable at best.