Novellas with groff
False Sharing
Two threads touching different variables on the same cache line fight over it.
The mechanism. Caches are coherent at cache line granularity, typically 64 bytes (128 on Apple silicon's performance cores). If thread A writes counter[0] and thread B writes counter[1], and both live on one line, every write invalidates the other core's copy. The data is logically independent; the hardware does not know that. You get the full cost of cache coherence traffic with none of the sharing.
How it shows up. A parallel loop that gets slower with more threads. Per-thread counters in an array. A struct where a hot atomic sits next to a frequently-read field. Lock-free queues where head and tail indices share a line — producer and consumer then ping-pong the line on every operation.
The fix. Pad or align to a cache line: #[repr(align(64))] in Rust, alignas(std::hardware_destructive_interference_size) in C++, @Contended in the JVM (with -XX:-RestrictContended), or just put each counter in its own 64-byte struct. Or avoid the sharing entirely: accumulate thread-locally and combine once at the end, which is usually both simpler and faster.
Finding it. perf c2c on Linux is purpose-built for this. Otherwise look for high MEM_LOAD_L3_HIT_RETIRED.XSNP_HITM or simply test the padding hypothesis — it takes five minutes and the speedup is unmistakable when you are right.
Takeaway. On modern hardware, memory layout is a concurrency concern. 64 bytes is a number worth remembering.