It's time for another "APIs you never heard of" article :)
This time, I'd like to talk about the time* APIs.
The time* APIs are a set of 7 APIs built into the windows multimedia extensions (winmm.dll). They provide a rudimentary set of timer functions for Windows applications. At this point, except for two of the APIs, they exist only for historical purposes, the core OS now provides significantly higher quality APIs for timers.
The time APIs fall into three rough categories:
- Timer information (timeGetDevCaps, timeGetTime and timeGetSystemTime)
- Timer callback functions (timeSetEvent and timeKillEvent)
- Timer frequency functions (timeBeginPeriod and timeEndPeriod)
The first two categories are obsolete (arguably timeGetDevCaps still has valid uses). The timeGetTime API is effectively identical to the GetTickCount() API, and timeGetSystemTime simply returns the exact same value that timeGetTime would have returned, packed into a MMTIME structure.
The timeSetEvent and timeKillEvent have been replaced with the Win32 Timer Queue functions, I'm not sure if I know of any reason to ever call the MME versions of these functions :). In fact, timeSetEvent will call PulseEvent API, which is fundamentally flawed. There is one difference between timeSetEvent and the Win32 timer queue functions - timeSetEvent will call timeBeginPeriod to set the timer resolution to the resolution specified in the call to timeSetEvent. Even with this, you're better off calling timeBeginPeriod and using the Win32 Timer Queue functions (because the Win32 timer queue functions are far more flexible).
But then there's the timeBeginPeriod and timeEndPeriod APIs. These are actually fun APIs, especially in the multimedia or gaming space, because they allow you to change the resolution of the internal scheduler, which can lower (or raise) the resolution with which the internal clock runs.
This has a number of side effects - it increases the responsiveness of the system to periodic events (when event timeouts occur at a higher resolution, they expire closer to their intended time). But that increased responsiveness comes at a cost - since the system scheduler is running more often, the system spends more time scheduling tasks, context switching, etc. This can ultimately reduce overall system performance, since every clock cycle the system is processing "system stuff" is a clock cycle that isn't being spent running your application. For some multimedia applications (video, for example) the increased system responsiveness is worth the system overhead (for instance, if you're interested in very low latency audio or video, you need the system timers to run at a high frequency).
Edit: Added comment about timeSetEvent calling timeBeginPeriod to set the resolution.\
Edit2 (years later): Updated the link to GetTickCount...
- Anonymous
September 08, 2005
Thanks for the entry Larry. It's always refreshing to see little-known APIs getting mentioned in the present day.
I just took a look at the timeBeginPeriod documentation and got a chuckle out of the return values: "Returns TIMERR_NOERROR if successful or TIMERR_NOCANDO if the resolution specified in uPeriod is out of range."
No can do!
- Anonymous
September 08, 2005
The comment has been removed
- Anonymous
September 08, 2005
timeSetEvent has the same resolution as all the other waitable objects in the system.
On all NT versions, the multimedia timers use the NT internal timer infrastructure, which is the exact same infrastructure that's used by the timer queue timers. For Win 3.1, it may have used a higher resolution timer.
However, on closer inspection, it appears that the timeSetEvent API will call timeBeginPeriod to set the system clock frequency to the resolution of the timer. I'll update the article.
For your compiler error, it appears that your callback function isn't declared with the correct function parameters - it needs to be a stdcall function that takes a LPVOID and a BYTE parameter, not a <whatever> function that takes a LPVOID and an int parameter. I'll talk to the owner of the API.
- Anonymous
September 08, 2005
Interesting comparison of timing function at http://developer.nvidia.com/object/timer_function_performance.html .
Strangely however, timeGetTime() and GetTickCount() perform completely different (and not by a small margin).. which is odd if they are really equivalent like you say.
Anyway in every place I worked, timeGetTime() was quite a popular function :)
- Anonymous
September 08, 2005
Fascinating purplet. It turns out that GetTickCount returns the tick count multiplied by the clock frequency, while TimeGetTime returns the "interrupt time", which is a somewhat different calculation (and is apparently not updated as frequently).
- Anonymous
September 08, 2005
With CPU's that can change their clock frequency (often seen in laptops) I'm never quite certain about the accuracy of timer functions.
- Anonymous
September 08, 2005
Chris, a good and relevent question. The simple answer is that NT guarantees that the timers are stable, which means that it doesn't matter what the clock frequency does, the timer frequency won't change.
- Anonymous
September 08, 2005
The comment has been removed
- Anonymous
September 08, 2005
The comment has been removed
- Anonymous
September 08, 2005
I once needed a time measuring facility to assess performance of my code. The multimedia timer API turned out to be surprisingly lightweight in comparison with QueryPerformanceCounter, and more precise than GetTickCount.
- Anonymous
September 08, 2005
The comment has been removed
- Anonymous
September 09, 2005
"NT guarantees that the timers are stable"
Except when it doesn't:
http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323
I do not like bad hardware chips, I do not want their timer slips. I do not want them on my bus, I do not want them erroneous.
- Anonymous
September 09, 2005
Dave, good point.
Centaur, QPC is heavyweight, and is documented as such. timeGetTime is faster but much less accurate.
I actually checked the code for timeGetTime and GetTickCount(). GetTickCount() takes the number of clock interrupts and multiplies it by the clock frequency. timeGetTime() reads a field called the "interrupt time", which is updated periodically by the kernel (I wasn't able to find out how frequently).
- Anonymous
September 09, 2005
> I'm not sure if I know of any reason to ever call the MME versions of these functions :).
How about if you want your code to still be able to run on Win9x?
I would LOVE to be able to stop having to consider Win9x, but about 15% of my customer base still runs some form of it (about 1% still runs Win95!)
- Anonymous
September 09, 2005
The comment has been removed
- Anonymous
September 09, 2005
The docs for timeGetTime tell you to monkey with timeBeginPeriod & timeGetDevCaps to improve timeGetTime's resolution.
- Anonymous
September 09, 2005
Ulric, timeGetTime returns millisecond resolution time that's at a lower resolution than GetTickCount(). Under what circumstances would you want to use it?
The think is that timeBeginPeriod will improve the resolution of GetTickCount() as well.
- Anonymous
September 09, 2005
I see your point, I'm thinking about the fact timeGetTime is more accurate on Win9x by default. On Win2k and up, I would hesitate recommend using GetTickCount over timeGetTime if timeBeginPeriod happens to affect GetTickCount as well, since the two API are unrelated and that behaviour is not documented.
- Anonymous
September 09, 2005
The TimeProc documentation is out of date too: it says "Applications should not call any system-defined functions from inside a callback function, except for PostMessage, timeGetSystemTime, timeGetTime, timeSetEvent, timeKillEvent, midiOutShortMsg, midiOutLongMsg, and OutputDebugString." This seems to be a hangover from the Win16 days when TimeProc was called from the interrupt service routine.
- Anonymous
September 09, 2005
The comment has been removed
- Anonymous
September 10, 2005
The comment has been removed
- Anonymous
September 13, 2005
The comment has been removed
- Anonymous
September 27, 2005
Simple way to change the resolution of the timer on your system without having to change your code... http://users.tpg.com.au/lucash/
- Anonymous
June 08, 2009
PingBack from http://insomniacuresite.info/story.php?id=2014