Use 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 SDKs now expose 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 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. 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.

  public class ThreadAffinityParameters {
    // Affinity mask for threads related to receiving network ops.
    public System.UInt64 ReceiveThreadsAffinityMask;
    // Affinity mask for threads related to sending network ops.
    public System.UInt64 SendThreadsAffinityMask;
    // Affinity mask for short-lived threads.
    public System.UInt64 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.

See the specific C# syntax.


Below is a code snippet example of setting the thread affinity of SpatialOS threads.

  var parameters = new ConnectionParameters();
  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.

Updated about a year ago

Use thread affinity

Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.