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

Managing CPU Cache Coherency

Minimize synchronization traffic between cores by reducing shared state updates.

In this lesson, you will learn how to minimize expensive cache coherency traffic by structuring your data to eliminate unnecessary cross-core synchronization.

The Cost of Consistency

Modern CPUs use the MESI (Modified, Exclusive, Shared, Invalid) protocol to ensure cache coherency. When one core modifies a memory location, the hardware must broadcast an "invalidate" signal to every other core's cache. If multiple threads frequently write to the same cache line, the hardware spends more time bouncing the cache line between cores—a phenomenon known as "cache ping-ponging"—than it does executing your code.

The Strategy: Thread-Local Isolation

To minimize synchronization traffic, you must design your architecture to favor data ownership over shared mutable state. Instead of wrapping a shared structure in a Mutex, strive to make data effectively "read-only" after initialization or partition it such that each thread works on a private copy.

Consider a performance-critical counter. A naive approach uses an AtomicU64 shared across all threads:

// High traffic: constant cache invalidation messages across cores
let global_counter = Arc::new(AtomicU64::new(0));

// Instead, use thread-local storage:
thread_local! {
    static LOCAL_COUNTER: Cell<u64> = Cell::new(0);
}

By using thread_local!, each core maintains its own counter in its local cache. There is zero cache coherency traffic during the hot loop because no other core needs to invalidate the local line. You only perform a single synchronization at the very end when aggregating the results.

Designing for Immutability

When data must be shared, prioritize immutable sharing. If your data structure is Sync and does not require mutation (e.g., using Arc<T> where T is read-only), the cache line remains in the "Shared" state across all cores. The CPU hardware handles this gracefully by allowing all cores to read the line simultaneously without firing off invalidation traffic.

By partitioning data into thread-local buckets or enforcing read-only patterns for shared state, you reduce the workload on the memory bus and allow the hardware to maintain coherence with minimal interference.

Key Takeaway: Maximize performance by ensuring that cache lines are either owned by a single core or remain strictly read-only across all cores to avoid expensive cache-line invalidation cycles.