In this lesson, you will learn how to structure your game data to keep related information physically close in memory, allowing your CPU to process it significantly faster.
The Problem: Jumping Across Memory
Modern CPUs are incredibly fast, but they spend a surprising amount of time waiting for data to arrive from your computer's RAM. When your CPU needs a piece of data, it fetches a "cache line" (a small chunk of memory) rather than just one variable. If your data is scattered across different memory locations—a common occurrence when using complex UObject pointers—the CPU suffers from "cache misses," forcing it to stall while it fetches the next chunk from slow RAM.
Data Oriented Design (DOD)
Instead of thinking about "objects" that contain all their own properties, think about "streams" of data. By organizing your data into contiguous arrays (TArray), you ensure that when the CPU fetches one element, the next several elements are already sitting in the high-speed cache, ready for processing.
Consider a system updating the positions of 1,000 projectiles:
Poor Locality (Array of Objects):
// Each TArray element is a pointer; projectiles are scattered in memory.
TArray<AProjectile*> Projectiles;
for (AProjectile* P : Projectiles) {
P->UpdatePosition(); // CPU jumps to a new memory address every iteration.
}
Improved Locality (Structure of Arrays):
struct FProjectileData {
TArray<FVector> Positions;
TArray<FVector> Velocities;
};
// All vectors are stored contiguously in memory.
for (int32 i = 0; i < Data.Positions.Num(); ++i) {
Data.Positions[i] += Data.Velocities[i] * DeltaTime;
// The next position is already in the cache!
}
By storing the raw data (the FVector coordinates) in a simple TArray rather than inside an array of UObject pointers, you remove the overhead of pointer indirection. When the CPU processes index i, the cache controller has already pre-fetched index i+1, i+2, and so on. This turns a slow, jumpy process into a smooth, linear sweep that maximizes the hardware's capabilities.
Key Takeaway: To optimize high-frequency loops, prefer storing primitive data in contiguous TArray buffers rather than iterating over arrays of pointers to individual objects.