False Sharing is the insidious parallel performance pathology where two or more threads on different cores modify independent variables that happen to reside on the same cache line — causing the hardware cache coherence protocol to repeatedly invalidate and reload the entire cache line across cores, creating catastrophic cache line "ping-pong" that can slow down parallel code by 10-100x despite the threads sharing no logical data.
How False Sharing Occurs
CPU caches operate on cache lines (typically 64 bytes). When Thread 0 on Core 0 writes variable A and Thread 1 on Core 1 writes variable B, and A and B are within the same 64-byte cache line, the coherence protocol (MESI/MOESI) invalidates Core 1's copy when Core 0 writes, and vice versa. Each write forces the other core to fetch the updated cache line from the L3 cache or the writing core's L1 — a round trip of 40-100 cycles per access instead of 3-4 cycles for a local L1 hit.
Classic Example
int counters[NUM_THREADS]; // Adjacent in memory!
void work(int tid) {
for (int i = 0; i < 1000000; i++)
counters[tid]++; // Each thread increments its own counter
}
counters[0] through counters[15] all live in a single 64-byte cache line. Despite each thread modifying only its own counter, every increment invalidates the line for all other cores. Performance: 10-50x slower than the single-threaded case.
Detection
- Performance Counters: High L1/L2 coherence miss rate (HITM events on Intel) despite non-shared data. Linux
perf c2cspecifically detects false sharing. - Profiling Tools: Intel VTune's memory access analysis highlights cache lines experiencing excessive coherence traffic.
Solutions
1. Padding: Insert unused bytes between variables to place them on separate cache lines:
struct PaddedCounter {
int value;
char padding[60]; // Ensure each counter occupies its own 64-byte line
};
PaddedCounter counters[NUM_THREADS];
2. Alignment: Use compiler attributes (alignas(64) in C++11, __attribute__((aligned(64))) in GCC) to force cache line alignment.
3. Thread-Local Accumulation: Each thread accumulates into a local variable (register), writing to the shared array only once at the end.
4. Data Structure Redesign: Replace arrays of per-thread values with per-thread structures spaced at cache line boundaries. Many parallel libraries (Intel TBB, Java @Contended) provide padded per-thread containers.
False Sharing is the performance trap that punishes the illusion of independence — threads that believe they are working on private data are secretly fighting over cache lines, and the hardware coherence protocol silently converts what should be embarrassingly parallel code into a serialized disaster.
Explore 500+ Semiconductor & AI Topics
From EUV lithography to CUDA optimization — search the full knowledge base or chat with our AI assistant.