Why are eight threads writing eight different variables slow?¶
Level: 201 · anyone whose per-thread counters were slower than one shared one
Stub — a question and its expected answers, not yet a lesson. No program behind this page has been written or run, so nothing on it has been through the check that backs every finished page. The table is what the finished lesson is expected to find, and every cell is unverified until a program prints it. (Not machine-checked here.)
One line: Eight threads each writing their own element of one array are writing to the same 64-byte cache line, and the CPU moves that line between the cores on every write — false sharing — so that eight independent counters run slower than a single locked one; padding each counter to its own line, or keeping it in a local and writing it once, gives the expected speedup.
The question¶
counts[i] += 1 a hundred million times per thread, thread i owning counts[i]. No data race — each thread has its own slot. Eight threads take longer than one. Pad each slot to 64 bytes and eight threads take an eighth of the time. The page measures both as Real runs and shows the padding in each language, plus the version that needs none: a local accumulator written back once.
What each language is expected to say¶
| Expected answer, to be verified by the program | |
|---|---|
| Rust | #[repr(align(64))] on the slot type; crossbeam::CachePadded names the fix (link) |
| Go | a struct with _ [56]byte padding, as the runtime's own source does |
| C | _Alignas(64) on the slot |
| C++ | alignas(std::hardware_destructive_interference_size) |
| Java | @Contended with -XX:-RestrictContended; or the local-accumulator fix |
| Python | not reachable under the GIL; the local-accumulator shape is the only lesson |
What the programs have to show¶
- Real runs: eight threads on adjacent slots against padded slots, time per language
- the local accumulator written back once, which is Splitting a sum's private partial sum
- the CI example: the counts, identical either way