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:
+142
-2
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user