Course contentsShow
← Rust
Lesson 1707 of 1,91015. Performance and Architectural EngineeringFree lesson

Array-of-Structures vs Structure-of-Arrays

Choose between data layouts based on whether the workload is field-oriented or record-oriented.

In this lesson, you will learn how to choose between Array-of-Structures (AoS) and Structure-of-Arrays (SoA) based on your workload’s access patterns to maximize cache efficiency.

The Data Layout Dilemma

When managing collections of entities, the physical arrangement of data in memory determines how effectively the CPU cache prefetcher can serve your operations.

Array-of-Structures (AoS)

The standard approach in Rust is grouping related data into a struct and storing them in a Vec.

struct Particle {
    x: f32,
    y: f32,
    active: bool,
}
let particles: Vec<Particle> = Vec::with_capacity(1000);

Use Case: Record-Oriented Workloads. If your logic frequently processes one complete entity at a time (e.g., updating a single Particle by reading all its fields simultaneously), AoS is ideal. The CPU loads the entire Particle struct into the cache line, minimizing memory requests when you perform operations across its fields.

Structure-of-Arrays (SoA)

Alternatively, you can group identical fields together into separate vectors.

struct ParticleSystem {
    x: Vec<f32>,
    y: Vec<f32>,
    active: Vec<bool>,
}

Use Case: Field-Oriented Workloads. If your workload performs bulk operations on specific fields—such as calculating a new position for all particles by reading only the x and y coordinates—SoA is superior. Because the x values are contiguous, the CPU can saturate the cache lines with relevant data, avoiding the "pollution" caused by loading the active flag into the cache when it isn't needed for the current calculation. This approach is also friendlier to SIMD (Single Instruction, Multiple Data) optimizations, as the compiler can easily vectorize operations across a contiguous slice of floats.

Selecting the Right Strategy

  • Choose AoS if your hot loop iterates over individual objects and interacts with multiple fields per object. It maintains high cache locality for the entity's complete state.
  • Choose SoA if your hot loop performs "columnar" transformations or bulk updates on a single field across thousands of items. It reduces bandwidth waste and enables better utilization of CPU prefetchers and vector registers.

Key Takeaway: Choose AoS for entity-centric processing to minimize latency on single-record access, and choose SoA for bulk, field-centric transformations to maximize throughput and SIMD utilization.