False Sharing in Concurrent Programs
This chapter focuses on false sharing specifically in multithreaded code, with concrete fixes and detection.
Why This Matters
False sharing silently serializes parallel code: two threads update distinct variables, yet the cache-coherence traffic makes them slow. It is one of the most common reasons a multithreaded program fails to scale.
Prerequisites
c.conc.2— data races.c.perf.5— false-sharing basics.
Core Concept
Two threads writing different variables that share a cache line cause false sharing: each write invalidates the other core's copy of the line, forcing repeated cache-coherence traffic. The threads never touch the same byte, but the hardware treats the line as one unit.
struct {
int a; /* thread 1 */
int b; /* thread 2 */
} counters; /* a and b on the same cache line (usually 64 bytes) */
Examples
Detecting and fixing
#include <stdalign.h>
struct alignas(64) Counter {
int value;
};
static struct Counter c1;
static struct Counter c2; /* now on different cache lines */
_Alignas(64) (C11) forces each counter to its own cache line. On platforms where the cache line is not exactly 64, a larger power-of-two alignment (128) is a safe over-estimate.
Padding without _Alignas
struct Counter {
int value;
char pad[64 - sizeof(int)];
};
This is more error-prone; prefer _Alignas.
How It Works
The cache-coherence protocol (MESI and variants) keeps lines coherent. When a core writes a byte in a line, it must invalidate that line in other cores. Two writers to the same line ping-pong the line between caches, even for different bytes.
Variations
Per-thread accumulation
Compute per-thread partial results in thread-local storage, then combine once at the end, avoiding shared counters entirely.
Padding to cache-line size
_Alignas(64) or _Alignas(128) separates hot per-thread variables.
Common Mistakes
- Assuming separate variables are automatically cache-line separated.
- Over-padding every variable (wastes memory) when only hot shared ones matter.
- Using
volatileinstead of_Alignas(volatile does not fix false sharing).
Undefined Behavior
- None inherent; false sharing is a performance issue.
Portability
- Cache-line size is hardware-specific. Use
_Alignaswith a safe power-of-two
(64 or 128).
Under the Hood
perf c2c (Linux) analyzes cache-coherency traffic and identifies false sharing. Hardware performance counters reveal cache-line invalidations.
Practical Usage
- Align hot per-thread variables to their own cache lines.
- Use per-thread accumulators and combine at the end.
- Profile with
perf c2cbefore and after.
Exercises
1. Write a two-thread program that increments two adjacent counters, measure with perf c2c, then fix with _Alignas(64). 2. Compare scaling before and after the fix. 3. Implement per-thread accumulation and compare.
Deep Challenge
Explain why _Atomic counters do not prevent false sharing, and show how to fix a pair of atomic counters that are falsely shared. Then measure the difference with perf c2c.
Related Concepts
c.perf.5— false sharing.c.cpu.cache-line— cache lines.c.conc.5— atomics.
References
- Linux
perf c2c, Intel/ARM optimization guides.
Verification
- False-sharing mechanism and fixes.
VERIFIED - No example was executed during generation unless noted. `Execution not
verified.`
Progress
- [ ] Read
- [ ] Understand
- [ ] Complete examples
- [ ] Complete exercises
- [ ] Complete deep challenge
Concept checkboxes
- [ ] False sharing mechanism
- [ ] Cache-line alignment
- [ ] Per-thread accumulation
Mastery levels
| Concept | Current level (0–8) | Target level |
|---|---|---|
| c.conc.false-sharing | 0 | 6 |