Get SpatialOS



Thread affinity

Thread affinity, or CPU pinning, enables the binding of a thread to a specific logical core, or set of cores. It prevents the operating system scheduler from switching threads amongst many cores, which can reduce performance in performance critical environments, such as console games. The Worker SDK now exposes thread affinity masks, which enable users to pin internal SpatialOS threads to specific cores in the same way other game logic threads would be pinned in a typical game engine.

Why use thread affinity?

Consoles have a fixed hardware configuration which you can optimize against. This is in contrast to desktops or laptops, that must scale to arbitrary CPU configurations or OS schedulers.

Benefits of thread affinity:

  • Avoids load from SpatialOS networking having an impact on rendering, audio, AI job systems and other engine systems, and vice versa.
  • Improve performance by increasing the percentage of local memory accesses.

If a game studio has shipped console titles before, engine programmers may have an established setup for thread affinity spanning physics, rendering, networking, audio, AI job systems, etc. SpatialOS aims to accommodate this by exposing thread affinity parameters.


Thread affinity does not work on the same way on macOS compared to other platforms. Instead, the affinity mask provides a hint to the scheduler.

SpatialOS uses several long-running and temporary threads on each worker. Long-running threads are involved in sending and receiving network packets, processing incoming messages, handling command timeouts and reporting metrics. Temporary threads are involved in initial connection attempts and connection handling.

Thread affinity is configurable by passing affinity masks within the ConnectionParameters for each language of the Worker SDK. Affinity masks are bit masks where setting 1 in the nth least significant position means the thread will be permitted to run on the nth core. Setting the affinity mask to permit a core index which does not exist is undefined behaviour, and we are not performing any checks at runtime whether the logical core count accommodates the affinity masks.

struct ThreadAffinityParameters {
  /** Affinity mask for threads related to receiving network ops. */
  uint64_t ReceiveThreadsAffinityMask;
  /** Affinity mask for threads related to sending network ops. */
  uint64_t SendThreadsAffinityMask;
  /** Affinity mask for short-lived threads. */
  uint64_t TemporaryThreadsAffinityMask;

Exposing the thread affinity configuration in this manner is a compromise between a single shared thread affinity mask limiting flexibility, and a thread affinity mask for each long running and temporary thread meaning we would introduce API breaking changes when adjusting our internal thread usage.

Language-specific syntax is available in the reference docs:


Below is a code snippet example of setting the thread affinity of SpatialOS threads within the C++ blank project.

worker::ConnectionParameters parameters;
parameters.ThreadAffinity.ReceiveThreadsAffinityMask = 8;
parameters.ThreadAffinity.SendThreadsAffinityMask = 4;
parameters.ThreadAffinity.TemporaryThreadsAffinityMask = 2;

Here, this snippet sets the thread affinity masks for receiving threads to 8, sending threads to 4, and temporary threads to 2. These are bit masks; a bit mask of 8 is equivalent to the binary string 00001000 with the 4th least significant bit set to 1. This means that receiving threads inside the SpatialOS SDK will only run on your logical core indexed 4th on your target platform. Similarly, sending threads affinity mask is 00000100, and so these threads will only run on the 3rd logical core. Lastly, temporary threads affinity mask is 00000010, and so these threads will only run on the 2nd logical core.

Search results

Was this page helpful?

Thanks for letting us know!

Thanks for your feedback

Need more help? Ask on the forums