A from-scratch implementation of a multi-producer multi-consumer lock-free queue in C++, plus the experiments that explain why the implementation works.
What I built
- A bounded ring buffer with per-slot sequence numbers, in the style of Dmitry Vyukov’s
design. Each slot holds
(seq, data); producers CAS the tail whenseq == tail, consumers CAS the head whenseq == head + 1. ABA-resistant by construction. - A baseline mutex-based queue and a multi-thread harness for direct comparison.
- A false-sharing experiment harness that measures L1 miss rate with and without cache-line padding on a Ryzen 9 7940HS.
- A memory-reordering stress test using Dekker’s algorithm to detect Store-Load reordering on x86 TSO under cache thrashing and concurrent I/O loads.
- GitHub Actions CI that runs the perf benchmarks on every PR and gates merges on a
5% regression threshold. The CI script is in
experiments/false_sharing/scripts/check_performance_regression.sh.
What the experiments showed
The interesting result was about CCX (CPU complex)-local cache sharing: under the right workload patterns, 1-byte padding within a CCX gives the same speedup as the textbook 64-byte cache-line padding, because the shared cache hierarchy inside a CCX absorbs the contention before it crosses to a different complex. That recovers ~63 bytes per slot of memory overhead in the common case.
(Read the per-experiment numbers in docs/experiments/cache_line_experiments.md in
the repo — they’re calibrated for the specific platform and shouldn’t be quoted
generically.)
Why this matters
This is the kind of work storage engines (CockroachDB, TigerBeetle, Materialize) and
proxy frameworks (pingora) depend on. The thing I wanted to be able to defend in
interview wasn’t “I used std::atomic” — it was “I know why the memory_order_release
on the sequence-number store pairs with the acquire on the consumer side, and what
happens if you weaken either.”