Coal, Glitchy Update Loops, and Waiting

Image licensed under CC Share Alike, found here

I've been fighting a glitchy update loop on Windows that I don't see on the equivalent update loop running on Linux. After a fair bit of digging, I narrowed it down to a call to Sleep in the Windows main loop. Eliminating the Sleep made the frame update buttery smooth like it is on Linux, but it shot my CPU usage to 100%, and the computer's fans roar like a vacuum cleaner.

Some research into this issue got me a good answer: a smooth framerate, a cool CPU, and quiet fans. Before the solution is revealed though, I wanted to bring some game programmer dark wisdom into the light where it may shrivel to a crisp and blow away.

Game Programmer Dark Side Wisdom

The most common advice people seem to give on the web is that it's fine to run the CPU at 100%, let the fans blow, what other use is your computer any way, especially when you have a game going? A cowed minority mentions that they're on a laptop, and grudgingly it's admitted that maybe an option for laptop users to run at less than 100% would be an admissible but sad concession.

CPU at 100% is not fine

Forget that! Anyone who thinks running at max power is a good idea should re-watch The Day After Tomorrow, and the compare and contrast the weather report on your evening news. News flash, it's very likely that the energy you consume to run your game at 100% came from burning something.

Game Programmer Light Side Wisdom

Luckily, the right answer does exist, and thanks to Christian Weis for working it out. The code debugged is posted here for posterity. This version is way stripped down from the original post, but accomplishes what I want; CPU loading is very light, the fans aren't going, and I've got no stuttering. I've calculated the wait delay I want in my main loop based on the time remaining to achieve my desired framerate (in my case, 60Hz).

// due to: // allows sleep at fine granularity with no stuttering, and not pegging CPU to 100% void MySleep( float Seconds ) { static HANDLE Timer = CreateWaitableTimer( NULL, FALSE, NULL ); // Determine time to wait.  LARGE_INTEGER WaitTime; WaitTime.QuadPart = (LONGLONG)(Seconds * -10000000); if ( WaitTime.QuadPart >= 0 ) return; // Give up the rest of the frame.  if ( !SetWaitableTimer( Timer, &WaitTime, 0, NULL, NULL, FALSE ) ) return; DWORD Result = MsgWaitForMultipleObjects ( 1, &Timer, FALSE, INFINITE, QS_ALLINPUT ); } 


Please consider adding this or something like it to your main loop. Minimally your fans are going to run less, and your workspace will be a little quieter.

I ask you all to consider carefully the consequences of the choices you make every day, even while you code.

  • That shader is really gorgeous, and I'm personally first in line for the latest and greatest, but could you do it in a lighter way?
  • Do you really need a full rebuild?
  • Would a better search algorithm burn less coal?
  • It's cool that some ancient clunker still runs and can serve files, but if you turned it off could you buy a newer-faster-bigger server with the power you saved in one year?
  • Did you turn off your monitor before you went home?

Seriously, we're all in this together.


Content by Nick Porcino (c) 1990-2011