Modern CPUs have multiple cores, each with its own L1/L2 cache. Cache coherence protocols (like MESI) ensure that when one core writes to a cache line, other cores’ copies are invalidated. This creates subtle performance traps.
Cache Lines
Memory is transferred between RAM and CPU caches in fixed-size blocks called cache lines (typically 64 bytes on x86). If a program accesses a single byte, the entire 64-byte block is loaded into the cache.
False Sharing
Two threads access different variables that happen to sit on the same cache line. Neither thread uses the other’s variable, but the cache coherence protocol treats them as the same line.
Thread A writes to x (on cache line N)
→ Core B's copy of line N is invalidated
Thread B writes to y (on the same line N)
→ Core A's copy of line N is invalidated
→ Both cores keep reloading the same line
Result: performance collapses — up to 100x slower. Neither thread shares data (no real sharing), but the hardware treats it as sharing. Hence “false sharing.”
Fix: Padding
// Before: x and y share a cache line
struct { int x; int y; };
// After: padded to separate cache lines
struct { int x; char pad[64]; int y; };
Memory Barriers (Fences)
CPUs reorder memory operations for performance. A memory barrier prevents reordering around the barrier.
// Thread 1 Thread 2
data = 42; while (!flag_ready);
flag_ready = 1; print(data);
Without a barrier: Thread 2 could see flag_ready == 1 but data == 0 — the write to data might be delayed past the write to flag_ready.
Barrier Types
| Barrier | Prevents |
|---|---|
mfence (full) | All reordering around the fence |
acquire (load) | Later loads/stores moving before |
release (store) | Earlier loads/stores moving after |
In high-level code: use std::atomic or synchronized — compilers insert the right barriers.
Q: What is false sharing?
A: Two threads access different variables that sit on the same cache line. Although they don’t share data logically, the cache coherence protocol treats the line as shared — invalidating it across cores on every write. This causes massive cache contention with zero data sharing.
Q: How do you detect false sharing?
A: Perf counters showing high cache miss rates with low actual data sharing. Thread A writes to address X, thread B writes to address Y, but X and Y are within 64 bytes. Tools: Linux perf (cache-misses), Intel VTune, Valgrind.
Q: How do you fix false sharing?
A: Pad structures so that fields accessed by different threads land on different cache lines. alignas(64) or __attribute__((aligned(64))) on C++ structs. In Go/Java, the compiler may add padding automatically.
Q: What is a memory barrier and when is it needed?
A: A CPU instruction that prevents memory reordering across the barrier. Needed when implementing lock-free code or custom synchronization. Regular mutexes already include the necessary barriers. Without them, you can observe operations happening in a different order than written in source code.
Premium Content
Unlock False Sharing & Memory Barriers and all premium lessons with a subscription.
From ₹199.99/year — See plans