4 ms·
The proposed fix is horrible: timer_freq.QuadPart /= 1000; // To convert frequency from 'ticks per second' to 'ticks per millisecond' Timer frequency is usu
by vardump 8y ago
The proposed fix is horrible:
timer_freq.QuadPart /= 1000; // To convert frequency from 'ticks per second' to 'ticks per millisecond'
Timer frequency is usually in the range of millions, but there's _nothing_ to prevent Microsoft from changing it to a value that's in the thousands. Or whatever they please, that's why win32 QueryPerformanceFrequency [1] API call exists in the first place! That would break any code with this "fix".
It also reduces accuracy — a second might no longer be 1000 ms, but maybe 1001 ms.
I think a better fix would be either to simply use doubles instead of floats or to at least offset the value to start of game — as long as a single game can't last months.
[1]: QueryPerformanceFrequency documentation: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644905%28v=vs.85%29.aspx https://msdn.microsoft.com/en-us/library/windows/desktop/ms6...
- tom_ 8y agoI don't think it's so bad as to count as horrible. QueryPerformanceCounter promises to count microseconds at worst: https://msdn.microsoft.com/en-us/library/ms644904(v=VS.85).aspx https://msdn.microsoft.com/en-us/library/ms644904(v=VS.85).a...
- vardump 8y agoWell, there's always the other side as well: if the value is in the billions, it'll degenerate back to the original bug. On single CPU socket systems, Windows seems to usually compute QueryPerformanceCounter by simply shifting CPU RDTSC count right by 10 bits, effectively dividing the value by 1024. So exactly 3.0 GHz system would have 2929687 in QueryPerformanceFrequency return value. On NUMA machines instead of RDTSC QueryPerformanceCounter uses HPET or APIC or whatever is available, because RDTSC is not HW synchronized between CPU sockets. I bet those HPET divisors will be in different divisor range. No matter what clock source Windows might use, simply using doubles would have fixed the issue. No point to use floats in the first place.
- jgh 8y agorationals might be a better solution since they could use whatever divisor they want without throwing away precision. I'm not very familiar with MS apis but if they provide some facility for telling you how many 'ticks' there are per second you could use that to generate the divisor as well. edit: I see you did link to the API!
- TeMPOraL 8y agoThere is. To save people the click, there's a pair of functions - QueryPerformanceCounter and QueryPerformanceFrequency. QPC gives you ticks, QPF gives you ticks per second, so QPC / QPF is the rational you seek.
- ericpauley 8y agoAgreed. Not sure how they concluded dividing the denominator was a good idea, seems like this exact problem (minus original float bug) shows up all the time. The solution is clearly to multiply by 1000 before performing floor division: `t=(now-start)*1000/freq`
- CookiePLMonster 8y agoValues are already offset (in noted snippet, timer_begin is approx. the time process has started, as in - queried during initialization). While I'll defend the idea of modifying QueryPerformanceFrequency result to achieve desired resolution (in this case, ticks per ms), you are absolutely right about the possibility of the value becoming several orders of magnitude smaller in the future. In this case multiplying the subtraction result by 1000 instead of dividing frequency is indeed far safer.
- vardump 8y agoWhy do you use floats in the first place? > While I'll defend the idea of modifying QueryPerformanceFrequency result to achieve desired resolution (in this case, ticks per ms) Did a quick back-of-the-envelope calculation, and the suggested solution is off by 42 milliseconds per minute on a hypothetical 3 GHz system. It might not sound like much (~2.5 frames at 60 fps), but I've seen smaller errors cause massive issues when it comes to time. It's better to do it right rather than to later on debug these often surprising issues we didn't have sufficient imagination for. Unless one is looking for an amazing bug hunting war story, of course. :)
- CookiePLMonster 8y agoI don't - it's not my code I had to fix. And as for returning the result - original code does return a float, so while I could also return a double it probably wouldn't make any difference, as I expect the game to store those results in floats too. Moreover, since the result is basically "session time", it is expected to last up to several hours - in which case floats are still fine.
- vardump 8y agoI've often seen code like that written by mathematicians and physicists. Sigh. Things like using a bit weird data types (floats instead of doubles) and writing functions thousands (!) of lines long. Then asking me why did the reconstruction have a weird circular error around the origo...