timeGetTime versus GetTickCount

I have a confession to make – I only just found out the difference between the Windows functions GetTickCount and timeGetTime.

I know what you’re thinking – I’m a game developer and I should know better than that. But I didn’t. And I’m sorry.

Naturally I’m handling my ignorance by writing some test code and then blogging about it. My therapist thought it would help me resolve the inner conflicts which this has triggered.

Testation

I got started on this investigation because of a post on Larry Osterman’s blog. His test code spins in a loop calling Sleep(53) and then measuring how much GetTickCount and timeGetTime change during each iteration. His observation was that dTime was extremely consistent, but dTick varied wildly. My results were different, showing dTick and dTime being slightly different, but equally consistent:

Tick: 32027332, Time: 32027332, dTick:  62, dTime:  63
Tick: 32027395, Time: 32027394, dTick:  63, dTime:  62
Tick: 32027457, Time: 32027456, dTick:  62, dTime:  62
Tick: 32027520, Time: 32027519, dTick:  63, dTime:  63
Tick: 32027582, Time: 32027581, dTick:  62, dTime:  62
Tick: 32027644, Time: 32027644, dTick:  62, dTime:  63
Tick: 32027707, Time: 32027706, dTick:  63, dTime:  62
Tick: 32027769, Time: 32027768, dTick:  62, dTime:  62

Without giving away too much I can tell you that the problem with Larry’s code is that it tests not just GetTickCount and timeGetTime, but also the behavior of Sleep(53), and hence the entire Windows scheduler, which is known to be complicated. And, Larry’s computer was (apparently without him realizing) running in a non-recommended configuration. On the other hand, his basic conclusions are correct.

(see my post Sleep Variation Investigated for details)

So, how can we better test the difference between GetTickCount and timeGetTime?

Better testation

Both GetTickCount and timeGetTime return unsigned integers that represent a millisecond count. So, it can be interesting to call them in a busy loop and see what values (deltas from a baseline actually) we get back, thus mostly taking the scheduler out of the equation. This is easily done with this code fragment:

char results[512] = {};

DWORD startTick = GetTickCount();
DWORD startTime = timeGetTime();
while (clock() < endClock)
{
    DWORD elapsedTick = GetTickCount() – startTick;
    if (elapsedTick < ARRAYSIZE(results))
        results[elapsedTick] |= 1;
    DWORD elapsedTime =  timeGetTime() – startTime;
    if (elapsedTime < ARRAYSIZE(results))
        results[elapsedTime] |= 2;
}

In short, spin for a while calling GetTickCount and timeGetTime as fast as possible, and record the millisecond offsets that are returned into the results array, with ‘1’ for GetTickCount and ‘2’ for timeGetTime. The results, printed out in a 32 element wide table, look like this:

3              21              2
1             21              21
              3              21
             21             21
            21              3
           21              21
          21              21
          3              21
         21             21
        21              3
       21              21
      21              21
      3              21
     21             21
    21              3
   21              21

I think that’s totally cool. ‘1’ shows up for millisecond offsets where GetTickCount returned a value, ‘2’ shows up for millisecond offsets where timeGetTime returned a value, and ‘3’ shows up for millisecond offsets where they both returned a value. Given that the table is 32 elements wide we can see that the results of both functions change about every 15-16 ms, giving us two nearly vertical lines.

This result is because these functions return a timer value that is updated by an interrupt, and on my laptop, according to clockres, that interrupt fires every 15.6 ms. If we look closely we can see that the columns tilt left about 0.8 columns every row, which is about 0.4 ms every 16 ms.

In other words, despite calling GetTickCount and timeGetTime literally millions of times per second, the values returned only changed about every 15.6 ms.

So far GetTickCount and timeGetTime look pretty much identical, with a sometimes one millisecond offset.

Documentabulous

Before proceeding it’s worth looking at some excerpts from the MSDN documentation for these two functions. On the one hand we have GetTickCount:

The return value is the number of milliseconds that have elapsed since the system was started.

Remarks

The resolution of the GetTickCount function is limited to the resolution of the system timer, which is typically in the range of 10 milliseconds to 16 milliseconds. The resolution of the GetTickCount function is not affected by adjustments made by the GetSystemTimeAdjustment function.

On the other hand we have timeGetTime:

Returns the system time, in milliseconds.

Remarks

The only difference between this function and the timeGetSystemTime function is that timeGetSystemTime uses the MMTIME structure to return the system time. The timeGetTime function has less overhead than timeGetSystemTime.

The default precision of the timeGetTime function can be five milliseconds or more, depending on the machine. You can use the timeBeginPeriod and timeEndPeriod functions to increase the precision of timeGetTime. If you do so, the minimum difference between successive values returned by timeGetTime can be as large as the minimum period value set using timeBeginPeriod and timeEndPeriod.

The documentation does a poor job of explaining the differences between these functions. It doesn’t make it clear whether they have the same zero point, and it is unnecessarily obscure about the differences. However, it does give some hints. In particular, if we assume that GetTickCount is not affected by both GetSystemTimeAdjustment and by timeBeginPeriod, and if we also assume that timeGetTime is affected by these functions then we have something to work with.

Fast testing

Let’s try running our tests after calling timeBeginPeriod(1) to set the timer interval to 1 ms. First Larry’s test:

Tick: 32028908, Time: 32028915, dTick:  47, dTime:  53
Tick: 32028970, Time: 32028968, dTick:  62, dTime:  53
Tick: 32029017, Time: 32029021, dTick:  47, dTime:  53
Tick: 32029064, Time: 32029074, dTick:  47, dTime:  53
Tick: 32029126, Time: 32029127, dTick:  62, dTime:  53
Tick: 32029173, Time: 32029180, dTick:  47, dTime:  53
Tick: 32029236, Time: 32029233, dTick:  63, dTime:  53
Tick: 32029282, Time: 32029286, dTick:  46, dTime:  53

Note that we can now reproduce Larry’s results, with dTick varying significantly and dTime being consistent. Now let’s look at the busy loop test results:

3              3               3
22222222222222322222222222222232
22222222222222322222222222222322
22222222222223222222222222223222
22222222222232222222222222223222
22222222222322222222222222232222
22222222223222222222222222322222
22222222223222222222222223222222
22222222232222222222222232222222
22222222322222222222222232222222
22222223222222222222222322222222
22222232222222222222223222222222
22222232222222222222232222222222
22222322222222222222322222222222
22223222222222222222322222222222
22232222222222222223222222222222

I may just be a nerd, but I think this is fascinating. We can see immediately that timeGetTime, represented by ‘2’, is now being updated every millisecond. Meanwhile GetTickCount is being updated at the same old rate as before – every 15.6 ms. That means that even though the timer interrupt now fires every ms Windows is continuing to simulate the 15.6 ms interrupt interval when you call GetTickCount.

Note also that the first row of busy loop test results still has a long timer interval. Apparently it takes about 32 ms before the effect of timeBeginPeriod shows up, but obviously you shouldn’t rely on those precise details.

Performance

Both GetTickCount and timeGetTime have to convert from a high frequency timer to a millisecond frequency timer, so one might expect them to have similar performance. However timeGetTime actually takes about twice as long to execute. Stepping through the assembly language (it’s all user mode code) shows that GetTickCount converts from an interrupt count to a millisecond count using multiplication and no function calls. timeGetTime does its conversion using division and two function calls. Presumably the functions were written by different teams, and the GetTickCount team optimized more carefully. While timeGetTime is twice as expensive as GetTickCount the cost is unlikely to matter – they both take just a fraction of a microsecond to execute.

The ReactOS is a helpful resource for understanding the implementation of GetTickCount, but their implementation of timeGetTime just calls GetTickCount, so it does not represent current reality. It does make me wonder if these functions used to be identical, but I don’t have a Windows XP machine handy to run that test.

What it really means

Both functions read from a counter that is updated by the timer interrupt and they both then convert it to ms.  When the timer interrupt runs more frequently (after a call to timeBeginPeriod) then timeGetTime gets increased resolution but for some reason – backwards compatibility perhaps? – GetTickCount simulates the worst-case timer interval.

Meanwhile the scheduler is affected by timeBeginPeriod and that, ironically, is part of why Larry got different results from GetTickCount and timeGetTime. The reduced timer resolution meant that Sleep(53) actually slept for 53 milliseconds, and it also gave timeGetTime increased resolution that it would not normally have. It’s ironic because it appears that Larry’s system was running with a reduced timer interval, but one of the comments he makes on his post describes timeBeginPeriod as a hideously bad idea in general.

Also curious is that Microsoft recommends replacing GetTickCount with GetTickCount64 in order to avoid wraparound bugs (/analyze warning 28159 triggers whenever you call GetTickCount), but timeGetTime, which is clearly the superior function for people who actually want to know what time it is, lacks a version that returns a 64-bit counter.

I guess I should stick with QueryPerformanceCounter.

All my testing was done on Windows 7. The rules may vary on different versions of Windows. You can reproduce my test results by building and running my code, which is available from ftp://ftp.cygnus-software.com/pub/timegettime.zip.

About brucedawson

I'm a programmer, working for Google, focusing on optimization and reliability. Nothing's more fun than making code run 10x faster. Unless it's eliminating large numbers of bugs. I also unicycle. And play (ice) hockey. And juggle.
This entry was posted in Performance, Programming and tagged , . Bookmark the permalink.

14 Responses to timeGetTime versus GetTickCount

  1. Riley L says:

    Don’t worry Bruce, as a game developer I also did not know the difference. Because as you alluded to at the end, we exclusively use QueryPerformanceCounter in our frameworks.

  2. munk says:

    I didn’t know these differences either. timeBeginPeriod() is a godsend if you want to write something resembling a realtime system, where your input-to-feedback loop is much smaller than typical rendering times.

  3. Pingback: Windows Timer Resolution: Megawatts Wasted | Random ASCII

  4. In case somebody wants to dig in deeper into this, keep your observations paired with the CPU you are running on. Specially core-1, core-2 or Nehalem(i3/i5/i7) and later. Afaik Nehalem moved the high res timer from core to uncore and making it invariant. Before it you needed to always query getfreq otherwise the results would be skewered. I can vaguely remember Pentium-D/core-1 being different in another way too, but that is too long ago🙂

  5. Alen Ladavac says:

    You might want to know that QueryPerformanceCounter() is not always perfectly reliable, even on the most modern machines. It can read from one of several possible time sources, depending on OS version, available hardware, BIOS settings and Windows boot options. Some of those sources have issues like skipping back and forth, slowly slipping, or just timer reading problems.
    Some people recommend reading from multiple points (GTC, QPC, RDTSC and real clock), and then try deducing which one bugged out on a particular frame. We’re still trying to work out a solution that doesn’t skip in at least some case.

  6. Dithermaster says:

    This reminds me of a funny story. Long ago I worked on a Windows 3.1 program that had an animated busy cursor. Whenever the code did something that took a while (this was in the days before multithreading coolness), you’d call the “updateBusyCursor” function which would use the system timer to determine if it should switch to its next animation state. One day a fellow programmer’s head popped above the cube wall and he said “I just made the program twice as fast by changing one line of code!” We all came over to see. It turns out our slowest function (an N^2 beast) called updateBusyCursor in its inner loop *lots* of times. And the timer function we were using (I don’t recall the name) did a transition down to DOS (INT 21H stuff) which took some time (hundreds of microseconds). We were calling it thousands of times per second. When he profiled the code we were spending more than half our time in that function. He replaced it with a newer Win32 API (perhaps GetTickCount?) which avoided the DOS thunk and our slowest function was literally twice as fast. Way to go!

  7. fenbf says:

    What should be used to measure execution time of a function? GetTickCount?
    Since the function may run faster than 1milisec I expect that GetTickCount will not work?

    • brucedawson says:

      You should probably use QueryPerformanceCounter for timing of fast functions. When doing performance measurements of small functions I generally call them in a loop and measure the cost of each call. Depending on your needs you may be interested in the fastest, slowest, or average time.

      On my laptop I can also use __rdtsc() for simple, precise, and low overhead timing, but don’t ship code that does that because it behaves differently on other processors.

      • fenbf says:

        Thanks! In legacy code I often see GetTickCount for such function time measurment, so probably it has to be changed into QueryPerfCounter or __rdtsc as you mentioned.

        • brucedawson says:

          timeGetTime() is fine for most production purposes because normally there is no reason to care about times of less than a millisecond. Even in the game industry, trying to hit a consistent 60 fps, the 1 ms precision of timeGetTime() (after using timeBeginPeriod) is sufficient for choosing when to start the next frame. More accurate timing is needed for benchmarking, but should rarely be needed in production.

          Given that your threads can easily be swapped out by the operating system for many ms it should rarely be necessary to do sub-ms timings in shipped software.

          But yeah, QueryPerformanceCounter for highest precision.

  8. MikeMarcin says:

    A few years late to the party, but there is a really nice description of some windows timing internals here:
    http://www.windowstimestamp.com/description

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s