In this lesson, you will learn how to restructure your data layouts to maximize CPU cache efficiency by transforming "Array of Structures" into "Structure of Arrays."
The Problem: Cache Misses
Modern CPUs are incredibly fast, but fetching data from system RAM is slow. To bridge this gap, the CPU relies on its L1/L2 caches. When you load a byte into the cache, the CPU pulls in a whole "cache line" (usually 64 bytes). If your object contains fields you don't need for a specific operation, you are wasting precious cache space, leading to "cache misses."
From Object-Oriented to Data-Oriented
In a typical Object-Oriented approach, we define a list of objects like this:
public struct Particle {
public Vector3 Position; // 12 bytes
public Vector3 Velocity; // 12 bytes
public float Health; // 4 bytes
public int Id; // 4 bytes
}
List<Particle> particles;
When you calculate Position += Velocity, the CPU loads the Health and Id into the cache unnecessarily. This is an "Array of Structures" (AoS).
To optimize for the CPU, we shift to a "Structure of Arrays" (SoA) layout. We group the data by how it is accessed:
public class ParticleSystem {
public Vector3[] Positions;
public Vector3[] Velocities;
public float[] Healths;
public int[] Ids;
}
Why This Wins
By storing data in separate, contiguous arrays, a loop updating Positions will only load Position data into the cache. Because the data is packed contiguously, the CPU prefetcher can predict the next memory address and load it before the instruction even requests it. This eliminates the "fat" in your memory layout, allowing you to process significantly more data in the same number of clock cycles.
By aligning your memory layout with your execution patterns, you minimize cache misses and maximize the throughput of your memory bus.
Key Takeaway: Organize your data into contiguous arrays based on how it is accessed during execution to ensure the CPU cache stays populated with relevant data rather than unused object properties.