header image
 

On WinAPI timers and their resolution

While working on PuttyRec (that is practically rewriting it from scratch) I wondered about resolution of various Windows timer APIs. Of course that’s pretty irrelevant in the context of a terminal recorder, not that you need to cope with more that a few events per second there. But being the curious man I am (and sometimes a perfectionist when it comes to code) I set to investigate the matter myself.

What selection do we have then? There is the SetTimer, of course. PuttyRec used it before and that worked without much problems. SetTimer has its problems though, the biggest being that it’s only available to use on UI threads (that is, threads owning a message queue). Although creating a message queue is easy (just call any message-related API), that’s hardly convenient. It also has reputation of being more than slightly inaccurate.

Then we have timeSetEvent, a multimedia timer. It doesn’t require message queue (but also runs on a separate thread) and is supposed to be more accurate. The MSDN page contains a curious notice though: Note This function is obsolete. New applications should use CreateTimerQueueTimer to create a timer-queue timer. A “better” function, eh? I used it before and it also seemed to work well.

Interestingly MSDN doesn’t specify what resolution these functions have. That’s pretty useless if you need a high-precision timer. I wrote a quick and dirty test program that runs all three of the above timers for a period of one second with varying timer period. As a reference I included a “busy wait” loop that just spins for desired amount of time. Time measurement was done by QueryPerformanceCounter. Without further ado, here are the results (32bit release, but 64bit/debug is roughly the same):

Period: 100ms
CreateTimerQueueTimer    : count=   9, avg error= 0.085%, std dev= 0.193
timeSetEvent             : count=   9, avg error= 0.013%, std dev= 0.017
Busy wait                : count=   9, avg error= 0.000%, std dev= 0.000
SetTimer                 : count=   8, avg error= 9.629%, std dev= 2.060

Period: 50ms
CreateTimerQueueTimer    : count=  19, avg error= 0.109%, std dev= 0.140
timeSetEvent             : count=  19, avg error= 0.027%, std dev= 0.022
Busy wait                : count=  19, avg error= 0.000%, std dev= 0.000
SetTimer                 : count=  15, avg error=24.800%, std dev= 3.117

Period: 20ms
CreateTimerQueueTimer    : count=  49, avg error= 0.177%, std dev= 0.242
timeSetEvent             : count=  49, avg error= 0.106%, std dev= 0.095
Busy wait                : count=  49, avg error= 0.135%, std dev= 0.656
SetTimer                 : count=  31, avg error=55.518%, std dev=12.167

Period: 10ms
CreateTimerQueueTimer    : count=  99, avg error= 0.216%, std dev= 0.197
timeSetEvent             : count=  99, avg error= 0.192%, std dev= 0.257
Busy wait                : count=  99, avg error= 0.122%, std dev= 0.537
SetTimer                 : count=  63, avg error=60.329%, std dev=35.078

Period: 5ms
CreateTimerQueueTimer    : count= 199, avg error= 0.796%, std dev= 3.244
timeSetEvent             : count= 199, avg error= 0.257%, std dev= 0.380
Busy wait                : count= 199, avg error= 0.014%, std dev= 0.106
SetTimer                 : count=  63, avg error=212.087%, std dev=74.290

Period: 2ms
CreateTimerQueueTimer    : count= 499, avg error= 2.291%, std dev=12.107
timeSetEvent             : count= 499, avg error= 0.790%, std dev= 0.831
Busy wait                : count= 499, avg error= 0.006%, std dev= 0.005
SetTimer                 : count=  63, avg error=680.188%, std dev=209.531

Period: 1ms
CreateTimerQueueTimer    : count= 999, avg error= 4.185%, std dev=11.480
timeSetEvent             : count= 999, avg error= 1.268%, std dev= 1.695
Busy wait                : count= 999, avg error= 0.225%, std dev= 3.743
SetTimer                 : count=  62, avg error=1450.387%, std dev=415.344

What can we say about that?

  • Obviously, inaccuracy raises as the period gets smaller.
  • SetTimer is really inaccurate. Even with a 100ms period it was off by 10%, and below that it’s just useless.
  • Busy wait was of course the most accurate.
  • CreateTimerQueueTimer was remarkably worse than the “obsolete” timeSetEvent! What’s going on here?
  • timeSetEvent was performing really well even with the smallest periods. Errors below 1%, low standard deviation. Deprecated my ass.

So there you have it. I’d recommend using timeSetEvent for good precision, and if that’s not enough for you – timeSetEvent with short busy wait at the end to refine the result.

And the source code:

Windows timers microbenchmark code Show

~ by omeg on November 4, 2011.

C/C++, code, winapi

Leave a Reply