2
\$\begingroup\$

I have learned how to create and manage threads in C++. Now I'm trying to create game loop based on multithreading but I can't imagine how should I split whole game to tasks. I mean, should rendering be separated from updating physics?

\$\endgroup\$
2
  • 3
    \$\begingroup\$ There are lots of ways to implement threading into game loops, and no one way is universally better. In general, you don't want to use threads just because they're there: you want to have a clear idea of the specific problem you're using threads to solve (and even then, don't neglect to consider non-threading solutions too). Over-eager multithreading can make your project harder to grow, easier to introduce bugs into, and harder to reproduce and diagnose those bugs. So start by outlining a specific problem you're having with sequential execution and we can help you solve that. \$\endgroup\$
    – DMGregory
    Jun 15, 2017 at 16:29
  • 1
    \$\begingroup\$ It seems like a good idea to research Jobs in the context of threading rather than creating monolithic areas of engine that live on different threads. Many recent engines use jobs and workers instead of UI-thread, display-thread, megablocks. \$\endgroup\$ Jun 16, 2017 at 2:01

1 Answer 1

4
\$\begingroup\$

First of all, heed the warning by DMGregory in the comments to the question. Multithreading is an evil source of weird, randomly appearing and impossible to reproduce bugs. Do not use it unless you know you have a performance problem and you already exhausted all other options of optimization.

Unfortunately there is no easy answer to the question which game features to parallelize and which not, because every game is different. But the general rule of thumb is that you want to parallelize tasks which are very CPU-intense, can be separated into independent units of computation and do not write any data to memory which is used by the other units of computation. The reason for this is that whenever you have one thread reading data another thread is writing, you might once in a while hit a weird race-condition where you are reading an intermediate state of the old and the new data.

For example, if you want to do route finding on a complex nav-mesh for 10000 actors at once, you might be able to multithread that by having 4 threads process 2500 actors each. But only if your actors don't communicate the results of their route-finding to each other and no unit actually executes a move until all threads have finished calculating their routes. Make sure to join all threads before you continue.

Regarding separating game mechanics from rendering: I would usually advise against this stunt, for the reason described above: Race Conditions. If you do this naively, then your rendering loop might from time to time end up rendering an object which is half in the previous frame and half in the next frame, which can manifest as really weird graphic glitches. Also, keep in mind that the heavy lifting of rendering usually happens mostly on the GPU, not the CPU, and those computations are asynchronous anyway. So the benefit of this is in many cases dubious anyway.

But if you do something in your rendering loop which is unusually CPU-intense and you really want to do this, you can avoid those race conditions by using a double-buffering strategy on your game-state. Have two copies of your game-state. While state A is rendered, your game loop calculates a new game-state B. When both the rendering-thread and the game-thread are finished, you switch the two states. game-state B is rendered while a new game-state A is calculated based on B. The same strategy can be used to execute any other game systems in parallel, as long as no two game systems need write-access to the same data-structure of the new game-state.

Another problem you should keep being aware of is that creating, managing and switching between threads is quite expensive on most operating systems. For that reason you should avoid creating short-lived threads and you should never have more threads than you have CPU cores (unless they are threads which idle most of the time, like a network listener or a file reading thread).

\$\endgroup\$

You must log in to answer this question.

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