From 77938cd31dcf928c94b40914d1bf3d231e027179 Mon Sep 17 00:00:00 2001 From: claude-asm-audit Date: Mon, 17 Aug 2026 22:41:07 +0000 Subject: [PATCH] =?UTF-8?q?bench:=20split=20ping=5Fpong=5Foneshot=20?= =?UTF-8?q?=E2=80=94=20spawn=5Fpair=5Fcontrol=20+=20ping=5Fpong=5Fsteady;?= =?UTF-8?q?=20refresh=20baseline=20(job=20e982b5b5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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). --- benches/baseline.json | 338 ++++++++++++++++++++++++------------------ benches/general.rs | 144 +++++++++++++++++- 2 files changed, 337 insertions(+), 145 deletions(-) diff --git a/benches/baseline.json b/benches/baseline.json index e03bf6c..054ef6f 100644 --- a/benches/baseline.json +++ b/benches/baseline.json @@ -2,313 +2,365 @@ "chained_spawn": { "smarm 1-thread": { "result": 1000, - "median": 466, - "min": 420, - "max": 503 + "median": 449, + "min": 440, + "max": 452 }, "smarm 20-thread": { "result": 1000, - "median": 2087, - "min": 1966, - "max": 2462 + "median": 2102, + "min": 1936, + "max": 2226 }, "tokio current_thread": { "result": 1000, - "median": 108, - "min": 101, - "max": 119 + "median": 109, + "min": 108, + "max": 125 }, "tokio multi-thread": { "result": 1000, - "median": 185, - "min": 162, - "max": 196 + "median": 186, + "min": 156, + "max": 211 } }, "yield_many": { "smarm 1-thread": { "result": 200000, - "median": 12513, - "min": 11098, - "max": 12846 + "median": 10563, + "min": 10181, + "max": 10589 }, "smarm 20-thread": { "result": 200000, - "median": 44080, - "min": 43502, - "max": 44754 + "median": 43218, + "min": 42523, + "max": 43864 }, "tokio current_thread": { "result": 200000, - "median": 5378, - "min": 4781, - "max": 5612 + "median": 5258, + "min": 5251, + "max": 5864 }, "tokio multi-thread": { "result": 200000, - "median": 6096, - "min": 5424, - "max": 7701 + "median": 5583, + "min": 5512, + "max": 6563 } }, "fan_out_compute": { "smarm 1-thread": { "result": 33860, - "median": 15121, - "min": 13878, - "max": 15143 + "median": 15030, + "min": 14584, + "max": 15039 }, "smarm 20-thread": { "result": 33860, - "median": 2452, - "min": 2389, - "max": 2833 + "median": 2362, + "min": 2274, + "max": 2426 }, "tokio current_thread": { "result": 33860, - "median": 14113, - "min": 12681, - "max": 14123 + "median": 14040, + "min": 14037, + "max": 14042 }, "tokio multi-thread": { "result": 33860, - "median": 2308, - "min": 1724, - "max": 2575 + "median": 2386, + "min": 2342, + "max": 2418 } }, "ping_pong_oneshot": { "smarm 1-thread": { "result": 1000, - "median": 839, - "min": 755, - "max": 861 + "median": 805, + "min": 789, + "max": 829 }, "smarm 20-thread": { "result": 1000, - "median": 7437, - "min": 7278, - "max": 8044 + "median": 7503, + "min": 7005, + "max": 7677 }, "tokio current_thread": { "result": 1000, - "median": 416, - "min": 399, - "max": 430 + "median": 410, + "min": 396, + "max": 440 }, "tokio multi-thread": { "result": 1000, - "median": 8963, - "min": 8918, - "max": 10302 + "median": 8911, + "min": 8844, + "max": 9198 } }, "spawn_storm_busy": { "smarm 1-thread": { "result": 10000, - "median": 109585, - "min": 103839, - "max": 116230 + "median": 103743, + "min": 103453, + "max": 104373 }, "smarm 20-thread": { "result": 10000, - "median": 15582, - "min": 15229, - "max": 17187 + "median": 15364, + "min": 14982, + "max": 15642 }, "tokio current_thread": { "result": 10000, - "median": 1072, - "min": 979, - "max": 1106 + "median": 1071, + "min": 1063, + "max": 1079 }, "tokio multi-thread": { "result": 10000, - "median": 11858, - "min": 11074, - "max": 13463 + "median": 12183, + "min": 11551, + "max": 12852 } }, "mpsc_contention": { "smarm 1-thread": { "result": 320000, - "median": 6123, - "min": 6084, - "max": 6830 + "median": 6711, + "min": 6703, + "max": 6722 }, "smarm 20-thread": { "result": 320000, - "median": 35813, - "min": 34901, - "max": 36057 + "median": 35331, + "min": 34287, + "max": 36335 }, "tokio current_thread": { "result": 320000, - "median": 5428, - "min": 5388, - "max": 6025 + "median": 6207, + "min": 5495, + "max": 6278 }, "tokio multi-thread": { "result": 320000, - "median": 67484, - "min": 50512, - "max": 74416 + "median": 72132, + "min": 66649, + "max": 75892 } }, "many_timers": { "smarm 1-thread": { "result": 10000, - "median": 123609, - "min": 114597, - "max": 124898 + "median": 117002, + "min": 103516, + "max": 118009 }, "smarm 20-thread": { "result": 10000, - "median": 54895, - "min": 53552, - "max": 56470 + "median": 53418, + "min": 52807, + "max": 53785 }, "tokio current_thread": { "result": 10000, - "median": 12427, - "min": 12422, - "max": 12565 + "median": 12444, + "min": 12389, + "max": 12483 }, "tokio multi-thread": { "result": 10000, - "median": 14004, - "min": 13884, - "max": 14367 + "median": 13948, + "min": 13791, + "max": 14268 } }, "multi_thread_scaling": { "smarm 1-thread": { "result": 33860, - "median": 15117, - "min": 15083, - "max": 15143 + "median": 15036, + "min": 15029, + "max": 15060 }, "smarm 2-thread": { "result": 33860, - "median": 7988, - "min": 7911, - "max": 8031 + "median": 7987, + "min": 7965, + "max": 8027 }, "smarm 4-thread": { "result": 33860, - "median": 4302, - "min": 4275, - "max": 4311 + "median": 4299, + "min": 4269, + "max": 4309 }, "smarm 20-thread": { "result": 33860, - "median": 2413, - "min": 2379, - "max": 2474 + "median": 2381, + "min": 2321, + "max": 2409 }, "tokio multi 1-thread": { "result": 33860, - "median": 14266, - "min": 14259, - "max": 16632 + "median": 14300, + "min": 14213, + "max": 14386 }, "tokio multi 2-thread": { "result": 33860, - "median": 7288, - "min": 7196, - "max": 8657 + "median": 7356, + "min": 7308, + "max": 7408 }, "tokio multi 4-thread": { "result": 33860, - "median": 3760, - "min": 3716, - "max": 4078 + "median": 3725, + "min": 3714, + "max": 3728 }, "tokio multi 20-thread": { "result": 33860, - "median": 2381, - "min": 1935, - "max": 2631 + "median": 2402, + "min": 2383, + "max": 2421 } }, "deep_recursion": { "smarm 1-thread": { "result": 1, - "median": 293, - "min": 286, - "max": 322 + "median": 291, + "min": 288, + "max": 323 }, "smarm 20-thread": { "result": 1, - "median": 729, - "min": 683, - "max": 752 + "median": 673, + "min": 627, + "max": 740 }, "tokio current_thread": { "result": 1, - "median": 12, - "min": 12, - "max": 13 + "median": 14, + "min": 13, + "max": 17 }, "tokio multi-thread": { "result": 1, - "median": 54, + "median": 53, "min": 50, - "max": 61 + "max": 54 } }, "yield_in_hot_loop": { "smarm 1-thread": { "result": 1000000, - "median": 40171, - "min": 38692, - "max": 42902 + "median": 30361, + "min": 29592, + "max": 30509 }, "tokio current_thread": { "result": 1000000, - "median": 63828, - "min": 63683, - "max": 70517 + "median": 69408, + "min": 67891, + "max": 70101 } }, "uncontended_channel": { "smarm 1-thread": { "result": 1000000, - "median": 12441, - "min": 12286, - "max": 13828 + "median": 11962, + "min": 11943, + "max": 12052 }, "tokio current_thread": { "result": 1000000, - "median": 15035, - "min": 15014, - "max": 16722 + "median": 14664, + "min": 14584, + "max": 14702 } }, "catch_unwind_panics": { "smarm 1-thread": { "result": 10000, - "median": 127342, - "min": 122307, - "max": 132850 + "median": 121767, + "min": 120583, + "max": 122123 }, "smarm 20-thread": { "result": 10000, - "median": 20430, - "min": 19417, - "max": 20735 + "median": 19950, + "min": 19260, + "max": 20565 }, "tokio current_thread": { "result": 10000, - "median": 10716, - "min": 10325, - "max": 11039 + "median": 10184, + "min": 10037, + "max": 10660 }, "tokio multi-thread": { "result": 10000, - "median": 2500, - "min": 2352, - "max": 2656 + "median": 2430, + "min": 2339, + "max": 2628 + } + }, + "ping_pong_steady": { + "smarm 1-thread": { + "result": 10000, + "median": 1427, + "min": 1419, + "max": 1437 + }, + "smarm 20-thread": { + "result": 10000, + "median": 18485, + "min": 17162, + "max": 18897 + }, + "tokio current_thread": { + "result": 10000, + "median": 1285, + "min": 1281, + "max": 1287 + }, + "tokio multi-thread": { + "result": 10000, + "median": 79958, + "min": 77547, + "max": 82155 + } + }, + "spawn_pair_control": { + "smarm 1-thread": { + "result": 1000, + "median": 632, + "min": 624, + "max": 634 + }, + "smarm 20-thread": { + "result": 1000, + "median": 5017, + "min": 4707, + "max": 5149 + }, + "tokio current_thread": { + "result": 1000, + "median": 287, + "min": 277, + "max": 335 + }, + "tokio multi-thread": { + "result": 1000, + "median": 8621, + "min": 8415, + "max": 8828 } } } \ No newline at end of file diff --git a/benches/general.rs b/benches/general.rs index 0da2be1..3f10d07 100644 --- a/benches/general.rs +++ b/benches/general.rs @@ -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::(); + let (tx_ba, rx_ba) = smarm::channel::(); + 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::(); + let (tx_ba, mut rx_ba) = tokio::sync::mpsc::unbounded_channel::(); + 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); }