Analyze thread scheduling

There are a few things to consider in order to determine if your game process threads are appropriately utilized and scheduled for the best performance.

  • Frame pacing
  • Multithreading and thread parallelization
  • CPU core affinity

Multithreading

Many games and game engines use multithreading to divide CPU work into logical tasks, which may be run somewhat independently. One typical configuration is a game thread for input and game logic, a render thread for preparing and submitting objects to be drawn, and worker threads for other subtasks such as animations or audio.

We recommend parallelizing threads to take advantage of performance gains of multithreading. An example of this is a scenario where the game and render threads are running partially or fully concurrently on different cores. This won't always be possible, such as in cases with shared data dependencies; however, when possible, this may result in lower CPU times and thus potentially higher frame rates.

Game with a well-parallelized main and render thread, as well as a worker thread and audio thread
Figure 1. Game with a well-parallelized main and render thread, as well as a worker thread and audio thread

CPU core affinity

One factor that significantly affects the performance of your CPU workloads is how they are scheduled on the cores. This may be split into two components:

  • Whether your game threads are running on the best cores for performance
  • Whether your game threads switch between cores frequently

You may investigate CPU thread behavior under CPU Usage by enabling the CPU in the profile config when taking a trace. By zooming into a section of your trace <200 ms, you can view the individual processes running on your device’s CPU cores. Typically, small cores correspond to smaller indices (for example, CPU 0-3) whereas large cores correspond to higher indices (for example, CPU 6-7).

Generally, when the game is in the foreground, persistent threads such as the game thread and render thread should run on the high-performance large cores, whereas other process and worker threads may be scheduled on smaller cores.

Game with main and render thread primarily running on the large cores (CPU 6-7), shown in light blue
Figure 2. Game with main and render thread primarily running on the large cores (CPU 6-7), shown in light blue

You may also observe whether your game threads switch between cores frequently, if your main and render thread change cores within a single CPU frame or between two consecutive CPU frames. This CPU behavior is likely an indicator that your game threads are not properly affinitized. Such core switches incur some overhead from the context switch and the loss of state with a core’s cache/registers, resulting in an increase in the length of your CPU frame.

Game with main (Thread-7) and render thread (Thread-8) that switch between cores, shown in purple
Figure 3. Game with main (Thread-7) and render thread (Thread-8) that switch between cores, shown in purple