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

Sleep() not reliable?

Started by Torath May 25, 2012 at 4:35 AM 10 replies 6k views
Original Post
Torath
Torath
Hi everyone,

I'm building a game engine from the ground up using C++ and DirectX. In my main game loop I am calling the function Sleep() each frame, but it doesn't consistently sleep for the time I specify. Instead of sleeping for 16ms for example, it will sleep for roughly 30ms.

I tried a different implementation using a waitable timer (using the functions SetWaitableTimer() and WaitForSingleObject()) and I see similar behavior.

The strange thing (and this might be a red herring) is I saw it work properly once while debugging the game in Visual Studio. However, when I run outside the debugger I get the behavior described above.

Any ideas?

Thanks for any help!
Ashaman73
Ashaman73
Sleep is not reliable for precise timing.
1. Sleep will only garantuee to sleep atleast the minimum of the given time, but there's no guarantee it will sleep exactly this time.
2. There's a timer resolution in windows, by default it is 15ms, this is the reason your sleep-calls sometimes needed more than the given time.

More infos about sleep (look out for timeBeginPeriod)
SimonForsman
SimonForsman
Sleep waits for atleast the time you specify, it can and will wait for longer most of the time and thus you should never use it to control your applications framerate, only use sleep to reduce battery usage on mobile devices or cpu usage for background services.

Windows normally have a scheduler timeslice of 10 or 16 ms, so a 10ms sleep is likely to wait for 20+ms , a 16 ms sleep for 32+ms, etc (The scheduler only steps in to swap cpu control around at 10 or 16ms intervals (this can be changed using the windows API but reducing the timeslice size also reduces overall throughput of the system), if you do a 16ms sleep at the start of a 16ms scheduler interval (very likely if your actual cpu execution time is low) the scheduler will kick in after another ~15.x ms, ignore your still sleeping thread and then get back to it after yet another 16ms which gives you 30+ms delays on your 16ms sleeps. (The only way for you to get control back in time is to have other threads on the system explicitly give up the rest of their timeslices).
[size="1"]I don't suffer from insanity, I'm enjoying every minute of it.
The voices in my head may not be real, but they have some good ideas!
frob
frob
As for ideas on how to help with it, each frame can use a blocking function call.

For example, both Direct3D and OpenGL have calls that wait until the graphics are done drawing.

D3D's Present() method and OpenGL's SwapBuffers() method by default will wait (a.k.a. block) until the buffer has flipped.

That delay is generally long enough for your app so you don't need to sleep; you can just render the next frame without worry.
wqking
wqking
I think you'd better not to ask sleep() to sleep the remaining time.
Instead, you can use a loop and sleep(0) to just transfer the cpu.

remainingTime = calcTime();
while(remainingTime > 0) {
Sleep(0);
remainingTime = calcTime();
}
Torath
Torath
Ah, interesting! Thanks for the help everyone, I'll change my implementation based on this info.
clb
clb
Calling Sleep(0); can still sleep any amount of time, e.g. 5ms - 30ms spikes are commonly observed.

The reality is that Windows is not a "hard" real-time multitasking operating system. There are very few of those around, perhaps notably the OS used by RIM in Blackberry is one.

To most precisely time your updates is to target against the vsync of the display, which you do simply by enabling vsync in your game. That gives you (typically) a smooth 60Hz update rate, and how it works is that the driver does some kernel-side magic to track the vblank signals. If you want to do e.g. 30Hz or 15Hz, you can use the presentation interval option to limit to updating every second vblank, or every third, and so on.

Another method to use if vsync is not viable, is to do first compute how much to sleep, then sleep e.g. one msec less than that, and do a busy spin-wait for the rest of the time. I have observed having multiple Sleep(0); calls instead of a single Sleep(target-someconstant); to be more prone to jitter, since each Sleep individually can vary for a long period of time. After that, do an empty "while(tick() < targetTickToPresent()) ;" loop to wait for the exact moment. But the bottom line is that any kind of Sleep pattern cannot guarantee a given hard target timing, and neither will a spinwait loop.
incertia
incertia
I recently made a post a few weeks ago questioning the reliability of Sleep(). You can view other people's responses here.
what
Antheus
Antheus
In semi-related news, WinRT no longer has Sleep(). It is generally a positive design decision, since by nature of non real-time OSes, Sleep isn't guaranteed to ever wake up.

So it's as good a time as any to learn the "proper" way.
Dragonsoulj
Dragonsoulj
If you need to sleep, particularly if it is just to keep some frame rate, use something like clb suggested:
while(tick() < targetTickToPresent()) ;
but try this (pseudo code):
bool gameIsRunning = true;
int timePerFrame = 60;
Timer myTimer;
myTimer.start();
while(gameIsRunning)
{
if(myTimer.elapsedTime() > timePerFrame)
{
// Game Update
myTimer.reset();
}
}
Aardvajk
Aardvajk
Throttling framerate will never be reliable on a PC. Vsync can be forced on or off in the user's graphics driver settings so if your game relies upon vsync for timing, it will break if the user chooses to do this.

Throttling also does not work in the situation where the target PC takes longer than the throttled interval to render a frame.

A far better approach is to fix your timestep but render as fast as possible, using interpolation to smooth out jitter. Gaffer's article, while the required reading on the subject, seems to overcomplicate the interpolation to me. I've always just stored the previous and current positions of an object and interpolated between them based on the blend, with perfect looking results.
Matias Goldberg
Matias Goldberg
Sleep( 0 ) is worse than Sleep( 1 )
The reason is that if you carefully look the documentation, Sleep( 0 ) will behave unfriendly with other Threads & Processes.

If you're so eager to use your CPU for other tasks, use SwitchToThread(). Sleep is not recommended at all.
If you really, really, reaaaally want to save CPU cycles and see your % CPU usage below 10%, there's no (performance-critical) reliable way (in Windows) to do it in user-mode other than VSync.
Unfortunately nanosleep is not available in Windows.

And please, read the fix your timestep article as it has already been suggested.

Cheers

Topic Locked

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

Sign in to reply to this topic.