false sharing and cache-line padding
Two coworkers each have their own notebook, but the rule is that notebooks can only be carried around the building in whole filing-cabinet drawers, and by bad luck both notebooks live in the SAME drawer. Now every time one person wants to write in their notebook, they must fetch the whole drawer — yanking it away from the other person, who then has to fetch it back to write in theirs. They never touch each other's notebook, yet they constantly fight over the drawer. That is false sharing: two cores updating different variables that happen to sit in the same cache line, and so ping-ponging that line between their caches.
Here is the mechanism. The cache coherence protocol works at the granularity of a whole cache line (commonly 64 bytes), not individual variables. When core A writes to any byte in a line, the protocol must invalidate that line in every other core's cache so they do not read stale data; for core B to then write its own (different) variable in the same line, it must re-fetch the line, which invalidates A's copy, and so on. Even though A and B never share a logical variable, the hardware shares the line, and the line bounces back and forth — each bounce costing the latency of a cross-core transfer. The fix is cache-line padding (and alignment): place each hotly-and-independently-written variable on its OWN cache line, by aligning it to the line size and padding the surrounding structure so no two such variables share a line. In C you can request this with alignment (for example, declaring a per-thread counter as alignas(64) or padding a struct to 64 bytes); the cost is a little wasted memory in exchange for removing the ping-pong.
It matters because false sharing is invisible in the source — the code looks perfectly parallel, the variables are genuinely independent — yet it can silently turn a multi-threaded speedup into a slowdown, where adding cores makes the program SLOWER as contention on the shared line grows. A typical signature is a parallel loop or an array of per-thread counters that scales badly for no obvious reason. The honest caveat: padding everything wastes memory and cache, so apply it only to the few variables you have measured to be hotly written by different cores — the PMU's cache-coherence or HITM events, or simply observing that per-thread data laid out contiguously scales worse than the same data spaced apart, are how you confirm it.
// FALSE SHARING: both counters share one 64-byte line -> cores fight over it struct { long a; long b; } c; // a and b are 8 bytes apart // FIX: give each its own line struct { alignas(64) long a; alignas(64) long b; } c; // now 64 bytes apart
alignas(64) pushes each independently-written counter onto its own cache line, ending the cross-core ping-pong.
False sharing is a pure performance bug, not a correctness bug: the values are always correct, only slow. And the fix wastes memory, so pad only the few variables measurement shows are hot — blanket padding hurts more than it helps.