[Python-Dev] Use QueryPerformanceCounter() for time.monotonic() and/or time.highres()?

Larry Hastings larry at hastings.org
Sun Apr 1 02:07:28 CEST 2012

On 03/31/2012 12:47 AM, Victor Stinner wrote:
>> Can you go into more detail about QPC()'s issues?
> Yes, see the PEP:
> http://www.python.org/dev/peps/pep-0418/#windows-queryperformancecounter

FYI, Victor, the PEP is slightly incomplete.  Not that this is your 
fault--you've done your homework.  But I've actually lived through it.  
I was a professional Win32 developer for 15 years, and I attempted to 
write a game on Windows back in the early-mid 2000s.

On Windows XP, QPC /usually/ uses the ACPI timer in my experience, but 
sometimes uses RDTSC.  Both of these had troubles.

With TSC, there's the clock skew between the two cores that they claim 
was fixed in SP2.  (You could also sidestep this problem by setting core 
affinity to the same core for all your threads that were going to 
examine the time.)  But there's another problem: the TSC frequency 
actually *does* change when SpeedStep kicks in.  I know someone who 
complained bitterly about running Half-Life 2 on their shiny new laptop, 
and when it'd overheat SpeedStep would knock down the processor speed 
and the game's logic update rate would drop in half and now Gordon was 
running through molasses.

With the ACPI timer, that's where you saw the 
leap-forwards-by-several-seconds-under-heavy-load problem (the cited 
MSKB article KB274323).  That only happened with a specific south bridge 
chipset, which was Pentium-III-only.  I never heard about anyone 
experiencing that problem--personally I had good experiences with that 
timer.  The downside of the ACPI timer is that it's slow to read; it 
took just over a microsecond in my experiments.  (timeGetTime was 20x 
faster.  I don't know how long GetTickCount takes.)

The documentation warnings about timeBeginPeriod is ancient, like 
Windows 95 era.  On any computer running Python 3.3, you can safely call 
timeBeginPeriod(1) with confidence that you'll get consistent 1ms 
resolution.  Likewise with calling into winmm--it shipped with every OS 
3.3 supports.  It's just not a big deal and you don't need to mention it 
in the PEP.

I had a hypothetical idea for a hybrid software clock for games that 
would poll all possible sources of time (RDTSC, QPC, GetTickCount, 
timeGetTime) and do its level best to create a high-quality synthetic 
time.  Like, if QPC jumped forward by a huge amount, and that jump 
wasn't corroborated by the other time functions, it'd throw that delta 
away completely.  It'd also notice if QPC's frequency had changed due to 
SpeedStep and recalibrate.  And it'd handle rollover of timeGetTime().  
Of course, part of the problem is that calling all these clocks is 
slow.  Another is that if QPC is implemented using RDTSC and RDTSC has 
problems you're kind of out of options--your best clock at that point 
only has 1ms accuracy.  Anyway I never wound up getting this to work--my 
attempts were all full of nasty heuristics and the code turned into 
hash.  Maybe someone smarter than me could figure out how to get it to work.

Sorry that this is incomplete / dashed off, but I'm still on vacation 
and it's been a few years since I did Windows timing stuff.  And I gotta 
go to bed--going to Madurodam in the morning!


More information about the Python-Dev mailing list