bench: split ping_pong_oneshot — spawn_pair_control + ping_pong_steady; refresh baseline (job e982b5b5)

general.rs sections 5/6: spawn_pair_control is ping_pong_oneshot with the
messages removed (2 spawn + 2 join per round); ping_pong_steady is one
persistent pair × 10k roundtrips over unbounded MPSC (smarm::channel vs
tokio::sync::mpsc::unbounded_channel). Box (5900X, 3729fff, rq-mpmc):
oneshot 804/control 633 → 79% spawn+join; steady 142 ns vs tokio 128
(0.90×) at 1T, 1.8 µs/roundtrip at 20T. history.md finding 16.

baseline.json regenerated from the same run (20T labels, 14 benches).
This commit is contained in:
claude-asm-audit
2026-08-21 12:22:46 +00:00
parent 5ddd122711
commit 77938cd31d
2 changed files with 337 additions and 145 deletions
+142 -2
View File
@@ -13,7 +13,18 @@
//! completeness.
//! 4. ping_pong_oneshot — N rounds of (spawn pair, send oneshot, await).
//! Closer to a request/response workload than channel
//! ping-pong.
//! ping-pong. NOTE: 2 spawns + 2 channel allocs + 2
//! joins per round; the message path is a minority
//! of it. Sections 5 and 6 split it apart.
//! 5. spawn_pair_control — section 4 with the messages removed: same
//! spawn/join shape, actors return immediately.
//! (4 − 5) ≈ per-round message-path cost.
//! 6. ping_pong_steady — ONE persistent pair, N roundtrips over unbounded
//! MPSC channels (smarm::channel vs
//! tokio::sync::mpsc::unbounded_channel — both
//! unbounded, non-blocking send). Steady-state
//! park/unpark cost per roundtrip, no spawn in the
//! loop.
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
@@ -418,6 +429,120 @@ fn bench_pp_tokio_multi() -> (u64, u128) {
(PP_ROUNDS, start.elapsed().as_micros())
}
// ---------------------------------------------------------------------------
// 5. spawn_pair_control — section 4 minus the messages
// ---------------------------------------------------------------------------
fn bench_ctl_smarm(threads: usize) -> (u64, u128) {
let start = Instant::now();
smarm::runtime::init(bench_cfg(threads)).run(|| {
for _ in 0..PP_ROUNDS {
let hb = smarm::spawn(|| {});
let ha = smarm::spawn(|| {});
ha.join().unwrap();
hb.join().unwrap();
}
});
(PP_ROUNDS, start.elapsed().as_micros())
}
fn bench_ctl_tokio_current() -> (u64, u128) {
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
let start = Instant::now();
let local = tokio::task::LocalSet::new();
local.block_on(&rt, async move {
for _ in 0..PP_ROUNDS {
let hb = tokio::task::spawn_local(async {});
let ha = tokio::task::spawn_local(async {});
let _ = ha.await;
let _ = hb.await;
}
});
(PP_ROUNDS, start.elapsed().as_micros())
}
fn bench_ctl_tokio_multi() -> (u64, u128) {
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(available_threads())
.build()
.unwrap();
let start = Instant::now();
rt.block_on(async move {
for _ in 0..PP_ROUNDS {
let hb = tokio::spawn(async {});
let ha = tokio::spawn(async {});
let _ = ha.await;
let _ = hb.await;
}
});
(PP_ROUNDS, start.elapsed().as_micros())
}
// ---------------------------------------------------------------------------
// 6. ping_pong_steady — one persistent pair, PP_STEADY roundtrips
// ---------------------------------------------------------------------------
const PP_STEADY: u64 = 10_000;
fn bench_steady_smarm(threads: usize) -> (u64, u128) {
let start = Instant::now();
smarm::runtime::init(bench_cfg(threads)).run(|| {
let (tx_ab, rx_ab) = smarm::channel::<u64>();
let (tx_ba, rx_ba) = smarm::channel::<u64>();
let echo = smarm::spawn(move || {
for _ in 0..PP_STEADY {
let v = rx_ab.recv().unwrap();
tx_ba.send(v + 1).unwrap();
}
});
for i in 0..PP_STEADY {
tx_ab.send(i).unwrap();
let v = rx_ba.recv().unwrap();
assert_eq!(v, i + 1);
}
echo.join().unwrap();
});
(PP_STEADY, start.elapsed().as_micros())
}
async fn steady_tokio_body() {
let (tx_ab, mut rx_ab) = tokio::sync::mpsc::unbounded_channel::<u64>();
let (tx_ba, mut rx_ba) = tokio::sync::mpsc::unbounded_channel::<u64>();
let echo = tokio::spawn(async move {
for _ in 0..PP_STEADY {
let v = rx_ab.recv().await.unwrap();
tx_ba.send(v + 1).unwrap();
}
});
for i in 0..PP_STEADY {
tx_ab.send(i).unwrap();
let v = rx_ba.recv().await.unwrap();
assert_eq!(v, i + 1);
}
echo.await.unwrap();
}
fn bench_steady_tokio_current() -> (u64, u128) {
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
let start = Instant::now();
rt.block_on(steady_tokio_body());
(PP_STEADY, start.elapsed().as_micros())
}
fn bench_steady_tokio_multi() -> (u64, u128) {
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(available_threads())
.build()
.unwrap();
let start = Instant::now();
rt.block_on(steady_tokio_body());
(PP_STEADY, start.elapsed().as_micros())
}
// ---------------------------------------------------------------------------
// main
// ---------------------------------------------------------------------------
@@ -453,7 +578,8 @@ fn main() {
);
println!(
"CHAIN_DEPTH={CHAIN_DEPTH}, YIELD_TASKS={YIELD_TASKS}×{YIELD_ROUNDS}, \
PRIME_N={PRIME_N}/{PRIME_WORKERS} workers, PP_ROUNDS={PP_ROUNDS}"
PRIME_N={PRIME_N}/{PRIME_WORKERS} workers, PP_ROUNDS={PP_ROUNDS}, \
PP_STEADY={PP_STEADY}"
);
// ---- 1. chained_spawn ----
@@ -491,4 +617,18 @@ fn main() {
run_n(&format!("smarm {n}-thread"), ITERS, || bench_pp_smarm(n));
run_n("tokio current_thread", ITERS, bench_pp_tokio_current);
run_n("tokio multi-thread", ITERS, bench_pp_tokio_multi);
// ---- 5. spawn_pair_control ----
print_header(&format!("spawn_pair_control: {PP_ROUNDS} rounds, no messages"));
run_n("smarm 1-thread", ITERS, || bench_ctl_smarm(1));
run_n(&format!("smarm {n}-thread"), ITERS, || bench_ctl_smarm(n));
run_n("tokio current_thread", ITERS, bench_ctl_tokio_current);
run_n("tokio multi-thread", ITERS, bench_ctl_tokio_multi);
// ---- 6. ping_pong_steady ----
print_header(&format!("ping_pong_steady: 1 pair × {PP_STEADY} roundtrips"));
run_n("smarm 1-thread", ITERS, || bench_steady_smarm(1));
run_n(&format!("smarm {n}-thread"), ITERS, || bench_steady_smarm(n));
run_n("tokio current_thread", ITERS, bench_steady_tokio_current);
run_n("tokio multi-thread", ITERS, bench_steady_tokio_multi);
}