How do I make accurate timings under Win32?

With difficulty :-) This is one of those unfortunate cases where the answer is hedged around with all sorts of OS-specific limitations and caveats. Let's assume you need to measure time down to around a few milliseconds. The first point to make is that the approach you adopt depends upon whether you want to just measure time, or get a timely notification. If you just to want to know the elapsed time between two events in your code you can use QueryPerformanceCounter. There are some mathematical complexities around allowing for counter rollover, but essentially this will tell you the elapsed time you need.

If QueryPerformanceCounter looks like it might be your thing, here's some code that should get the job done:

// QueryPerf sample code. Error-handling omitted. Note that
// QueryPerformanceFrequency can fail if your system doesn't
// support performance counters. READ THE HELP!!
// In the function you want to measure:
   
TCHAR szDebug [100];
   
__int64 liTicksPerSec64;
__int64 liBeginTime64;
__int64 liEndTime64;
   
QueryPerformanceFrequency((LARGE_INTEGER*)&liTicksPerSec64);
QueryPerformanceCounter((LARGE_INTEGER*)&liBeginTime64);

//
// do some time-consuming uninterrupted stuff...
//

QueryPerformanceCounter ((LARGE_INTEGER*)&liEndTime64);
itus = CalcMicroSecInterval (liTicksPerSec64,
                             liBeginTime64,
                             liEndTime64);
   
wsprintf (szDebug, "Time to do stuff: %dms", itus/1000);
OutputDebugString (szDebug);
   
// Helper function:
   
int CalcMicroSecInterval (__int64 liTps64,
                          __int64 liStart64,
                          __int64 liEnd64)
{
   double dTicksPerUs;
   double dInterval;
   __int64 iIntTicks64;
   int iInterval;
   int iTicksIn;
   
   dTicksPerUs = (double)liTps64 / (double)1000000.0;
   if (liEnd64 > liStart64)
      iIntTicks64 = liEnd64 - liStart64;
   else
      iIntTicks64 = liStart64 - liEnd64;

   iTicksIn = (int) iIntTicks64;
   dInterval = ((double) iIntTicks64) / dTicksPerUs;
   iInterval = (int) dInterval;
   return iInterval;
}

If you want a notification, things get more complex. There are the standard Windows timers documented under WM_TIMER, but these suffer from some problems:

  • Limit of accuracy is around 55ms under 95/98/ME, 10-15ms under the NT family.
  • WM_TIMER messages can get "merged" because they're implemented as a flag inside the GetMessage function.
  • You need to run a message pump.

Alternatives are

  • multimedia timers (see the help beginning with timeSetEvent) which will take you down to a theoretical resolution of 1ms.
  • waitable timer objects (NT only - see the help for CreateWaitableTimer and also tip 16).

But there's a fly in this ointment - Windows is not a real-time OS, and kernel-mode software can tromp all over your best-laid plans. There are badly written drivers around that can disable interrupts at the drop of a hat, and all your carefully constructed code won't be worth a damn if the processor can't see the outside world anymore. Typically the culprits are flaky video drivers, but can be pretty much anything. Don't believe me? try writing a little program that sets up multimedia timers and checks their accuracy with performance counters (or download my crude little timetest.exe here). Fortunately, these issues are a lot less prevalent now than when I first wrote this tip, and multi-core processors have helped a lot.

People who absolutely MUST have reliable accurate ms or sub-ms timings under Windows nearly always have to resort to hardware solutions or kernel mode code.

Download