[Python-Dev] Use QueryPerformanceCounter() for time.monotonic() and/or time.highres()?
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:
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