How Unity DOTS Improves CPU Cache Locality

Unity’s Data-Oriented Technology Stack (DOTS) transforms game performance by shifting the underlying architecture from an object-oriented model to a data-oriented design. While traditional MonoBehaviours scatter data across memory and degrade hardware efficiency through constant cache misses, DOTS organizes component data contiguously in memory. This fundamental architectural shift maximizes CPU cache utilization, minimizes latency, and allows modern processors to operate at peak efficiency when simulating large numbers of game elements.

The Cache Bottleneck in MonoBehaviours

Traditional Unity development relies on MonoBehaviours, which are standard C# classes inheriting from managed objects. Because classes are reference types, their data is allocated dynamically on the managed heap. Over time, as objects are spawned, moved, and destroyed, these allocations become fragmented across disparate memory locations.

When a system iterates through a list of MonoBehaviours to update them, the CPU must follow memory pointers to fetch each individual object. This process, known as "pointer chasing," severely degrades CPU cache locality. Furthermore, MonoBehaviours follow an Array-of-Structures (AoS) approach, bundling logic, transforms, physics data, and custom fields into a single monolithic object.

Modern CPUs load data into fast L1, L2, and L3 caches in 64-byte blocks called cache lines. When an update loop touches a MonoBehaviour, the CPU loads the entire 64-byte chunk into the cache. If the system only needs to read a 12-byte Vector3 position, the remaining 52 bytes of the cache line are filled with irrelevant metadata, virtual method tables, and unused fields. This results in frequent cache evictions and cache misses, forcing the CPU to stall while waiting for high-latency system RAM to deliver data.

How DOTS Maximizes Cache Locality

DOTS eliminates these inefficiencies by decoupling data from behavior through the Entity Component System (ECS). In ECS, entities are lightweight IDs, components are pure data structs containing no logic, and systems contain execution logic without storing state.

1. Contiguous Memory and Archetypes

DOTS groups entities sharing the exact same component types into specific memory blocks called Archetype Chunks (typically 16 KB in size). Within each chunk, components are stored using a Structure-of-Arrays (SoA) layout rather than an Array-of-Structures layout. All instances of a specific component type are packed tightly alongside one another in contiguous memory arrays.

2. Efficient Cache Line Saturation

Because components are packed contiguously, iterating over a specific component ensures that nearly 100% of each 64-byte cache line contains data relevant to the current operation. If a system updates positions, the CPU loads a cache line filled exclusively with position data. There are no object headers, no pointers, and no extraneous fields hogging cache capacity.

3. Predictable Hardware Prefetching

Modern CPUs feature hardware prefetchers that detect linear memory access patterns and automatically pull subsequent data from RAM into the cache before the instruction even requests it. Because DOTS processes linear, contiguous arrays inside chunks, the CPU prefetcher can accurately predict incoming memory needs, virtually eliminating CPU idle time caused by memory latency.

4. Hardware Optimization via Burst and Jobs

This optimized memory layout enables the C# Job System and the Burst Compiler to achieve maximum throughput. With data guaranteed to be contiguous and free of pointer aliasing, the compiler can auto-vectorize code using Single Instruction, Multiple Data (SIMD) instructions. This allows a CPU core to process multiple components in a single clock cycle, a feat that is impossible when dealing with the fragmented memory patterns of traditional MonoBehaviours.