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 https://www.cygnus-software.com/ftp_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 as fast. Unless it's eliminating large numbers of bugs. I also unicycle. And play (ice) hockey. And sled hockey. And juggle. And worry about whether this blog should have been called randomutf-8. 2010s in review tells more: https://twitter.com/BruceDawson0xB/status/1212101533015298048
This entry was posted in Performance, Programming and tagged , . Bookmark the permalink.

18 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

  9. lorwei says:

    Years later, I have stumbled upon something weird with GetTickCount() and timeGetTime(): when I set the latter’s resolution to 1ms (timePeriodBegin(1)), and then measure the timepoint at which GetTickCount() increments using timeGetTime() …

    int last_tick = GetTickCount();
    while (!(GetTickCount()-last_tick))
    ;
    int diff_time = GetTickCount() – timeGetTime();

    … I find that the “lag” between the two timers (the lag being diff_time) is constant throughout the run of my program, but between the executions of my program the lag changes dramatically, varying between 1 and 16 ms.
    Why would the point during the 16 ms interval at which GetTickCount() increments be varying between different executions of the program?

    • brucedawson says:

      That is pretty cool. I did not know that. Does this happen even if the timer frequency has not been raised prior to your program being run?

      I guess the translation from timeGetTime()’s data source to GetTickCount() is done in user space using a value that is latched at process startup, but ???

      Bizarre. Thanks for sharing.

      • lorwei says:

        “Does this happen even if the timer frequency has not been raised prior to your program being run?”

        The problem is I have to raise the frequency at least once before being able to time the tick-counter using timeGetTime(). At first I thought the lag between the two might be determined at the time when I increase the resolution with timeBeginPeriod() (sort of fixing GetTickCount() to some point of the 16ms interval and then keeping them synchronized). But when I increase the resolution once at the beginning, and then repeatedly execute the program without changing the resolution in between, the lag nevertheless varies, so the call to timeBeginPeriod() does not seem to be responsible for setting the lag.

        Looking a bit futher into this, the lag between the two functions appears to be fairly uniformly distributed between 0 and 16ms (at least for the first 1000 runs of the test program), while never leaving this interval. How strange that the OS would somehow guarantee GetTickCount() to be synched with timeGetTime() to within one cycle of GetTickCount(), but then leave it to be undetermined within that interval between program executions.

        • There exist a lot more oddities here.

          It seems Microsoft removed the documentation for KeGetTickCount() which Larry Osterman mentiones here:

          https://blogs.msdn.microsoft.com/larryosterman/2009/09/02/whats-the-difference-between-gettickcount-and-timegettime/#comment-68891

          This seemed odd, so I did some more digging. First I found that the following functions also seem to exist:
          * NtGetTickCount()
          * ZwGetTickCount()
          * NtQueryTimerResolution()
          * ZwQueryTimerResolution()
          * NtTickCount()
          * RtlGetTickCount()

          also exist. NtTickCount seems especially interesting, Bill Blunden discusses those in his book on rootkits:

          https://books.google.com/books?id=ifQPC86G66sC&pg=PA114&lpg=PA114&dq=“RtlGetTickCount”#v=onepage&q=”RtlGetTickCount”&f=false

          Also of interest to the whole timer matter, the discussion of TickCountLow & TickCountLowDeprecated, in the KUSER_SHARED_DATA struct, by Geoff Chappell:

          https://www.geoffchappell.com/studies/windows/km/ntoskrnl/structs/kuser_shared_data.htm

          Which mentions interrupt 0x2A, and googling that leads us to learn that it exposes “KiGetTickCount()”, but only on x86, NOT on x86-64. Perhaps perhaps Larry Osterman made a typo and ACTUALLY meant KiGetTickCount()? At first this seemed likely, as googling for “KiQueryInterruptTime()” also yields many results.

          But perhaps he really meant KeTickCount, with no get? Looking for that also gives us results, and perhaps finally clears this up: It seems he meant KeQueryTickCount() (No get here!), which does exist, right alongside KeQuerySystemTime() & KeQueryInterruptTime().

          Looking all this up has fried my brain a bit by now, so the above almost certainly contains some errors, and I’ll leave it at this now, with one last note that it seems mostly only people interested in anti-debugging measures seem to have noticed:

          Besides RDTSC, there also exists RDPMC. One might suspect that QPC queries this, but alas, the matter seems more complicated:

          https://www.felixcloutier.com/x86/RDPMC.html

          and RDTSCP, which, unlike what Wikipedia claims, isn’t actually a serializing version of RDTSC, it just seems awfully like one:
          https://www.felixcloutier.com/x86/RDTSCP.html

          And even after all this, I still haven’t figured out what tscsyncpolicy, useplatformclock, useplatformtick & disabledynamictick REALLY do.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.