Give the mechanism by which false sharing costs performance, then compute the throughput loss when two threads on different cores increment separate 8-byte counters that happen to sit 16 bytes apart, at an attempted 50 million increments per second each.

Give the mechanism by which false sharing costs performance, then compute the throughput loss when two threads on different cores increment separate 8-byte counters that happen to sit 16 bytes apart, at an attempted 50 million increments per second each.

Approach: Trace the cache line's state through the coherence protocol as each core writes, then price each transfer and compare with the attempted rate.

The two counters share one 64-byte cache line, so every increment transfers the line between cores at 40 to 100 ns, capping combined throughput near 10 to 25 million per second against the 100 million attempted, a loss of roughly 80%. Coherence works at cache line granularity, so although the counters are logically independent the hardware cannot tell. When core A writes, it must hold the line in the modified state, which invalidates core B's copy. Core B's next write misses, requests the line, and the protocol transfers it from A's cache, taking 40 ns on the same socket and over 100 ns across sockets. Each increment therefore costs a coherence round trip instead of the 1 ns an L1 hit would cost, and the two cores ping the line back and forth continuously. The fix is padding: align each counter to its own 64-byte line, or better to 128 bytes because the adjacent line prefetcher pulls pairs of lines on many Intel parts. Alternatives are to keep per-thread counters and sum them only when the value is read, which removes the sharing entirely, and to check the layout with a tool that reports coherence misses rather than assuming the compiler laid the structure out sensibly.

Follow-up: You pad the counters and throughput is still capped at 30 million per second. What is the next thing you look at?

Key concepts: false sharing, cache coherence, cache line, padding.