False Sharing: The Hidden Performance Killer
As CPU manufacturing has advanced, high-end CPUs now pack dozens of cores and over a hundred threads, backed by L1, L2, and L3 caches. With these caches, CPU cores can process data faster and access main memory less often, pushing compute performance even higher.
CPU Cache Hierarchy and Access Latency
The CPU cache hierarchy, and how it relates to memory and disk, looks like this:

As is well known, a single memory access is expensive for the CPU, and fetching data from disk means waiting far longer still. Techniques like mmap help mitigate this, but overall the CPU's compute performance is the ceiling of the whole machine — every other component lags behind in data transfer speed. Here are the latencies for the CPU accessing each tier of storage:
| Storage | Medium | Cost (USD) | Random access latency |
|---|---|---|---|
| L1 Cache | SRAM | 7 | 1ns |
| L2 Cache | SRAM | 7 | 4ns |
| Memory | DRAM | 0.015 | 100ns |
| Disk | SSD (NAND) | 0.0004 | 150us |
| Disk | HDD | 0.00004 | 10ms |
The pattern is clear: the larger and cheaper a storage device is, the more data it holds but the slower it is to access; the faster the device, the more it costs. The CPU accesses L1 Cache 100 times faster than main memory — which is exactly why CPUs have L1 through L3 caches: they sit between the CPU and memory as a caching layer, reducing how often memory needs to be touched.
Cache Lines
When the CPU reads data from memory into cache, it doesn't fetch it byte by byte — it fetches it in blocks. These blocks are called cache lines. In other words, the cache line is the unit in which the CPU reads data from memory into cache.
On Linux, you can check the cache line size with:
# Check the L1 cache line size of cpu0, in bytes
$ cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size
Mainstream CPUs typically use 64-byte cache lines, while Apple's M1 chip already uses 128-byte cache lines.
What Is False Sharing
Now that we know data is copied from memory into the CPU cache in cache-line units, let's look at what false sharing is and how to avoid it.
Imagine a dual-core CPU where the two cores run two different threads in parallel, each reading a different piece of data from memory: two long variables, A and B (8 bytes each). CPU1 wants to fetch A and modify it, CPU2 wants to fetch B and modify it — but the two variables sit at contiguous addresses in physical memory (DRAM). If the cache line is 64 bytes and variable A sits at the start of a cache line, then B immediately follows A, and both variables land in the same cache line. Since the cache line is the unit of transfer from memory to cache, both variables get pulled into each core's private cache together.
Now consider: what happens when the two threads on different cores each modify their own variable — say CPU1's thread only changes A, and CPU2's thread only changes B?
At that point, the copies of the cache line in the two cores' caches diverge, which could easily corrupt the final result. To keep both copies consistent, the MESI protocol for multi-core cache coherence was born.
To comply with MESI, a core that modifies data must notify the other cores: the cache line you hold is stale, go fetch it from memory again. That notification and reload is where the performance cost comes from.
When multiple CPU cores concurrently modify unrelated variables that happen to live in the same cache line, you get false sharing. Each core is working on different data, yet they drag each other down because they share a cache line: one core updates data in the line, and every other core has to reload the entire line — a process that takes considerable time.
How to Avoid False Sharing
The most common technique is cache line padding. The idea is to insert unused filler data between variables, isolating hot variables into separate cache lines so that data accessed by different CPU cores no longer lands in the same cache line — eliminating false sharing.
There are also programming-level tricks, such as deliberately laying out frequently modified data across different cache lines, or using Thread Local Storage (TLS) to avoid data contention between threads. At the hardware level, Intel offers Cache Allocation Technology (CAT), which lets programmers explicitly control how cache is allocated; there's also cache partitioning, which divides the cache into regions assigned to different threads — likewise sidestepping cross-thread contention and false sharing.
Beyond false sharing, other cache-related performance issues deserve attention. Take cache misses: when the CPU needs data that isn't in cache, it must fetch it from memory, which takes far longer. Cache misses can be reduced through prefetching and well-chosen cache replacement algorithms.
Wrapping Up
Caching is a cornerstone of performance in computer architecture, but the cache line as a unit of transfer brings hidden traps like false sharing along with it. When multiple cores concurrently modify different variables in the same cache line, the coherence overhead of the MESI protocol drags performance down noticeably. The fix is equally direct: use padding or careful data layout to give each hot variable its own cache line. When writing highly concurrent code, paying attention to how your data is laid out in memory often saves you from this kind of invisible performance killer.
COMMENTS