False sharing problem
What Is False Sharing?
False sharing occurs when multiple processor cores modify logically independent variables that happen to reside in the same cache line. This triggers cache coherence protocols (like MESI) to invalidate and transfer the line between cores, creating contention despite no actual data sharing.
Why it happens:
- Cache coherence operates at cache line granularity (typically 64 bytes)
- Compilers/allocators pack data structures densely
- Adjacent array elements or struct fields land in the same line
- Each core's write invalidates the line in other cores' caches
The critical insight: The hardware cannot tell that you're accessing different bytes. It only knows: "Someone wrote to this 64-byte block, so I must invalidate everyone else's copy."
The Mechanism: Cache Line Ping-Pong

Derivation from First Principles
Let's build up why this happens:
Step 1: Cache coherence granularity
- Memory is divided into blocks (cache lines) of size bytes (typically )
- Address maps to cache line
- Two addresses share a line if:
Step 2: MESI protocol constraint
- A line can be Modified in at most one cache
- Write to Modified line: local, fast (1-2 cycles)
- Write to Shared/Invalid line: must acquire exclusive ownership
- Send invalidation to all other caches
- Wait for acknowledgments
- Transition to Modified
- Cost: 50-200 cycles (remote cache access)
Step 3: False sharing scenario
Consider two cores accessing different variables:
- Core 0 writes to address (variable
counter[0]) - Core 1 writes to address (variable
counter[1]) - If , they share a cache line
Timeline:
Time Core 0 Core 1 Line State
0 Write counter[0] - Modified in C0
1 - Write counter[1] → Invalidate C0
→ Modified in C1
2 Write counter[0] - → Invalidate C1
→ Modified in C0
3 - Write counter[1] → Invalidate C0
→ Modified in C1
... (ping-pong continues)
Cost per false sharing write:
Compare to isolated write:
Slowdown factor:
Concrete Examples
The Solution: Cache Line Padding
Goal: Ensure each core's data occupies a separate cache line.
Common Mistakes
Detection and Measurement
How to detect false sharing:
-
Profiling tools:
- Linux
perf c2c(cache-to-cache): Shows which cache lines have high coherence traffic - Intel VTune: "Memory Access Analysis" highlights contended cache lines
- AMD μProf: Cache line contention view
- Linux
-
Performance counters:
- Monitor
MEM_LOAD_RETIRED.FB_HIT(load from fill buffer, sign of recent invalidation) OFFCORE_RESPONSE.DEMAND_RFO.L3_MISS.REMOTE_HITM(remote cache hit-modified, false sharing signature)
- Monitor
-
Symptoms:
- Code scales poorly (2cores slower than 1core per throughput)
- High L3 cache miss rate despite good locality
- CPU utilization low but no obvious blocking
Quantifying the cost:
Let = writes per core, = coherence latency (fetch line from a remote cache), = local write latency.
Without false sharing — every write hits a line already in Modified state locally:
With false sharing — in the worst case, every write finds the line stolen by another core, so it must pay the coherence penalty once to re-acquire it:
Key correction — the per-write penalty does NOT scale with core count. Each contended write incurs exactly one coherence miss (fetch the line back from whoever stole it), regardless of how many cores participate in the ping-pong. Adding more cores makes it more likely your line gets stolen before your next write (raising the probability of paying the penalty toward 1), but it never makes a single write pay the penalty multiple times.
Per-write slowdown factor:
This is independent of . So the realistic worst-case slowdown is bounded by the ratio (~50–100×), not by .
Total time (worst case, every write contended):
The Big Picture: When to Worry
High risk scenarios:
- Per-thread counters/statistics (arrays indexed by thread ID)
- Shared data structures with per-core fields (like the
ThreadDatastruct) - Lock-free data structures with adjacent metadata
- Producer-consumer ques where head/tail pointers are close
Low risk scenarios:
- Read-only data (no invalidations)
- True data sharing (cores need to see each other's updates anyway)
- Single-writer patterns (one core modifies, others read)
Recall Explain to a 12-year-old
Imagine you and your friend are both doing homework at a big table. You're working on math, they're working on history—totally different subjects. But there's only one big eraser on the table.
Every time YOU make a mistake and grab the eraser, your friend has to stop and wait. And every time THEY grab the eraser, you have to stop. Even though you're not using the same homework sheet, you keep blocking each other because you're fighting over the same eraser.
That's false sharing! The eraser is like a "cache line" in the computer. Even though you're working on different variables (math vs. history), if they're stored in the same cache line (the same eraser), the computer cores keep blocking each other. And here's the subtle part: grabbing the eraser back from your friend takes the same amount of time whether there are 2 friends or 10 friends at the table—you only ever grab it from whoever has it right now. The fix? Give each person their own eraser (pad the variables into separate cache lines), and now everyone can work at full speed!
Connections
- Cache coherence protocols — MESI/MOESI are the mechanisms that cause false sharing overhead
- Cache line size and alignment — Understanding64-byte granularity is foundational
- Lock-free data structures — Often vulnerable to false sharing in metadata
- NUMA architectures — False sharing is even more expensive cross-socket
- Memory allocators — jemalloc/tcmalloc can reduce false sharing with per-thread arenas
- Atomic operations — Provide correctness but don't prevent false sharing
- Multicore scaling — False sharing is a major barrier to linear speedup
#flashcards/hardware
What is false sharing? :: When multiple cores write to logically independent variables that reside in the same cache line, causing unnecessary cache coherence traffic and performance degradation.
Why does false sharing cause slowdown even when there's no data race?
What is the typical cache line size on x86 processors?
How much slower can false sharing make parallel code?
Does the per-write false-sharing penalty scale with the number of cores N?
What is the formula to check if two addresses share a cache line?
How do you fix false sharing?
alignas(64) or struct padding to guarantee separation.What C++17 feature helps with portable cache line padding?
std::hardware_destructive_interference_size — gives the cache line size for the current architecture.Can atomic operations prevent false sharing?
What Linux tool can detect false sharing?
perf c2c (cache-to-cache) — shows which cache lines have high coherence traffic between cores.What is the "ping-pong" effect in false sharing?
If you pad to 128 bytes instead of 64, what's the downside?
What performance counter indicates false sharing on Intel?
OFFCORE_RESPONSE.DEMAND_RFO.L3_MISS.REMOTE_HITM — shows remote cache hit-modified, the signature of false sharing.In a per-thread counter array, why does false sharing occur?
counter[0], counter[1]) are only 4 bytes apart, so 16 of them fit in one64-byte cache line. All cores contend for the same line.What's the memory cost of fixing false sharing for 16 threads?
Concept Map
Hinglish (regional understanding)
Intuition Hinglish mein samjho
Hinglish (regional understanding)
Intuition Hinglish mein samjho
Dekho yaar, false sharing ka core idea bahut simple hai par bahut logon ko dikhta nahi. Jab multiple CPU cores parallel kaam karte hain, toh cache coherence hardware line-by-line kaam karta hai, jahan ek cache line typically 64 bytes ki hoti hai. Ab problem yeh hai ki agar do cores alag-alag variables pe likh rahe hain, lekin woh variables physically ek hi cache line ke andar baithe hain, toh hardware ko lagta hai ki dono same data touch kar rahe hain. Woh doosre core ki copy ko invalidate kar deta hai, chahe logically dono ka data bilkul independent ho. Bilkul us roommate example ki tarah jahan alag-alag pages likhne pe bhi poora folder lock ho jata hai.
Ab yeh matter kyun karta hai? Kyunki cost ka farak zabardast hai. Jab ek core apni local Modified line pe likhta hai toh sirf 1-2 cycles lagte hain, but jab false sharing hoti hai toh usse exclusive ownership leni padti hai, doosre caches ko invalidation bhejni padti hai aur acknowledgment ka wait karna padta hai, jisme 100-200 cycles chale jaate hain. Matlab tumhara code 50 se 100 guna slow ho sakta hai, aur sabse pareshaan karne wali baat yeh ki koi actual data sharing hai hi nahi! Tumhara program logically toh perfectly parallel hai, par hardware usse serialize kar deta hai. Isko cache line ping-pong bolte hain, jahan line baar-baar ek core se doosre core ki taraf bounce karti rehti hai.
Iska practical takeaway yeh hai ki jab tum multicore programming karte ho, tumhe apni data structures ka memory layout dhyan se sochna padta hai. Jaise agar tum ek array of counters bana rahe ho jahan har thread ek counter update karega, toh unhe alag-alag cache lines pe padding daal ke separate karna zaroori ho jaata hai. Yeh chhoti si cheez tumhare parallel program ki performance ko theek karke rakhti hai, warna tumhe lagega ki extra cores add karne se speed badh rahi hai, par actually woh ek dusre ko block karke sab slow kar rahe honge. Toh yaad rakho: hardware ko byte-level ka pata nahi, use sirf 64-byte block dikhta hai.