Skip to content

feat(bench): add rigtorp SPSCQueue C++ contender via FFI - #11

Merged
Milerius merged 6 commits into
mainfrom
feat/cpp-contenders
Mar 27, 2026
Merged

Milerius merged 6 commits into
mainfrom
feat/cpp-contenders

Conversation

@Milerius

Copy link
Copy Markdown
Owner

Summary

  • Vendor rigtorp/SPSCQueue header (MIT, header-only) into crates/bench/cpp/
  • Thin C wrapper (rigtorp_ffi.cpp) compiled via cc crate at -O3 -std=c++17
  • Safe Rust FFI wrappers (RigtorpU64, RigtorpMsg48, RigtorpMsg64)
  • New bench-contenders-cpp feature flag — enabled by --all-features in CI
  • Full coherent benchmark matrix: single, burst_100, burst_1000 × u64, msg48, msg64
  • Update bench-report.sh to recognize rigtorp-cpp implementation

Local Results (M4 Pro, -C target-cpu=native)

Single Push+Pop (ns/op)

Element mantis/copy mantis/inline rigtorp-cpp rtrb crossbeam
u64 2.26 2.30 3.09 2.66 4.00
msg48 3.61 5.74 4.59 5.03 5.47
msg64 4.63 8.14 6.35 6.92 7.02

Burst 1000 (ns/op)

Element mantis/copy mantis/inline rigtorp-cpp rtrb crossbeam
u64 4094 4219 5178 3604 6106
msg48 4579 5898 6369 6288 8010
msg64 4657 7772 21977 22166 9720

Test plan

  • cargo +nightly clippy --bench spsc --all-features -- -D warnings passes
  • cargo fmt --all --check passes
  • All 9 rigtorp benchmarks produce valid results
  • bench-report.sh normalize filter handles spsc/rigtorp/ prefix

🤖 Generated with Claude Code

Vendor rigtorp/SPSCQueue header (MIT licensed) and expose it through
a thin C wrapper compiled by the cc crate. New bench-contenders-cpp
feature flag enables rigtorp benchmarks in the same coherent matrix
(single, burst_100, burst_1000 × u64, msg48, msg64).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Mar 27, 2026 •

Copy link
Copy Markdown

Benchmark Report

Commit: 2a0599d86e794342d1791f515a817400aec2f5be

Linux

CPU: AMD EPYC 9V74 80-Core Processor | Arch: x86_64 | Compiler: rustc 1.96.0-nightly (23903d01c 2026-03-26)

Latency (ns/op, lower is better)

Single Push+Pop

Element crossbeam mantis/copy mantis/general mantis/inline rigtorp-cpp rtrb
msg48 24.66 10.83 🏆 33.93 34.13 12.31 24.43
msg64 8.67 3.93 🏆 16.03 16.08 13.75 6.9
u64 5.81 2.83 🏆 - 4.24 4.59 3.72

Burst 100

Element crossbeam mantis/copy mantis/inline rigtorp-cpp rtrb
msg48 3310.59 230.13 🏆 2615.6 2052.68 2687.54
msg64 1201.42 188.19 🏆 1355.48 1671.71 783.67
u64 894.81 178.63 🏆 406.92 461.21 334.64

Burst 1000

Element crossbeam mantis/copy mantis/inline rigtorp-cpp rtrb
msg48 33286.79 2472.7 🏆 26496.66 20626.79 26854.76
msg64 12764.84 2496.72 🏆 13603.93 16939.88 8014.6
u64 8905.1 1780.67 🏆 4228.36 4605.4 3376.5

Batch 100

Element mantis/copy
msg48 75.61 🏆
u64 15.24 🏆

Batch 1000

Element mantis/copy
msg48 1217.38 🏆
u64 108.9 🏆

Full Drain

Element mantis/inline
u64 2204.16 🏆
Instructions per Op (lower is better)
Full results (all fields)
Workload ns/op p50 p99 cycles insns bmiss l1d llc
spsc/inline/single_item/u64 4.24 4.2 4.4 15.6 - - - -
spsc/inline/single_item/msg48 34.13 33.8 36 127.5 - - - -
spsc/inline/single_item/msg64 16.08 16 16.7 57.4 - - - -
spsc/inline/burst_100/u64 406.92 404.9 427.8 1343.8 - - - -
spsc/inline/burst_100/msg48 2615.6 2612.4 2645.3 11692.3 - - - -
spsc/inline/burst_100/msg64 1355.48 1335.1 1453.1 6163 - - - -
spsc/inline/burst_1000/u64 4228.36 4221.9 4332.3 16724.7 - - - -
spsc/inline/burst_1000/msg48 26496.66 26122.6 30261.9 93387.4 - - - -
spsc/inline/burst_1000/msg64 13603.93 13559.6 14171.4 48924.3 - - - -
spsc/inline/full_drain/u64 2204.16 2196.1 2287.7 8954.3 - - - -
copy/single/u64 2.83 2.8 2.9 8.1 - - - -
copy/single/msg48 10.83 10.8 11 47 - - - -
copy/single/msg64 3.93 3.9 4 13.6 - - - -
general/single/msg48 33.93 33.9 34.7 127.2 - - - -
general/single/msg64 16.03 16 16.2 57.5 - - - -
copy/burst/100/u64 178.63 177.6 188.4 811.8 - - - -
copy/burst/100/msg48 230.13 229.1 238.6 803.5 - - - -
copy/burst/100/msg64 188.19 187.6 193.7 589.5 - - - -
copy/burst/1000/u64 1780.67 1776.5 1837.6 6332.9 - - - -
copy/burst/1000/msg48 2472.7 2470.7 2500.3 10688.6 - - - -
copy/burst/1000/msg64 2496.72 2493.1 2559.7 10934.7 - - - -
copy/batch/100/u64 15.24 15.1 16.4 42.9 - - - -
copy/batch/100/msg48 75.61 75.5 76.9 307.6 - - - -
copy/batch/1000/u64 108.9 108.8 110.6 367.4 - - - -
copy/batch/1000/msg48 1217.38 1216.9 1223.3 5145.1 - - - -
spsc/rtrb/single_item/u64 3.72 3.7 4.1 12.3 - - - -
spsc/rtrb/single_item/msg48 24.43 24.4 24.6 75.3 - - - -
spsc/rtrb/single_item/msg64 6.9 7.1 7.6 23.2 - - - -
spsc/rtrb/burst_100/u64 334.64 332.6 352.2 1466.4 - - - -
spsc/rtrb/burst_100/msg48 2687.54 2744 2928.4 12560.1 - - - -
spsc/rtrb/burst_100/msg64 783.67 838.9 957 2800.3 - - - -
spsc/rtrb/burst_1000/u64 3376.5 3371.6 3434.7 11817.6 - - - -
spsc/rtrb/burst_1000/msg48 26854.76 27411.7 29172.2 97767.8 - - - -
spsc/rtrb/burst_1000/msg64 8014.6 7508 9468.8 28786.6 - - - -
spsc/crossbeam/single_item/u64 5.81 5.8 5.9 17.1 - - - -
spsc/crossbeam/single_item/msg48 24.66 24.6 24.8 76.5 - - - -
spsc/crossbeam/single_item/msg64 8.67 8.6 9.5 32.2 - - - -
spsc/crossbeam/burst_100/u64 894.81 893.6 915.8 3154.9 - - - -
spsc/crossbeam/burst_100/msg48 3310.59 3309 3348.5 11484.5 - - - -
spsc/crossbeam/burst_100/msg64 1201.42 1192.1 1281.7 4970.8 - - - -
spsc/crossbeam/burst_1000/u64 8905.1 8901.7 8940.5 36628.2 - - - -
spsc/crossbeam/burst_1000/msg48 33286.79 33276.1 33476.7 134449.6 - - - -
spsc/crossbeam/burst_1000/msg64 12764.84 12759.7 12838.1 44388.9 - - - -
spsc/rigtorp/single_item/u64 4.59 4.6 4.7 17.6 - - - -
spsc/rigtorp/single_item/msg48 12.31 12.2 13.7 35.6 - - - -
spsc/rigtorp/single_item/msg64 13.75 13.7 14.9 47.5 - - - -
spsc/rigtorp/burst_100/u64 461.21 460.7 466.2 1637.3 - - - -
spsc/rigtorp/burst_100/msg48 2052.68 2018.3 2278.6 7367.8 - - - -
spsc/rigtorp/burst_100/msg64 1671.71 1739.9 1918.9 6053.6 - - - -
spsc/rigtorp/burst_1000/u64 4605.4 4597.4 4695.1 19154.8 - - - -
spsc/rigtorp/burst_1000/msg48 20626.79 20329 22879.5 87049.3 - - - -
spsc/rigtorp/burst_1000/msg64 16939.88 16031.6 19485.4 63495.7 - - - -
macOS

CPU: Apple M1 (Virtual) | Arch: aarch64 | Compiler: rustc 1.96.0-nightly (23903d01c 2026-03-26)

Latency (ns/op, lower is better)

Single Push+Pop

Element crossbeam mantis/copy mantis/general mantis/inline rigtorp-cpp rtrb
msg48 10.34 9.37 11.24 11.09 9.97 8.47 🏆
msg64 10.73 9.49 12.04 11.64 9.86 9.37 🏆
u64 8.43 8.33 - 9.23 8.64 6.6 🏆

Burst 100

Element crossbeam mantis/copy mantis/inline rigtorp-cpp rtrb
msg48 1707.14 420.52 🏆 636.73 1826.78 695.93
msg64 1755.27 416.76 🏆 746.95 1774.19 744.36
u64 1674.34 441.19 312.53 🏆 1569.77 594.47

Burst 1000

Element crossbeam mantis/copy mantis/inline rigtorp-cpp rtrb
msg48 18096.52 4348.2 🏆 6403.09 19195.13 6999.76
msg64 18284.65 4795.66 🏆 7822.71 35088.22 20783.06
u64 17073.99 3341.14 2908.22 🏆 16248.06 6246.04

Batch 100

Element mantis/copy
msg48 88.85 🏆
u64 21.16 🏆

Batch 1000

Element mantis/copy
msg48 820.47 🏆
u64 121.21 🏆

Full Drain

Element mantis/inline
u64 4555.1 🏆
Instructions per Op (lower is better)
Full results (all fields)
Workload ns/op p50 p99 cycles insns bmiss l1d llc
spsc/inline/single_item/u64 9.23 8.8 12.5 0.3 - - - -
spsc/inline/single_item/msg48 11.09 10.3 16.5 0.4 - - - -
spsc/inline/single_item/msg64 11.64 11.1 16.1 0.3 - - - -
spsc/inline/burst_100/u64 312.53 280.2 565 11.1 - - - -
spsc/inline/burst_100/msg48 636.73 600.2 872.7 23 - - - -
spsc/inline/burst_100/msg64 746.95 699.1 1114.1 21.8 - - - -
spsc/inline/burst_1000/u64 2908.22 2690.4 4019.6 132.1 - - - -
spsc/inline/burst_1000/msg48 6403.09 6009.7 9417.7 193.2 - - - -
spsc/inline/burst_1000/msg64 7822.71 7256.8 11094.6 280.4 - - - -
spsc/inline/full_drain/u64 4555.1 4317.6 6093.4 167.7 - - - -
copy/single/u64 8.33 7.9 12.7 0.2 - - - -
copy/single/msg48 9.37 8.8 13.9 0.3 - - - -
copy/single/msg64 9.49 8.8 14.9 0.3 - - - -
general/single/msg48 11.24 10.3 18 0.3 - - - -
general/single/msg64 12.04 11.4 22.2 0.3 - - - -
copy/burst/100/u64 441.19 408 640.8 14.4 - - - -
copy/burst/100/msg48 420.52 404.9 626.2 14.6 - - - -
copy/burst/100/msg64 416.76 403.5 512.3 13.6 - - - -
copy/burst/1000/u64 3341.14 3322.2 3976.1 110.3 - - - -
copy/burst/1000/msg48 4348.2 4266.9 5243.2 165.1 - - - -
copy/burst/1000/msg64 4795.66 4803.2 5061.6 191.7 - - - -
copy/batch/100/u64 21.16 20.8 25.9 0.8 - - - -
copy/batch/100/msg48 88.85 88 105.4 2.5 - - - -
copy/batch/1000/u64 121.21 119.9 131.5 3.9 - - - -
copy/batch/1000/msg48 820.47 834.9 971.9 25.8 - - - -
spsc/rtrb/single_item/u64 6.6 6.5 7 0.2 - - - -
spsc/rtrb/single_item/msg48 8.47 8.4 9 0.3 - - - -
spsc/rtrb/single_item/msg64 9.37 9.2 11 0.3 - - - -
spsc/rtrb/burst_100/u64 594.47 584.7 675.7 22.3 - - - -
spsc/rtrb/burst_100/msg48 695.93 686.2 770.7 21 - - - -
spsc/rtrb/burst_100/msg64 744.36 739.3 783.5 22.1 - - - -
spsc/rtrb/burst_1000/u64 6246.04 6170.6 6587.8 203 - - - -
spsc/rtrb/burst_1000/msg48 6999.76 6960.1 7354.6 234.7 - - - -
spsc/rtrb/burst_1000/msg64 20783.06 20636 21700.9 880.6 - - - -
spsc/crossbeam/single_item/u64 8.43 8.3 9.3 0.3 - - - -
spsc/crossbeam/single_item/msg48 10.34 10.2 10.9 0.4 - - - -
spsc/crossbeam/single_item/msg64 10.73 10.7 11.1 0.4 - - - -
spsc/crossbeam/burst_100/u64 1674.34 1650.1 1813 51.7 - - - -
spsc/crossbeam/burst_100/msg48 1707.14 1691.3 1786.1 54.7 - - - -
spsc/crossbeam/burst_100/msg64 1755.27 1747.3 1811.6 58.1 - - - -
spsc/crossbeam/burst_1000/u64 17073.99 17058.1 20111.5 616.6 - - - -
spsc/crossbeam/burst_1000/msg48 18096.52 17576.9 20954.5 695.8 - - - -
spsc/crossbeam/burst_1000/msg64 18284.65 18037.4 19510.6 754.5 - - - -
spsc/rigtorp/single_item/u64 8.64 8.7 9.1 0.3 - - - -
spsc/rigtorp/single_item/msg48 9.97 9.9 10.4 0.4 - - - -
spsc/rigtorp/single_item/msg64 9.86 9.8 10.4 0.4 - - - -
spsc/rigtorp/burst_100/u64 1569.77 1563 1630.8 48.4 - - - -
spsc/rigtorp/burst_100/msg48 1826.78 1829.9 1894.5 59.7 - - - -
spsc/rigtorp/burst_100/msg64 1774.19 1766.2 1853.5 60.1 - - - -
spsc/rigtorp/burst_1000/u64 16248.06 16274.9 17296.6 577.3 - - - -
spsc/rigtorp/burst_1000/msg48 19195.13 18984.7 20288 739.8 - - - -
spsc/rigtorp/burst_1000/msg64 35088.22 34268.3 42788.2 1325.6 - - - -

Milerias and others added 5 commits March 27, 2026 09:07
Replace Relaxed atomic loads of own head/tail position with Cell-based
local caches, matching the rtrb optimization pattern. The producer and
consumer now read their own position from a Cell (register-friendly)
and only use atomic stores for cross-thread publishing.

Group producer-local fields (head_local + tail_remote) and consumer-local
fields (tail_local + head_remote) into dedicated cache structs that share
a single CachePadded line each — zero additional memory overhead.

Results on M4 Pro (before → after):
- copy/single/u64:       2.21ns → 1.80ns (-19%)
- copy/burst/100/msg48:  372ns  → 181ns  (-51%)
- copy/burst/1000/u64:   4044ns → 2790ns (-31%)
- inline/burst/100/u64:  383ns  → 209ns  (-45%)
- inline/burst/1000/u64: 4260ns → 2683ns (-37%)

Mantis now sweeps all 9 benchmark categories vs rtrb, crossbeam,
and rigtorp C++. The burst_1000/u64 result flipped from losing to
rtrb by 12% to beating it by 20%.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
On x86, Cell::set before atomic store creates a store-forwarding
dependency that hurts the next iteration's Cell::get. Moving it
after the atomic store takes it off the critical path.

Also update CLAUDE.md to document nightly-only toolchain.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
On ARM, Cell-based local caching avoids implicit ordering overhead
of atomic self-loads (66-79% improvement in burst workloads).
On x86 (TSO), Relaxed atomic load is already a plain mov instruction,
so the Cell adds store-forwarding overhead without benefit.

Use cfg(target_arch = "aarch64") to enable Cell caching only on ARM.
x86 falls back to Relaxed atomic self-load (zero overhead on TSO).
Remote index caching (tail_remote/head_remote) remains unconditional.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
These fields are only used on ARM where Cell-based local caching
avoids atomic self-load overhead. On x86 they were dead fields
causing clippy errors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Document why head_local/tail_local use cfg(target_arch = "aarch64"):
ARM Relaxed atomics carry implicit ordering overhead (store buffer
drain, coherence protocol), while x86 TSO makes them a plain mov.
Cell caching wins on ARM but adds store-forwarding cost on x86.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Milerius
Milerius merged commit cb73f2b into main Mar 27, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants