Throughput benchmarks
Ring throughput benchmarks
This page reports sustained throughput (M items / second) across every ring primitive in the substrate at the matrix of (locale, capacity, producer-count, consumer-count) cells the primitive is configured to accept. All numbers are direct measurements - no statistical model, no projection.
The full machine-generated table lives on the throughput results page (regenerated by re-running the sweep). This page summarises the headline numbers and links the rest.
Reproducing the numbers
The bench harness is a single binary (bench_throughput) that
dispatches per primitive; the matrix on the results page is the
cross product of the cells it lists, at n_items = 100000 with 3
runs per cell (median published).
cargo build --release --example bench_throughput -p subetha-cxc
# One cell per invocation; emits one line of key=value pairs.
./target/release/examples/bench_throughput <primitive> <locale> <capacity> <n_producers> <n_consumers> <n_items>What’s benched
| Primitive arg | Source | Shape | Locales |
|---|---|---|---|
spsc | SharedRingSpsc | 1P / 1C Lamport pair | anon |
mpsc | SharedRingMpsc | NP / 1C composed Lamport rings | anon |
mpsc-fifo | SharedRingMpscFifo | NP / 1C single Vyukov ring | anon |
mpmc | SharedRingMpmc | NP / NC composed Lamport grid | anon |
vyukov | SharedRing | NP / NC Vyukov MPMC, global FIFO | anon / file / shmfs |
broadcast | SharedBroadcastRing | 1P / NC fan-out | anon / file / shmfs |
pubsub | PubSubRing | 1P / NC absolute-position | anon / file / shmfs |
adaptive-{spsc,mpsc,mpmc,vyukov} | AdaptiveRing | shape morphs at runtime; UNPINNED dispatch (one Acquire + match + delegate per call) | anon / file / shmfs |
adaptive-pinned-{spsc,mpsc,mpmc,vyukov} | AdaptiveRing
via PinnedRing | PINNED hot path: native primitive speed, validity-checked at meaningful intervals | anon / file / shmfs |
locale-adaptive | LocaleAdaptiveRing | any shape across any locale; unpinned dispatch (locale tag load + delegate) | anon / file / shmfs |
capacity-{spsc,mpsc,mpmc,vyukov} | CapacityAdaptiveRing | UNPINNED dispatch through wrapper (one ArcSwap + stale-walk + AdaptiveRing dispatch + delegate per call) | anon / file / shmfs |
capacity-pinned-{spsc,mpsc,mpmc,vyukov} | CapacityAdaptiveRing
via PinnedCapacity + PinnedRing | PINNED hot path: skip wrapper AND skip AdaptiveRing dispatch; near-native | anon / file / shmfs |
capacity-broadcast / capacity-pinned-broadcast | CapacityBroadcastRing (un/pinned variants) | slot count morphs; pinned hot-loops on the inner SharedBroadcastRing | anon / file / shmfs |
capacity-pubsub | CapacityPubSubRing (publish path holds chain lock by design; subscribe path is lock-free) | slot count morphs | anon / file / shmfs |
crossbeam | crossbeam-channel::bounded (external baseline) | NP / NC | n/a |
std-mpsc | std::sync::mpsc::sync_channel (external baseline) | NP / 1C | n/a |
For each primitive the sweep runs every combination of:
- Capacity: 256, 1024, 4096, 16384
- Producers: 1, 4, 8
- Consumers: 1, 4, 8 (and 2 / 4 / 8 for fan-out)
- Locale: anon, file, shmfs (where the primitive supports it)
- Runs: 3 per cell by default (
-Runsraises it); min / median / mean / max reported, and median is the headline because run-to-run jitter at small workloads has long tails
Pinned vs unpinned: what the two numbers mean
The adaptive wrappers (AdaptiveRing, CapacityAdaptiveRing, LocaleAdaptiveRing) expose two API surfaces:
- Unpinned dispatch -
ring.try_send(...)/ring.try_recv(...)calls go through the wrapper’s per-call dispatch (read the shape tag / load the active state / fall through to the inner primitive). This is what theadaptive-*andcapacity-*table rows measure. - Pinned hot path -
ring.pin_current_shape()returns aPinnedRing(orPinnedCapacity->pin.ring()-> further pin) that exposes the inner primitive directly. Hot loopspin.spsc_try_pop(...)etc. without per-call dispatch. The pin is invalidated on morph; the caller checkspin.is_still_valid()periodically and re-acquires. This is what*-pinned-*rows measure.
The pinned path approximates the underlying native primitive (spsc, mpsc, mpmc, vyukov) numbers because the morph-aware dispatch is bypassed for the duration of the pin. Production code that has stable shape / capacity / locale takes the pin; production code that’s actively morphing pays the dispatch cost on every call.
What the numbers mean
- M items / s = total items pushed-and-popped divided by wall time.
- Each item is a 52- or 56-byte payload (matches the per-slot payload size of the primitive).
- Producers + consumers run on dedicated OS threads. The bench harness’s main thread participates only as the !Sync MPSC / MPSC-fifo consumer (those types’ Consumer is a per-thread token by design).
- “anon” = in-process anonymous mmap. “file” = mmap-backed file in
%TEMP%(cross-process via OS page cache). “shmfs” = named shared-memory region (cross-process, RAM-resident, never touches the page cache; on Linux this is/dev/shm, on Windows it’s named-section mapping).
What the numbers don’t mean
- Per-op latency (mean / stddev / p99) is NOT in these tables - it’s measured by the criterion benches under
crates/subetha-cxc/benches/(look forcapacity_adaptive_ring.rs,capacity_broadcast_ring.rs,capacity_pubsub_ring.rs, plus the pre-existingshared_ring.rsandshared_broadcast_ring.rs). Runcargo bench -p subetha-cxc --bench <name>to reproduce. - These numbers are SINGLE-machine. Cross-host throughput rides on the QUIC / TCP bridges (separately benched) and depends on network conditions.
- The “best per primitive” table picks the best-of-matrix cell. Real workloads won’t always hit those cells; the per-cell deep-dive tables are the honest picture.
Bench audit notes
The harness reports the effective producer/consumer count, not the requested one. For example: SPSC always runs 1P/1C regardless of CLI request (the underlying primitive enforces this at compile time via the !Sync Producer/Consumer token); broadcast / pubsub ignore the producer arg (1 producer is the only mode). This is why effP and effC columns matter - they answer “what did the bench actually do” rather than “what did I ask for”.
When a cell can not run (e.g. SPSC at locale=shmfs has no constructor, or 1P/8C requested where max_producers must be >= max_consumers), the harness emits a SKIP line with the reason. The “Skipped cells” section at the bottom of throughput_results.md
lists them.