Skip to main content
GameDev.net gamedev.net
🔒 Locked

High resolution timer

Started by all_names_taken Nov 11, 2007 at 5:19 AM 16 replies 11.9k views
Original Post
all_names_taken
all_names_taken
I need a high resolution timer class that works for both Linux and Windows (by using #ifdefs etc, since I know that the platform independent C++ standard library time functions are too inaccurate). A. is there any good public domain implementation of this already, that you would recommend? or B. if I need to write it myself: 1. in Windows, which of the many possible implementations should I use? QueryPerformanceCounter() Windows timers (events, I think) GetTickCount() (someone said this one was inaccurate) timeGetTime() etc. etc. If possible, I would like one with at least millisecond resolution, and available on as much hardware as possible released since around year 2000. 2. in Linux, which of the many possible implementations should I use? gettimeofday() time() gethrtime() gethrvtime() As with the windows timer, I'm looking for as good hardware compatibility as possible, and millisecond resolution. It should also not be available only on a small number of Linux distributions, but is part of the standard so I can expect it to work on all of the major distributions.
hydroo
hydroo
clock_gettime is posix I think.

The down-side is that it's pretty much up to the kernel what to do.

clock_gettime on linux 2.6.21+ should(meaning I never tested it) use this mm-timer or what it's called (10mhz+ resolution). Maybe someone can share his windows-experience to this.
lexs
lexs
This is what i am using: http://rafb.net/p/g3v1v524.html
However i'm using SDL, but i guess you could just check how SDL implements SDL_GetTicks() to see how you should do it on linux.
swiftcoder
swiftcoder
Quote:
Original post by lexs
This is what i am using: http://rafb.net/p/g3v1v524.html
However i'm using SDL, but i guess you could just check how SDL implements SDL_GetTicks() to see how you should do it on linux.


That really offers no advantages over using SDL's timer directly - especially since the Windows implementation there is broken (doesn't check for jumps in the performance timer), while SDL's works fine.

As to your original question:
All *nix variants (Linux, Mac OS X, etc.): gettimeofday() is the best way to do this. It uses the highest performance timer available, and exists on every *nix platform.
Windows: Here you need to use QueryPerformanceCounter(), but because it often skips/jumps, you need to compare it with the lower resolution GetTickCount(), and correct the performance counter when it jumps.
Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]
lexs
lexs
Quote:
Original post by swiftcoder
Quote:
Original post by lexs
This is what i am using: http://rafb.net/p/g3v1v524.html
However i'm using SDL, but i guess you could just check how SDL implements SDL_GetTicks() to see how you should do it on linux.


That really offers no advantages over using SDL's timer directly - especially since the Windows implementation there is broken (doesn't check for jumps in the performance timer), while SDL's works fine.


I had problems with my game running out of sync badly and i guessed it was a timer issue, so i hacked together this class (its not perfect by any means) and it worked again. It didnt run out of sync on every computer but some and with this class it worked on them all. :)

EDIT: I just checked the SDL source and it uses QueryPerformanceCounter() but only when USE_GETTICKCOUNT is defined, maybe it isnt defined in the regular win32 build?
Oh, seems like the whole QPC part has been commented out and hires_timer_available set to false. That explains it.

Maybe someone could be nice and write a proper crossplatform timer wrapper class? :)
all_names_taken
all_names_taken
Quote:
Original post by swiftcoder
Windows: Here you need to use QueryPerformanceCounter(), but because it often skips/jumps, you need to compare it with the lower resolution GetTickCount(), and correct the performance counter when it jumps.

Hm, I'm not sure I understand what is skipped. Do you mean that QPC occasionally skips doing a new hardware check and just reads off the same old value from some memory location, so that the result equals the last result read? So, should the comparison be as follows (pseudo code)?
NewQPCResult = QueryPerformanceCounter();while (NewQPCResult - OldQPCResult == 0 && newGetTickCountResult - oldGetTickCountResult > 0) {  NewQPCResult = QueryPerformanceCounter();}

Or how should the comparison be made?

Quote:
Original post by lexs
Maybe someone could be nice and write a proper crossplatform timer wrapper class? :)

I can post mine here when it's ready and tested for windows :), but I won't be able to test it on Linux until in a few days.

Edit: here's a temporary timer, but it doesn't use QPC, but instead uses timeGetTime. It's probably accurate, but the resolution isn't too high.

#ifdef WIN32	#define WIN32_LEAN_AND_MEAN	#include <windows.h>	#include <mmsystem.h>#endif#include <SDL/SDL.h>inline unsigned long myGetTicks() {#ifdef WIN32	return timeGetTime();#else	return SDL_GetTicks();#endif}


[Edited by - all_names_taken on November 11, 2007 11:08:18 AM]
swiftcoder
swiftcoder
Quote:
Original post by all_names_taken
Quote:
Original post by swiftcoder
Windows: Here you need to use QueryPerformanceCounter(), but because it often skips/jumps, you need to compare it with the lower resolution GetTickCount(), and correct the performance counter when it jumps.

Hm, I'm not sure I understand what is skipped. Do you mean that QPC occasionally skips doing a new hardware check and just reads off the same old value from some memory location, so that the result equals the last result read? So, should the comparison be as follows (pseudo code)?
NewQPCResult = QueryPerformanceCounter();while (NewQPCResult - OldQPCResult == 0 && newGetTickCountResult - oldGetTickCountResult > 0) {  NewQPCResult = QueryPerformanceCounter();}

Or how should the comparison be made?


Take a look here: Microsoft KB: 274323
Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]
swiftcoder
swiftcoder
Quote:
Original post by Jan Wassenberg
Here's a writeup on the topic: Timing Pitfalls and Solutions

HTH+HAND


Nice, I hadn't seen a thorough analysis of this before.

It also might be worth talking to the guys at OGRE - they are using GetTickCount() to stabilise QueryPerformanceCounter(), it seems with much success.
Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]
scorpion007
scorpion007
QueryPerformanceCounter is probably the most accurate timer on windows.

If you have a Pentium 3 or newer, you can use the RDTSC instruction.
swiftcoder
swiftcoder
Quote:
Original post by scorpion007
QueryPerformanceCounter is probably the most accurate timer on windows.
If you have a Pentium 3 or newer, you can use the RDTSC instruction.

Neither of which are stable... Most accurate is not enough unless you can enforce some kind of stability on the output.
Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]
nife87
nife87
On Windows I use QueryPerformanceCounter (with a fallback for timeGetTime, if QPC for some reason is not available) and double-checking the results of QPC for the famous QPC "leap" with GetTickCount (faster than timeGetTime and quite sufficient, since its results are only used for checking for leaps larger than 500 ms).
Jan Wassenberg
Jan Wassenberg
Quote:
It also might be worth talking to the guys at OGRE - they are using GetTickCount() to stabilise QueryPerformanceCounter(), it seems with much success.

Ah, interesting. I had a quick look at the code; unfortunately, there are several issues:
- it sets thread affinity during each call, which usually entails a thread switch and can easily incur delays of >= 1 quantum (e.g. 10 ms);
- no attempt is made to smooth the spike that comes up when the PMT-race-condition gremlins cause QueryPerformanceCounter to jump. Observed time can leap forward by, or remain constant over an interval of 10 or 55 ms, depending on platform.
- long-term drift of the GetTickCount time source is not accounted for (makes the above issue even worse).

Amusingly, what they do check for is a change in QueryPerformanceFrequency; this is unnecessary because the nominal QPC frequency is guaranteed not to change, and even counterproductive because it interferes with frequency slewing.

I guess we can say: the code apparently works well enough for games/graphics purposes, but don't use it for accurate and/or precise high-resolution timing.


Quote:
QueryPerformanceCounter is probably the most accurate timer on windows.

Please read http://www.ijs.si/time/#definitions to understand what "accurate" means. Whatever QPC is, it's certainly not accurate.

Quote:
If you have a Pentium 3 or newer, you can use the RDTSC instruction.

RDTSC was introduced with the original Pentium.
E8 17 00 42 CE DC D2 DC E4 EA C4 40 CA DA C2 D8 CC 40 CA D0 E8 40E0 CA CA 96 5B B0 16 50 D7 D4 02 B2 02 86 E2 CD 21 58 48 79 F2 C3
CrazyCdn
CrazyCdn
I use QPC on non-AMD machines (check first) since they don't share a frequency between cores as Intel does so you can get issues. I also have a timeGetTime() fall back because I want 1ms resolution (or better if its not too expensive). But I use timeBeginPeriod() to set it.

There has been a lot written on timers, a few of the articles are covered here, if I have time I will post the ones I have but they're on my other PC.
"Those who would give up essential liberty to purchase a little temporary safety deserve neither liberty nor safety." --Benjamin Franklin
scorpion007
scorpion007
Quote:
Original post by Jan Wassenberg
Please read http://www.ijs.si/time/#definitions to understand what "accurate" means. Whatever QPC is, it's certainly not accurate.

Yeah, I probably should have used the expression "highest resolution" instead of "most accurate".

Quote:
RDTSC was introduced with the original Pentium.

Oops, yeah, my mistake.
Sc4Freak
Sc4Freak
Quote:
Original post by swiftcoder
Quote:
Original post by Jan Wassenberg
Here's a writeup on the topic: Timing Pitfalls and Solutions

HTH+HAND


Nice, I hadn't seen a thorough analysis of this before.

It also might be worth talking to the guys at OGRE - they are using GetTickCount() to stabilise QueryPerformanceCounter(), it seems with much success.

As I understand it, UT2004 also used this method for timing.
Jan Wassenberg
Jan Wassenberg
Quote:
As I understand it, UT2004 also used this method for timing.

Looks like it's biting them in the ass :))
E8 17 00 42 CE DC D2 DC E4 EA C4 40 CA DA C2 D8 CC 40 CA D0 E8 40E0 CA CA 96 5B B0 16 50 D7 D4 02 B2 02 86 E2 CD 21 58 48 79 F2 C3

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.