Cache-Line False Sharing Moves Coherence Ownership Between CPUs

Two threads can update different variables without sharing a lock or touching the same bytes and still interfere at the hardware level. If those variables occupy the same cache line, a coherent multiprocessor treats their storage as one coherence unit. Repeated writes from different CPUs can therefore move ownership of that line between caches even though the program considers the variables independent.

This effect is false sharing. It is a property of the memory layout, access pattern, and coherence granularity together; proximity in a structure alone does not establish a performance problem.

Coherence operates below the language-level object boundary

Source code gives each field its own identity, but cache coherence generally tracks fixed-size lines rather than C fields, Rust values, counters, or array elements. A write requires the issuing CPU to obtain a coherence state that permits modification of the line. Copies held by other CPUs must transition as required by the coherence protocol.

Consider two counters placed in one line:

cache line
+----------------------+----------------------+
| counter_a            | counter_b            |
| written by CPU 0     | written by CPU 1     |
+----------------------+----------------------+

The CPUs are not contending for the same counter. They are contending for permission to modify the same coherence unit. A write to counter_a can affect the cache state containing counter_b, and the next write to counter_b can require another ownership transition.

The exact protocol states and transfer path are architecture and implementation dependent. MESI-family names are useful models, but software should not infer a specific physical transfer sequence from those labels alone.

A shared line is not automatically harmful

Read-only sharing is a normal and useful cache behavior. Multiple CPUs can hold copies of a line containing immutable or mostly read-only data without the repeated ownership changes associated with competing writes.

False sharing becomes relevant when concurrent access includes writes and the affected line is active enough for coherence traffic to matter. Common patterns include separate per-thread counters packed into an array, a frequently modified field beside data read by other CPUs, or unrelated global variables placed on the same line.

A layout that is harmless for one workload can become expensive under another. Thread placement, write frequency, CPU topology, object alignment, and the implementation’s cache hierarchy all influence the observed cost. There is no portable latency penalty that can be assigned to a false-sharing event.

Padding changes both coherence and memory costs

Separating frequently written fields onto different cache lines can remove the accidental coherence relationship:

line 0
+----------------------+
| counter_a            |
+----------------------+

line 1
+----------------------+
| counter_b            |
+----------------------+

That layout is not free. Padding enlarges objects, consumes more cache capacity, can increase memory footprint, and may put pressure on TLB coverage when applied across large collections. It can also move other fields into new combinations that create a different sharing pattern.

For that reason, cache-line alignment is a targeted layout decision rather than a universal rule for concurrent data. Fields written together by the same CPU may benefit from remaining close, while independently hot writers are stronger candidates for separation.

Per-CPU state reduces ownership traffic by changing the data model

Another approach is to stop updating one shared location on every operation. Per-CPU counters and batched aggregation give each CPU a local update path, then combine values at a less frequent boundary.

This changes more than layout. A globally updated counter can offer one set of visibility semantics, while per-CPU aggregation can expose a value assembled from several local states. The acceptable design depends on the consistency contract required by the caller.

Linux uses per-CPU data extensively in paths where reducing shared writes is worth the additional aggregation semantics. The technique addresses coherence pressure by reducing cross-CPU writes rather than merely placing the same write pattern on wider spacing.

Measurement has to identify the contested line

A CPU utilization increase or a slower multithreaded benchmark does not by itself identify false sharing. Lock contention, memory bandwidth, scheduler movement, NUMA placement, branch behavior, and other cache effects can produce similar symptoms.

On Linux, perf c2c can report cache-to-cache activity and associate hot lines with access locations on supported hardware. Structure-layout tools such as pahole can then connect an offset to fields occupying the same line. Hardware event availability and interpretation vary by processor, so the reported events remain platform-specific evidence rather than a universal measurement interface.

The useful unit of analysis is the cache line plus the code paths accessing it. Once the writers and readers are identified, a layout change can be measured against the same workload. A reduction in coherence events with a corresponding workload improvement provides stronger evidence than alignment changes made from source inspection alone.

Layout is part of concurrency performance

False sharing sits at a boundary between software independence and hardware granularity. Separate variables can be logically unrelated while remaining physically coupled by the coherence unit that contains them.

The practical constraint is specific: frequently accessed data with cross-CPU writes deserves line-level inspection when scaling degrades. Separation, per-CPU state, or reduced write frequency can each change the coherence pattern, but each also changes memory use, layout, or visibility semantics. The appropriate fix follows from the measured access pattern rather than from field adjacency by itself.