6.1.10 · D5Parallelism & Multicore
Question bank — False sharing problem
Before the traps, one anchor picture so every claim below has something to point at:

The two variables x and y are different bytes but sit inside the same 64-byte line. The hardware coherence unit is the whole line — it cannot see the byte boundary you care about. Keep this image in mind; every trap below is a variation of it.
True or false — justify
True or false: False sharing is a correctness bug that can produce wrong answers.
False — it is purely a performance bug. Every read and write still returns the correct value; the program is just slow because the line ping-pongs between caches.
True or false: If two variables are in the same cache line but only one core ever writes to them, false sharing still occurs.
False — false sharing needs a writer competing with another accessor. One lone writer keeps the line Modified in its own cache with no invalidation traffic.
True or false: Two cores that only ever read the same cache line suffer false sharing.
False — the MESI Shared state lets many caches hold a read-only copy simultaneously. No invalidations fire until someone writes.
True or false: Using std::atomic on each counter removes false sharing.
False — atomics guarantee no torn/lost updates but the atomic write still needs the line in Modified state, so it invalidates the other core's copy exactly like a plain write.
True or false: False sharing can happen between two completely unrelated global variables you never intended to be near each other.
True — the linker/allocator may place independent globals in adjacent bytes of the same line; logical independence gives no physical separation.
True or false: Making the cache line larger would reduce false sharing.
False — a larger line packs more independent variables together, so it makes false sharing more likely, not less.
True or false: False sharing gets worse as you add more cores writing to the shared line.
True — each extra writer adds another party that must be invalidated, so the ping-pong involves more caches and coherence traffic scales up, hurting Multicore scaling.
True or false: Padding a struct to 64 bytes always wastes exactly 60 bytes per element.
False — the waste is bytes; it depends on the payload size, and if the payload already fills a line the waste can be zero.
True or false: On a single-core machine, false sharing still slows your program.
False — with one core there is only one cache and no other copy to invalidate, so the coherence ping-pong that defines false sharing cannot arise.
Spot the error
"I aligned every counter to 8 bytes, so each one gets its own cache line." — what's wrong?
8-byte alignment still packs eight variables into one 64-byte line. You need alignment to the line size (64 bytes), not the word size. See Cache line size and alignment.
"My struct's fields are private and each thread only touches its own field, so there's no sharing." — what's wrong?
Language-level privacy is invisible to the hardware. The coherence protocol works on physical addresses; if the private fields share a line, they falsely share regardless of access modifiers.
"I'll fix false sharing by putting a lock around each counter." — what's wrong?
A lock serializes access but does nothing about where the bytes live. The lock itself may false-share, and even correct locking still bounces the same line — you added contention, not separation.
"False sharing only affects arrays, so my scalar variables are safe." — what's wrong?
Any two hot variables that land in the same line false-share, whether they came from an array, two struct fields, or two separate globals placed adjacently by the allocator.
"I added char padding[64] after my int value, so it's a full line now." — what's wrong?
The int plus 64 padding bytes is 68 bytes — larger than a line, so two elements can straddle a boundary and still share. You want the total to equal (or align to) 64, e.g.
char padding[60]."I read the value on core 0 and only write it on core 1, so no invalidation happens on core 0." — what's wrong?
Core 1's write forces the line to Modified in core 1, invalidating core 0's read copy. Core 0's next read then misses and must re-fetch — that is exactly the false-sharing cost.
"To avoid false sharing I moved the two variables 32 bytes apart." — what's wrong?
32 bytes is less than a 64-byte line, so they can still occupy the same line. Separation must be at least one full line and the base addresses must land in different lines.
Why questions
Why can't the CPU just track sharing at the individual-byte level to avoid this?
Byte-level coherence tags would need enormous per-byte state and traffic; hardware trades that off by tracking whole cache lines, which is efficient normally but blind to sub-line independence.
Why is a read-modify-write (like counter++) the worst pattern for false sharing?
It requires the line in exclusive/Modified state to complete, so every increment must own the line — guaranteeing an invalidation of every other core that touched it. See Atomic operations.
Why does false sharing hurt even though there is "no real data dependency"?
The dependency is physical, not logical — the shared 64-byte transfer unit couples the variables. The hardware enforces coherence on the line, so independent logic still pays coherent-transfer cost.
Why does padding "trade space for time"?
You spend extra memory (dead padding bytes) so that each hot variable owns its line; the payoff is dropping from ~100–200 coherence cycles per write down to ~1–2 local cycles.
Why prefer std::hardware_destructive_interference_size over hard-coding 64?
It lets the compiler pick the architecture's true line size (some CPUs differ, and prefetchers may fetch pairs of lines), keeping your padding portable instead of tied to one x86 assumption.
Why can false sharing quietly appear after a "harmless" refactor that reorders struct fields?
Reordering changes which fields land in which line. A field two cores hammer might move from a lonely line into a shared one, so identical logic suddenly ping-pongs. This is why Memory allocators and layout choices matter.
Why does false sharing interact badly with NUMA architectures?
The invalidated line may live in a remote memory node, so re-fetching it crosses the interconnect — turning a local cache miss into a far more expensive cross-node round trip.
Edge cases
If a variable is written by only one core but read by several, what state does the line settle into and is there false sharing?
It settles into Shared for the readers until the writer strikes; the single writer periodically invalidates all readers, so yes, there is real coherence traffic — a milder but genuine false-sharing case.
What happens if two false-sharing variables are actually never accessed at the same time (e.g. phase-separated)?
If accesses never overlap in time, the line is handed off cleanly with no contention; false sharing only bites when writes to the line interleave across cores.
Does false sharing exist if both variables sit in a line that is currently in the Invalid state in both caches (cold start)?
On the first touch there is no other copy to invalidate, so no false-sharing penalty yet; the ping-pong only begins once both cores have populated and started writing the line.
What is the smallest number of cores and writers needed for false sharing to occur?
At least two cores, and at least two accessors of the same line where one is a writer — one on each core, each hitting a different byte of the shared line.
If sizeof(payload) already equals the cache line size, how much padding is required?
None — the payload alone fills the line, so the next element naturally starts on a new line, giving for free.
At what array size does false sharing between counter[0] and counter[7] (4-byte ints, 64-byte line) disappear naturally?
counter[0]..counter[15] all fit one line, so indices 0 and 7 share it; they only separate once elements are ≥16 apart, i.e. crossing into the next line at index 16.Recall Quick self-check before you leave
False sharing is a ___ bug (not correctness), triggered when ___ cores write to different bytes of the ___ line, fixed by ___ each hot datum to its own line. ::: performance; two or more; same cache; padding/aligning.