HFT-52 · Weeks 1 and 5 · Lock-free queues

SPSC ring buffer

A fixed array, two ever-growing indices and a mask. The producer core owns head, the consumer core owns tail, and every time one side reads the other's index a cache line crosses the interconnect. Run it, then switch on cached indices and batching and watch the transfers per item fall.

Producer core → 16-slot ring → consumer core
Producer (head) Consumer (tail) Item in ring Line crossed cores this step Outer band: slot lines, state producer/consumer
Cache lines (64 B) and their MESI state in each core
LineHoldsProducerConsumer
Last step
Cross-core transfers per item: same arrivals, 8,000 items, each design
DesignIndex lines / itemSlot lines / itemvs week 1

Indices never wrap. head and tail count up forever; the slot is index & (CAP − 1), which is what index % CAP compiles to when CAP is a power of two. Empty is head == tail, full is head − tail == CAP, so no slot is wasted. Model: two cores with MESI-style states; head, tail and the slots sit on separate 64-byte lines (16-byte slots, four per line). A read of a line the other core modified, or a write to a line the other core holds, is one cross-core transfer, tens of nanoseconds each (week 4). Arrivals are a seeded Poisson stream at the producer rate; the consumer spends an exponential service time per item at its rate; one tick is 10 ns. Cached indices reload the remote index only when the local copy says full (producer) or empty (consumer). Batching publishes once per batch of whatever is already available, never waiting for a batch to fill. Cached indices pay off when the ring is neither nearly full nor nearly empty; batching pays off when items arrive faster than one at a time.