20
\$\begingroup\$

I was wondering if there is any possible harm when my game loop runs as fast as the system allows?

I currently have a loop, that, by measuring passed time in nanoseconds, runs the game logic and rendering logic at predefined speeds without a problem. In fact any logic I do in the loop is clocked to a certain amount of calls each second.

The loop itself though just runs about as fast as it likes to which comes up to around 11.7 million loops a second on my machine.

Loop (simple pseudocode):

while(!isGameOver){

 if(canPollInputs){
    pollInputs()
 }

 while(canStepLogic){
    stepLogic()
 }

 if(canRender){
    render()
 }
}

My question is basically if that simple loop, if its not running at a controlled speed, can do any harm to a system?

Edit: That means my logic is running 30 times a second (30 tps), my renderer is running at 60 fps, I'm polling inputs a 100 times a second and there's also some logic to cope with logic or rendering taking longer than expected. But the loop itself is not throttled.

Edit: Using Thread.sleep() to e.g. Throttle the main loop down to 250 loops per second leads to a reduction but the loops runs at around 570 loops per second instead of the desired 250 (will add code when I'm at my desktop machine..)

Edit: Here we go, a working java gameloop in order to clarify things. Also feel free to use it but don't claim it yours ;)

private void gameLoop() {

    // Time that must elapse before a new run
    double timePerPoll = 1000000000l / targetPPS;
    double timePerTick = 1000000000l / targetTPS;
    double timePerFrame = 1000000000l / targetFPS;
    int maxFrameSkip = (int) ( (1000000000l / MINIMUM_FPS) / timePerTick);

    int achievedPPS = 0;
    int achievedFPS = 0;
    int achievedTPS = 0;

    long timer = TimeUtils.getMillis();

    int loops = 0;

    int achievedLoops = 0;

    long currTime = 0l;
    long loopTime = 0l;

    long accumulatorPPS = 0l;
    long accumulatorTPS = 0l;
    long accumulatorFPS = 0l;

    long lastTime = TimeUtils.getNano();

    while(!isRequestedToStop) {
        currTime = TimeUtils.getNano();
        loopTime = currTime - lastTime;
        lastTime = currTime;

        loops = 0;

        accumulatorPPS += loopTime;
        accumulatorTPS += loopTime;
        accumulatorFPS += loopTime;

        if(accumulatorPPS >= timePerPoll) {
            pollInputs();
            playerLogic();
            achievedPPS++;
            accumulatorPPS -= timePerPoll;
        }

        while(accumulatorTPS >= timePerTick && loops < maxFrameSkip) {
            tick();
            achievedTPS++;
            accumulatorTPS -= timePerTick;
            loops++;
        }

        // Max 1 render per loop so player movement stays fluent
        if(accumulatorFPS >= timePerFrame) {
            render();
            achievedFPS++;
            accumulatorFPS -= timePerFrame;
        }

        if(TimeUtils.getDeltaMillis(timer) > 1000) {
            timer += 1000;
            logger.debug(achievedTPS + " TPS, " + achievedFPS + " FPS, "
                    + achievedPPS + " Polls, " + achievedLoops + " Loops");
            achievedTPS = 0;
            achievedFPS = 0;
            achievedLoops = 0;
        }

        achievedLoops++;
    }
}

As you can see there is almost no code run on each loop but always a certain selection based on how much real time has passed. The question is referring to that 'worker loop' and how it does influence the system.

\$\endgroup\$
8
  • 2
    \$\begingroup\$ obligatory 'fix your timestep' link: gafferongames.com/game-physics/fix-your-timestep please read it. Also note that Thread.Sleep() cannot be used to throttle your timestep reliably as the OS does not guarantee to return control to your application after the requested amount of time. (It may vary). \$\endgroup\$
    – Roy T.
    Aug 12, 2014 at 11:59
  • \$\begingroup\$ Yeah, that's the problem with pseudocode. I did most of what gaffer writes about (currently working on a delta implementation that complements my loop structure). If I cannot use Thread.sleep() then what else is available within standard Java? \$\endgroup\$
    – dot_Sp0T
    Aug 12, 2014 at 12:10
  • 1
    \$\begingroup\$ You usually use a busy loop with Thread.Sleep(0) and hope for the best. Gaffer talks about this a lot in a few articles. \$\endgroup\$
    – Roy T.
    Aug 12, 2014 at 12:22
  • \$\begingroup\$ Well that is basically what I've been doing, just without the thread.sleep(0), AND what originally provoked this question. Is there any harm in a busy (resp not busy as even that little repetitive logic is throttled down) loop for the system? \$\endgroup\$
    – dot_Sp0T
    Aug 12, 2014 at 13:10
  • \$\begingroup\$ A while back, a bug caused Starcraft II to do this under certain conditions. It ended up literally melting a few GPUs. So yeah, it's very possible for an uncontrolled game loop to cause harm. \$\endgroup\$ Aug 12, 2014 at 20:49

4 Answers 4

34
\$\begingroup\$

It will cause one CPU core to always run on 100%. This usually doesn't cause any harm to the system. CPUs are designed to run on 100% for hours. But on a mobile device it will drain the battery quickly and heat up the device, which will likely cost you about a stars in your store ratings. On a desktop computer this is less of a problem, but it will consume more of the users electricity, causes the CPU fan to spin faster which might cause some noise and waste CPU cycles which could otherwise be used by other processes. While these are not critical defects, they are still bad style, so you should avoid them when possible.

You didn't say anything about how your game logic loop works internally, but when you are using the delta-time approach (each calculation you are doing takes the time since the last call into account), you are dealing with very small delta-time values. This means you can run into problems with floating-point inaccuracy which can cause all kinds of strange behaviors. Also, the resolution of the system timer is often limited, so when your logic loop is too fast, you can get a delta-t value of zero, which then might cause a division by zero resulting in a crash.

To mitigate this problem, you should limit your framerate (graphic and logic) to the maximum of what the human eye can perceive. How much that is is disputed and depends on the kind of animation and on what kind of display it is shown, but estimations range from 40 FPS to 120 FPS. That means you need to set a minimum time of execution of each loop between 20ms and 8ms. Should a loop iteration finish faster, let the cpu thread sleep for the remaining timespan.

\$\endgroup\$
7
  • \$\begingroup\$ Thanks for the explanation Philipp. I actually did refer to the fact that I'm running at a certain fps and tps as well as do the polling only a certain amount of times a second. Will try to make it clearer though. \$\endgroup\$
    – dot_Sp0T
    Aug 12, 2014 at 10:42
  • \$\begingroup\$ @dot_Sp0T After your edit to your question, only the first paragraph and the last sentence are relevant to your case. However, I would like to retain the second and third paragraph of my answer because they might be helpful for others. \$\endgroup\$
    – Philipp
    Aug 12, 2014 at 13:41
  • \$\begingroup\$ I will presumably as well accept your answer as the first paragraph perfectly well gives an answer. Would you mind to somehow visually set it apart from the rest of the answer? \$\endgroup\$
    – dot_Sp0T
    Aug 12, 2014 at 18:14
  • \$\begingroup\$ Play an unpatched Civilization 2 on a desktop and you'll find it to be a problem. At least, I do. shrug \$\endgroup\$
    – corsiKa
    Aug 12, 2014 at 20:05
  • 1
    \$\begingroup\$ In addition to battery and fan considerations, increased CPU usage can lead to excess heat which can be uncomfortable (e.g. laptop, esp my rMBP) and even lead to the PC shutting down (esp older PC's with dusty coolers). These factors are exasperated if you run all cores a 100%. \$\endgroup\$
    – NPSF3000
    Aug 13, 2014 at 12:36
2
\$\begingroup\$

You're wasting CPU cycles. That means lower battery time on notebooks, tablets and phones, higher electricity bills, more heat generated by the machine, noisier fans. Also, you may be eating cycles from other important system processes (e.g. the window server could become jerky), which could then affect gameplay. Some system schedulers on today's pre-emptive multitasking systems also penalize applications that use too many cycles, so you may be fast first, then suddenly see strange jerkiness when you're throttled by the system.

Many hardcore gamers also build custom PCs from scratch that are at the edge of their fan's specs, in which case a hot day and the heat your game generates may cause the safeties to trigger in the machine (at best) or even make parts overheat and die (at worst). So if that's your target audience, you may want to ensure you're always leaving a bit of space "at the top".

Finally, there are gameplay issues where you might be advantaging players with faster machines over those with slower ones. Having the game loop capped at a certain frequency and using additional cycles solely for non-gameplay-relevant aspects (like rendering higher fidelity graphics, surround sound effects or whatever) will make your game more fair.

\$\endgroup\$
3
  • \$\begingroup\$ I would say conversely people who passionately build their machines use good fans, even watercooling. Only HTPC or mini ITX cases may have limitations on that part. Otherwise, poor but enthusiats people will use the box ventirad. which is enough even at 100%, hot but ok. \$\endgroup\$
    – v.oddou
    Nov 19, 2014 at 2:11
  • \$\begingroup\$ I'm just repeating from experience. If you look at Star Trek Online, they actually have a separate setting in their GUI that will insert sleep()s in their main loop to keep certain machines from overheating, so it can't be an uncommon thing if the maker of Neverwinter and STO saw the need to add it. Keep in mind that passion != skill. There are many skilled and passionate tinkerers, but there are also beginners and others that are still learning, and with a decent drop-out rate, statistics says they're the majority. \$\endgroup\$
    – uliwitness
    Nov 19, 2014 at 7:45
  • \$\begingroup\$ Thanks to these comments I came to reread your answer. You list valid points but sadly don't really address the question with the faster machines vs slower machines controversy. The logic loop (called gameloop in your third paragraph) is capped. The question was about capping the backbone loop which simply reevaluates every run through if input oughta be pulled, logic be calculated or a frame should be rendered - so no faster logic unless your machine is terribly lame \$\endgroup\$
    – dot_Sp0T
    Nov 19, 2014 at 13:26
1
\$\begingroup\$

You shouldn't have jittery movement / gameplay

There are two ways that you can implement game logic - tied to a real time, or tied to the number of "turns"/processing steps. If some thingy in your game is moving leftwards, will it's motion be different if your stepLogic() was called 100 instead of 50 times?

If your code treats the elapsed time explicitly everywhere and does it correctly, then it might be okay; but if there's anything in your code that depends on the number of 'steps' then you'll get unwanted side-effects.

First, the speed of things that should be moving constantly will vary unpredictably (depending on processor usage), and this makes for very annoying controls - you can't make a precise punch or a jump if suddenly the game speeds up or slows down. Even for games with no 'twitch' at all, it looks annoying and jittery if things aren't moving smoothly.

Second, you might get problems with character abilities depending on the computer speed - a classic example is the old Quake issue where the maximum jumping range was unintentionally dependent on your fps.

These issues may appear even if you try to have the code to be fps-independent, accumulation of rounding errors can often cause such bugs.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ As much as I like your answer its not relevant to the question, as I'm not asking about jittery movement/gameplay but about the harm an uncontrolled loop (that does not control any logic or rendering or anything else) can do to the sys \$\endgroup\$
    – dot_Sp0T
    Aug 12, 2014 at 13:28
1
\$\begingroup\$

The only potential harm is to your power consumption, so don't do this on mobile devices. Conversely, quite a few embedded systems spend their entire lives in a loop waiting for things to happen.

It used to be entirely normal to simply render game frames as fast as possible, sometimes without even a timer to compensate gameplay for different CPU speeds. Bullfrog's racing game Hi-Octane was a particularly bad victim of this, and I suspect this is what the poster mentioning Civ2 is referring to.

I'd advise you to at least poll inputs on every pass if you're on a Windows system or similar, to ensure snappy input response.

\$\endgroup\$
1
  • \$\begingroup\$ Its not a question of timer, its a question of a chronometer I would rather say, or a performance counter. One needs to get the real time at some point, and a free loop works perfectly. However a timer (in the sense of regular interruption) is attempting to regulate the loop speed in general usage. I do not consider this good. It is preferable to use the swap/present/flip function do the wait on vsync signal, to regulate the game loop. \$\endgroup\$
    – v.oddou
    Nov 19, 2014 at 2:15

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .