benchmark_rust_and_go_currency

Tokio vs Crossbeam vs Go: concurrency benchmark

Same workloads, parameters, channel capacities and checksums in every implementation:

Threads and tasks

Results

AMD Ryzen 5 5625U (6 cores / 12 threads), performance governor, AC power, Rust 1.98.0, Go 1.27.0. Median of 10 runs.

Every thread count, confidence intervals and CPU time: summary.md, summary.csv. Flame graphs of every test at 12 threads, click to zoom: rishikesh01.github.io/…/flame-20260919-185544.

MPMC channels

async-channel (Tokio and tuned Tokio, Tokio channels; Tokio has none), kanal (tuned Tokio), crossbeam-channel, Go chan.

Test Tokio/Go tasks Crossbeam threads Tokio Tuned Tokio, Tokio channels Tuned Tokio Crossbeam Go Unit
4 senders → 4 receivers, capacity 1024 8 8 9.32M (async-channel) 9.21M (async-channel) 81.0M 23.3M 18.7M messages/s
4 senders → 4 receivers, capacity 1 8 8 2.03M (async-channel) 1.93M (async-channel) 19.4M 5.17M 6.87M messages/s

Single-receiver channels

Tokio mpsc; tuned Tokio kanal (mpsc for select); tuned Tokio, Tokio channels mpsc; Crossbeam and Go use their MPMC channels.

Test Tokio/Go tasks Crossbeam threads Tokio Tuned Tokio, Tokio channels Tuned Tokio Crossbeam Go Unit
1 sender → 1 receiver, capacity 1024 2 2 13.2M 23.8M 137M 38.3M 29.6M messages/s
1 sender → 1 receiver, capacity 1 2 2 5.13M 5.12M 20.5M 6.76M 11.3M messages/s
4 senders → 1 receiver, capacity 1024 5 5 8.81M 7.07M 104M 35.4M 25.9M messages/s
4 senders → 1 receiver, capacity 1 5 5 5.00M 4.92M 20.0M 5.74M 10.1M messages/s
Ping-pong 2 2 4.46M 4.35M 7.51M 3.86M 4.03M round trips/s
Ping-pong latency p50 2 2 140 ns 141 ns 80 ns 280 ns 241 ns lower is better
Ping-pong latency p99 2 2 2.17 µs 2.15 µs 2.13 µs 301 ns 391 ns lower is better
Select over 2 channels, capacity 1024 3 3 20.6M 40.6M 40.6M 13.8M 13.9M messages/s
Select over 2 channels, capacity 1 3 3 5.96M 5.34M 5.37M 7.37M 7.30M messages/s

Tasks, CPU, locks and memory

Test Tokio/Go tasks Crossbeam threads Tokio Tuned Tokio, Tokio channels Tuned Tokio Crossbeam Go Unit
Spawn and join, 10K tasks 10K 10K 3.87M 5.83M 5.95M 29.2K 5.95M tasks/s
Spawn and join, 1M tasks 1M — 4.05M 5.44M 5.41M — 6.48M tasks/s
CPU-heavy hashing 256 12 116M 116M 115M 114M 108M items/s
Lock contention, 8 workers 8 8 9.78M 81.9M 80.0M 31.9M 25.6M increments/s
Memory per idle task, 10K tasks 10K 10K 411 B 245 B 244 B 9.92 KiB 2.78 KiB lower is better
Memory per idle task, 1M tasks 1M — 385 B 257 B 258 B — 2.69 KiB lower is better

— : not run (Crossbeam is capped at 20,000 threads).

Why

Implementations

Workload Tokio Tuned Tokio Crossbeam Go
spsc, mpsc, mpmc mpsc / async-channel ¹ kanal + drain up to 256 per wake-up channel::bounded chan
pingpong mpsc(1) kanal bounded(1) bounded(1) chan cap 1
spawn tokio::spawn + JoinHandle tokio::spawn, results in a shared slice scoped OS threads goroutines + WaitGroup
cpu tasks on the runtime same work-stealing deque pool goroutines
select tokio::select! select! + recv_many(256) select! select
mutex tokio::sync::Mutex parking_lot::Mutex std::sync::Mutex ² sync.Mutex
idle task waiting on a Semaphore same blocked OS thread blocked goroutine
allocator system mimalloc system Go runtime
  1. Tokio has no MPMC channel (broadcast sends every message to every receiver).
  2. Crossbeam has no mutex.

Capacity: 1024 for spsc, mpsc, mpmc, select; 1 for the -cap1 cases and pingpong (Tokio can’t do 0).

rust/tokio-tuned-bench builds two binaries from one library: tokio-tuned-bench (kanal) and tokio-tuned-tokio-channels-bench (Tokio’s mpsc, async-channel for mpmc). Every other workload is the same code.

rust/tokio-variants-bench turns on single Tokio tweaks (--impls tokio,tokio+kanal,tokio+kanal+batch). Its generic wrappers make it slower than tokio-tuned.

Method

Compared with established practice

Sources: crossbeam-channel benchmarks, kanal suite, Tokio benches, Go chan_test.go, benchstat, Savina, Georges et al. 2007, pyperf, wrk2.

Practice Here
Fresh process per measurement, shuffled order yes
Thread-count sweep, CPU pinning yes
Received values and parameters checked yes
Warm-up inside the measured process spawn only
Median with confidence interval yes
CPU time and peak memory alongside throughput yes
Capacities 0 / 1 / N / unbounded 1 and 1024 only
Several payload sizes no, u64 only
Work per message or outside the lock no, empty critical sections
20 runs per cell (benchstat) no, 10
performance governor, turbo/SMT off, isolated CPUs governor only
Open-loop latency histogram (wrk2) no, ping-pong is closed-loop
Scheduler cases: chained spawn, yield, remote spawn no
Concurrency testing (loom, shuttle, go test -race) no

Caveats

Running

Requires Linux, Rust 1.98+, Go 1.27+, Python 3.10+, taskset and lscpu; numpy is optional.

echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
python3 runner/run.py --profile full --impls tokio,crossbeam,go,tokio-tuned-tokio-channels,tokio-tuned  # about 30 minutes
python3 runner/run.py                                                                                   # smoke test, about 1 minute
python3 runner/report.py results/<run>                                                                  # rebuild the reports
python3 runner/flamegraph.py --results results/<run>                                                    # flame graphs, about 2 minutes

Flags: --workloads, --threads, --reps, --warmup, --seed, --no-build, --out.

Each run writes results/<profile>-<timestamp>/: raw.jsonl, summary.csv, summary.md, report.html.

flamegraph.py writes results/flame-<timestamp>/: one SVG per test and an index.md table of the run’s medians, each linked to its graph. It needs perf, cargo install inferno rustfilt and sudo sysctl kernel.perf_event_paranoid=1 kernel.kptr_restrict=0. Rust is rebuilt with frame pointers because DWARF unwinding lost about half the stacks. In spot checks that build ran within 10% of the benchmark build, except Crossbeam 1 → 1 (30% slower) and 4 → 4 (15% faster).

rust/common/                shared CLI, JSON output, checksums
rust/tokio-bench/           Tokio
rust/tokio-tuned-bench/     tuned Tokio, and tuned Tokio with Tokio channels
rust/tokio-variants-bench/  single-tweak Tokio builds
rust/crossbeam-bench/       Crossbeam
go/                         Go
runner/                     run.py, report.py, flamegraph.py