Selecting Timer Functions

Home | Up | Search | X-Zone News | Services | Book Support | Links | Feedback | Smalltalk MT | The Scrapyard | FAQ | Technical Articles

 

Selecting Timer Functions

Written by Robert Dunlop
Microsoft DirectX MVP


Related Articles of Interest:

Implementing Steady Motion
Writing the Game Loop

The Need for Time

When designing a game title, the recurring concern is game performance - will the game play  fast enough?

An equally important question is whether the game will play smoothly.  No matter how optimized your engine may  be, it will never be able to provide a full sense of realism if object motion is not fluid, or if the frame rate is not consistent.

To achieve this, it is important that we are able to achieve an accurate measure of time.  There are a number of reasons that your software will need to use timekeeping functions, including :

bullet

To control the rate of motion of objects, such that their motion per frame is consistent with variations in the time between frames.

bullet

To throttle the frame rate according to the refresh rate of the monitor.

bullet

For management of system resource allocation, such as performing low priority tasks at a regular interval.

bullet

For timing game events.

Available Time Keeping Functions

There are a variety of functions available for dealing with timing issues, but they basically fall into two categories :

bulletFunctions that you can call to return a readout of the current time.
bulletTimer services, that can be instructed to send a message to your application or execute a callback function after a specified period.

For the purpose of this article we will be dealing with the former.  While timers do find use in game titles, their reliance on the message system means that your application will not be notified until all other tasks are completed.  There are ways to work with this, but that is the makings for another article....

Below is as explanation of three basic time keeping services.  Note that these functions do not provide an absolute time (i.e. time of day), but rather provide a relative time to system or Windows startup.

DWORD timeGetTime(VOID)

Include : MMSYSTEM.H
Library : WINMM.LIB

Returns current system time in milliseconds.  This function is accurate to 1 millisecond
DWORD GetTickCount(VOID)

Include : WINBASE.H
Library : KERNAL32.LIB

Returns current system time in milliseconds.  Note that although the resolution is in milliseconds, the actual precision may range from a few milliseconds up to 55 ms, depending on how the operating system sets the timer.
BOOL QueryPerformanceFrequency
   (LARGE_INTEGER *lpFrequency)

BOOL QueryPerformanceCounter
   (LARGE_INTEGER *lpCount)

Include : WINBASE.H
Library : KERNAL32.LIB

The Pentium processor and most PC compatible processors such as AMD support a high-resolution Performance Counter, which can provide a resolution of less than one microsecond.

The QueryPerformanceFrequency function is used to determine the actual frequency of the counter.  This is the numeric value that the Performance Counter will increase by in 1 second.

The QueryPerformanceCounter function is used to read the Performance Counter, which may be divided by the counter frequency to determine the time in seconds.

Both of these functions take a pointer to a LARGE_INTEGER union, which will be filled with the requested value if the function is successful.

The function will return 0 on failure, or non-zero on success.

The Proof is in The Numbers

Of course, we never really know something until we have proven it to ourselves ;-)

To this end, I once set out to test these facts for myself, as any good programmer will.  For this purpose, I created a short profiler that tests each of these three methods, by performing each test 10,000 times in a tight loop.  The total time to execute was measured using the Performance Counter.  Another test was performed  running each function 100 times and analyzing the results to determine resolution and accuracy.  

Note that this is based on a small sample, and is intended to give a rough idea of the operation of these functions.  The times listed do not take into account the amount of time required to run the loop, or to acquire the results - this is mean merely as a qualitative test.  With that said, here are the general results of my testing, along with a critique of each function :

Timer Function Resolution Accuracy Execution Speed
timeGetTime

This function provides a rock steady 1 millisecond time base, and is sufficient for timing applications that do not require a resolution of below a millisecond.

Note, however, that it also has the longest execution time of any of these functions, so it should not be used for polling in a tight loop.

1 ms 1 ms 8.0 us
GetTickCount

The accuracy of this function is very much dependent upon the operating system it is ran on, and if too coarse to be relied upon for frame timing.

On the other hand, the low overhead of calling this function makes it ideal polling, and is a good candidate for timing low priority background tasks.

1 ms 5-55 ms 0.1 us
Performance Counter

If accuracy and resolution are key, as is the case in frame timing, the Performance Counter is hands down the way to go.

This function is not available on all machines, so your application should test for its presence before using it.  If not available, include code that allows the program to drop back to using timeGetTime() as a backup plan.

<1 us ~1 us 4.2 us

Moving Ahead

So, what to do with this new found knowledge?  Now that we understand the benefits and downfalls of the various timer functions, the following articles will provide tutorials on implementing timing in a real-time game environment:

Writing the Game Loop

The game loop drives the flow of the program, providing a heartbeat for synchronizing object motion and rendering of frames in a steady, consistent manner.  Provides source code for a basic game loop using the Performance Counter, and provides automatic detection of the Performance Counter and fallback to timeGetTime if it is not available. 

Steady Motion

To allow for variations in playback, it is important that motion within the game be scaled to frame time.  This insures that motion is steady, and aids in synchronizing the motion of objects.

Synchronization of Game Threads

The use of threads can allow your title to make use of time that is often spent idle, providing better performance and tolerance for changes in system overhead.  Learn how and when to implement threads in your game, and how properly time and synchronize their execution.

 

Related Articles of Interest:

Implementing Steady Motion
Writing the Game Loop

This site, created by DirectX MVP Robert Dunlop and aided by the work of other volunteers, provides a free on-line resource for DirectX programmers.

Special thanks to WWW.MVPS.ORG, for providing a permanent home for this site.

Visitors Since 1/1/2000: Hit Counter
Last updated: 07/26/05.