shreyas@portfolio:~/projects/lockfree-queue
$ cat projects/lockfree-queue.md
# Lock-Free MPMC Queue | C++ / concurrency / CI-CD | 2080 LOC
# repo: github.com/shreyasganesh0/lockfree-queue
# tags: systems, concurrency, memory-ordering
demo: MPMC ring buffer — 8 slots · producers CAS the tail · consumers CAS the head

Each slot's number is its sequence. Producers can only write when seq == tail; on success they bump seq → tail+1. Consumers read when seq == head+1. No mutex; coordination is atomic CAS on head/tail + acquire/release on the slot seq.

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 when seq == tail, consumers CAS the head when seq == 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.”


← all projects · view on github →

perf: ·