Cache line false sharing, measured
Two threads writing to two different variables should not contend. If those variables share a 64-byte cache line, they do.
What it looks like
Throughput that gets worse as you add cores. Not flat — worse. That shape is nearly diagnostic on its own; almost nothing else degrades with added parallelism the way false sharing does.
perf stat -e cache-misses,cache-references \
-e LLC-load-misses ./bench
Confirming it
perf c2c exists for exactly this and is worth learning:
perf c2c record ./bench
perf c2c report --stdio
It reports HITM events — loads that hit a line modified in another core's cache. That is the actual signature, not a general rise in cache misses.
The fix is boring
Pad the structure so contended fields land on separate lines.
struct counter {
_Atomic long value;
char pad[64 - sizeof(_Atomic long)];
} __attribute__((aligned(64)));
Measure first. Padding everything wastes cache and will make some workloads slower, and the version of this bug people imagine is far more common than the version that is really there.