style: cargo fmt sweep under rustc 1.97.1 (toolchain reformat, no semantic change)

This commit is contained in:
smarm-agent
2026-08-13 05:56:49 +00:00
parent 1262cc30e3
commit 95306c7f60
71 changed files with 1956 additions and 763 deletions
+67 -26
View File
@@ -26,7 +26,9 @@ use std::time::Instant;
const ITERS: u32 = 15;
fn available_threads() -> usize {
std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1)
std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(1)
}
fn env_sets() -> u32 {
@@ -108,17 +110,15 @@ fn bench_chained_smarm(threads: usize) -> (u64, u128) {
fn bench_chained_tokio_current() -> (u64, u128) {
let counter = Arc::new(AtomicU64::new(0));
let c2 = counter.clone();
let rt = tokio::runtime::Builder::new_current_thread().build().unwrap();
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 {
// Use a oneshot done channel like tokio's own chained_spawn bench.
let (done_tx, done_rx) = tokio::sync::oneshot::channel();
fn iter(
c: Arc<AtomicU64>,
done: tokio::sync::oneshot::Sender<()>,
n: u64,
) {
fn iter(c: Arc<AtomicU64>, done: tokio::sync::oneshot::Sender<()>, n: u64) {
if n == 0 {
let _ = done.send(());
} else {
@@ -186,7 +186,9 @@ fn bench_yield_smarm(threads: usize) -> (u64, u128) {
}
fn bench_yield_tokio_current() -> (u64, u128) {
let rt = tokio::runtime::Builder::new_current_thread().build().unwrap();
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 {
@@ -235,11 +237,22 @@ const PRIME_N: u64 = 400_000;
const PRIME_WORKERS: u64 = 64;
fn is_prime(n: u64) -> bool {
if n < 2 { return false; }
if n < 4 { return true; }
if n % 2 == 0 { return false; }
if n < 2 {
return false;
}
if n < 4 {
return true;
}
if n % 2 == 0 {
return false;
}
let mut i = 3u64;
while i * i <= n { if n % i == 0 { return false; } i += 2; }
while i * i <= n {
if n % i == 0 {
return false;
}
i += 2;
}
true
}
@@ -250,7 +263,11 @@ fn count_primes(lo: u64, hi: u64) -> u64 {
fn primes_slice(w: u64) -> (u64, u64) {
let per = PRIME_N / PRIME_WORKERS;
let lo = w * per;
let hi = if w + 1 == PRIME_WORKERS { PRIME_N } else { lo + per };
let hi = if w + 1 == PRIME_WORKERS {
PRIME_N
} else {
lo + per
};
(lo, hi)
}
@@ -267,7 +284,9 @@ fn bench_primes_smarm(threads: usize) -> (u64, u128) {
tc.fetch_add(count_primes(lo, hi), Ordering::Relaxed);
}));
}
for h in handles { h.join().unwrap(); }
for h in handles {
h.join().unwrap();
}
});
(total.load(Ordering::Relaxed), start.elapsed().as_micros())
}
@@ -275,7 +294,9 @@ fn bench_primes_smarm(threads: usize) -> (u64, u128) {
fn bench_primes_tokio_current() -> (u64, u128) {
let total = Arc::new(AtomicU64::new(0));
let t2 = total.clone();
let rt = tokio::runtime::Builder::new_current_thread().build().unwrap();
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 {
@@ -287,7 +308,9 @@ fn bench_primes_tokio_current() -> (u64, u128) {
tc.fetch_add(count_primes(lo, hi), Ordering::Relaxed);
}));
}
for h in handles { let _ = h.await; }
for h in handles {
let _ = h.await;
}
});
(total.load(Ordering::Relaxed), start.elapsed().as_micros())
}
@@ -309,7 +332,9 @@ fn bench_primes_tokio_multi() -> (u64, u128) {
tc.fetch_add(count_primes(lo, hi), Ordering::Relaxed);
}));
}
for h in handles { let _ = h.await; }
for h in handles {
let _ = h.await;
}
});
(total.load(Ordering::Relaxed), start.elapsed().as_micros())
}
@@ -344,7 +369,9 @@ fn bench_pp_smarm(threads: usize) -> (u64, u128) {
}
fn bench_pp_tokio_current() -> (u64, u128) {
let rt = tokio::runtime::Builder::new_current_thread().build().unwrap();
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 {
@@ -395,7 +422,6 @@ fn bench_pp_tokio_multi() -> (u64, u128) {
// main
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// Knob helper — reads SMARM_ALLOC_INTERVAL / SMARM_TIMESLICE_CYCLES env vars
// so the sweep script can override the preemption knobs without recompiling.
@@ -404,10 +430,14 @@ fn bench_pp_tokio_multi() -> (u64, u128) {
fn bench_cfg(threads: usize) -> smarm::runtime::Config {
let mut cfg = smarm::runtime::Config::exact(threads);
if let Ok(v) = std::env::var("SMARM_ALLOC_INTERVAL") {
if let Ok(n) = v.parse::<u32>() { cfg = cfg.alloc_interval(n); }
if let Ok(n) = v.parse::<u32>() {
cfg = cfg.alloc_interval(n);
}
}
if let Ok(v) = std::env::var("SMARM_TIMESLICE_CYCLES") {
if let Ok(n) = v.parse::<u64>() { cfg = cfg.timeslice_cycles(n); }
if let Ok(n) = v.parse::<u64>() {
cfg = cfg.timeslice_cycles(n);
}
}
cfg
}
@@ -417,7 +447,10 @@ fn main() {
println!("smarm general benchmarks");
println!("available parallelism: {n} threads");
let sets = env_sets();
println!("ITERS={ITERS}×{sets} sets = {} samples (+1 warmup, discarded)", ITERS * sets);
println!(
"ITERS={ITERS}×{sets} sets = {} samples (+1 warmup, discarded)",
ITERS * sets
);
println!(
"CHAIN_DEPTH={CHAIN_DEPTH}, YIELD_TASKS={YIELD_TASKS}×{YIELD_ROUNDS}, \
PRIME_N={PRIME_N}/{PRIME_WORKERS} workers, PP_ROUNDS={PP_ROUNDS}"
@@ -426,21 +459,29 @@ fn main() {
// ---- 1. chained_spawn ----
print_header(&format!("chained_spawn: depth {CHAIN_DEPTH}"));
run_n("smarm 1-thread", ITERS, || bench_chained_smarm(1));
run_n(&format!("smarm {n}-thread"), ITERS, || bench_chained_smarm(n));
run_n(&format!("smarm {n}-thread"), ITERS, || {
bench_chained_smarm(n)
});
run_n("tokio current_thread", ITERS, bench_chained_tokio_current);
run_n("tokio multi-thread", ITERS, bench_chained_tokio_multi);
// ---- 2. yield_many ----
print_header(&format!("yield_many: {YIELD_TASKS} tasks × {YIELD_ROUNDS} yields"));
print_header(&format!(
"yield_many: {YIELD_TASKS} tasks × {YIELD_ROUNDS} yields"
));
run_n("smarm 1-thread", ITERS, || bench_yield_smarm(1));
run_n(&format!("smarm {n}-thread"), ITERS, || bench_yield_smarm(n));
run_n("tokio current_thread", ITERS, bench_yield_tokio_current);
run_n("tokio multi-thread", ITERS, bench_yield_tokio_multi);
// ---- 3. fan_out_compute ----
print_header(&format!("fan_out_compute: primes in [2, {PRIME_N}) across {PRIME_WORKERS}"));
print_header(&format!(
"fan_out_compute: primes in [2, {PRIME_N}) across {PRIME_WORKERS}"
));
run_n("smarm 1-thread", ITERS, || bench_primes_smarm(1));
run_n(&format!("smarm {n}-thread"), ITERS, || bench_primes_smarm(n));
run_n(&format!("smarm {n}-thread"), ITERS, || {
bench_primes_smarm(n)
});
run_n("tokio current_thread", ITERS, bench_primes_tokio_current);
run_n("tokio multi-thread", ITERS, bench_primes_tokio_multi);
+90 -41
View File
@@ -64,11 +64,22 @@ const PRIME_N: u64 = 400_000;
const WORKERS: u64 = 64;
fn is_prime(n: u64) -> bool {
if n < 2 { return false; }
if n < 4 { return true; }
if n % 2 == 0 { return false; }
if n < 2 {
return false;
}
if n < 4 {
return true;
}
if n % 2 == 0 {
return false;
}
let mut i = 3u64;
while i * i <= n { if n % i == 0 { return false; } i += 2; }
while i * i <= n {
if n % i == 0 {
return false;
}
i += 2;
}
true
}
@@ -96,7 +107,9 @@ fn bench_primes_smarm(threads: usize) -> (u64, u128) {
tc.fetch_add(count_primes(lo, hi), Ordering::Relaxed);
}));
}
for h in handles { h.join().unwrap(); }
for h in handles {
h.join().unwrap();
}
});
(total.load(Ordering::Relaxed), start.elapsed().as_micros())
}
@@ -104,7 +117,9 @@ fn bench_primes_smarm(threads: usize) -> (u64, u128) {
fn bench_primes_tokio_current() -> (u64, u128) {
let total = Arc::new(AtomicU64::new(0));
let t2 = total.clone();
let rt = tokio::runtime::Builder::new_current_thread().build().unwrap();
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 {
@@ -116,7 +131,9 @@ fn bench_primes_tokio_current() -> (u64, u128) {
tc.fetch_add(count_primes(lo, hi), Ordering::Relaxed);
}));
}
for h in handles { let _ = h.await; }
for h in handles {
let _ = h.await;
}
});
(total.load(Ordering::Relaxed), start.elapsed().as_micros())
}
@@ -138,17 +155,21 @@ fn bench_primes_tokio_multi() -> (u64, u128) {
tc.fetch_add(count_primes(lo, hi), Ordering::Relaxed);
}));
}
for h in handles { let _ = h.await; }
for h in handles {
let _ = h.await;
}
});
(total.load(Ordering::Relaxed), start.elapsed().as_micros())
}
fn bench_primes_baseline() -> (u64, u128) {
let start = Instant::now();
let total: u64 = (0..WORKERS).map(|w| {
let (lo, hi) = primes_slice(w);
count_primes(lo, hi)
}).sum();
let total: u64 = (0..WORKERS)
.map(|w| {
let (lo, hi) = primes_slice(w);
count_primes(lo, hi)
})
.sum();
(total, start.elapsed().as_micros())
}
@@ -167,15 +188,17 @@ fn bench_pingpong_smarm(threads: usize) -> (u64, u128) {
tx_a.send(0).unwrap();
loop {
let v = rx_b.recv().unwrap();
if v >= PING_ROUNDS { break; }
if v >= PING_ROUNDS {
break;
}
tx_a.send(v + 1).unwrap();
}
});
let hb = smarm::spawn(move || {
loop {
let v = rx_a.recv().unwrap();
tx_b.send(v + 1).unwrap();
if v + 1 >= PING_ROUNDS { break; }
let hb = smarm::spawn(move || loop {
let v = rx_a.recv().unwrap();
tx_b.send(v + 1).unwrap();
if v + 1 >= PING_ROUNDS {
break;
}
});
ha.join().unwrap();
@@ -198,7 +221,9 @@ fn bench_pingpong_tokio_current() -> (u64, u128) {
tx_a.send(0).unwrap();
loop {
let v = rx_b.recv().await.unwrap();
if v >= PING_ROUNDS { break; }
if v >= PING_ROUNDS {
break;
}
tx_a.send(v + 1).unwrap();
}
});
@@ -206,7 +231,9 @@ fn bench_pingpong_tokio_current() -> (u64, u128) {
loop {
let v = rx_a.recv().await.unwrap();
tx_b.send(v + 1).unwrap();
if v + 1 >= PING_ROUNDS { break; }
if v + 1 >= PING_ROUNDS {
break;
}
}
});
let _ = ha.await;
@@ -229,7 +256,9 @@ fn bench_pingpong_tokio_multi() -> (u64, u128) {
tx_a.send(0).unwrap();
loop {
let v = rx_b.recv().await.unwrap();
if v >= PING_ROUNDS { break; }
if v >= PING_ROUNDS {
break;
}
tx_a.send(v + 1).unwrap();
}
});
@@ -237,7 +266,9 @@ fn bench_pingpong_tokio_multi() -> (u64, u128) {
loop {
let v = rx_a.recv().await.unwrap();
tx_b.send(v + 1).unwrap();
if v + 1 >= PING_ROUNDS { break; }
if v + 1 >= PING_ROUNDS {
break;
}
}
});
let _ = ha.await;
@@ -264,7 +295,9 @@ fn bench_spawn_smarm(threads: usize) -> (u64, u128) {
cc.fetch_add(1, Ordering::Relaxed);
}));
}
for h in handles { h.join().unwrap(); }
for h in handles {
h.join().unwrap();
}
});
(counter.load(Ordering::Relaxed), start.elapsed().as_micros())
}
@@ -272,7 +305,9 @@ fn bench_spawn_smarm(threads: usize) -> (u64, u128) {
fn bench_spawn_tokio_current() -> (u64, u128) {
let counter = Arc::new(AtomicU64::new(0));
let c = counter.clone();
let rt = tokio::runtime::Builder::new_current_thread().build().unwrap();
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 {
@@ -283,7 +318,9 @@ fn bench_spawn_tokio_current() -> (u64, u128) {
cc.fetch_add(1, Ordering::Relaxed);
}));
}
for h in handles { let _ = h.await; }
for h in handles {
let _ = h.await;
}
});
(counter.load(Ordering::Relaxed), start.elapsed().as_micros())
}
@@ -304,7 +341,9 @@ fn bench_spawn_tokio_multi() -> (u64, u128) {
cc.fetch_add(1, Ordering::Relaxed);
}));
}
for h in handles { let _ = h.await; }
for h in handles {
let _ = h.await;
}
});
(counter.load(Ordering::Relaxed), start.elapsed().as_micros())
}
@@ -320,24 +359,34 @@ fn main() {
println!("PRIME_N={PRIME_N}, WORKERS={WORKERS}, PING_ROUNDS={PING_ROUNDS}, SPAWN_COUNT={SPAWN_COUNT}");
// ---- Primes ----
print_header(&format!("Fan-out/fan-in: count primes in [2, {PRIME_N}) across {WORKERS} workers"));
run_n("baseline (serial)", ITERS, bench_primes_baseline);
run_n("smarm single-thread", ITERS, || bench_primes_smarm(1));
run_n(&format!("smarm {n}-thread"), ITERS, || bench_primes_smarm(n));
run_n("tokio current_thread", ITERS, bench_primes_tokio_current);
run_n("tokio multi-thread", ITERS, bench_primes_tokio_multi);
print_header(&format!(
"Fan-out/fan-in: count primes in [2, {PRIME_N}) across {WORKERS} workers"
));
run_n("baseline (serial)", ITERS, bench_primes_baseline);
run_n("smarm single-thread", ITERS, || bench_primes_smarm(1));
run_n(&format!("smarm {n}-thread"), ITERS, || {
bench_primes_smarm(n)
});
run_n("tokio current_thread", ITERS, bench_primes_tokio_current);
run_n("tokio multi-thread", ITERS, bench_primes_tokio_multi);
// ---- Ping-pong ----
print_header(&format!("Ping-pong: {PING_ROUNDS} round-trips between two actors"));
run_n("smarm single-thread", ITERS, || bench_pingpong_smarm(1));
run_n(&format!("smarm {n}-thread"), ITERS, || bench_pingpong_smarm(n));
run_n("tokio current_thread", ITERS, bench_pingpong_tokio_current);
run_n("tokio multi-thread", ITERS, bench_pingpong_tokio_multi);
print_header(&format!(
"Ping-pong: {PING_ROUNDS} round-trips between two actors"
));
run_n("smarm single-thread", ITERS, || bench_pingpong_smarm(1));
run_n(&format!("smarm {n}-thread"), ITERS, || {
bench_pingpong_smarm(n)
});
run_n("tokio current_thread", ITERS, bench_pingpong_tokio_current);
run_n("tokio multi-thread", ITERS, bench_pingpong_tokio_multi);
// ---- Spawn throughput ----
print_header(&format!("Spawn throughput: {SPAWN_COUNT} actors spawned and joined"));
run_n("smarm single-thread", ITERS, || bench_spawn_smarm(1));
print_header(&format!(
"Spawn throughput: {SPAWN_COUNT} actors spawned and joined"
));
run_n("smarm single-thread", ITERS, || bench_spawn_smarm(1));
run_n(&format!("smarm {n}-thread"), ITERS, || bench_spawn_smarm(n));
run_n("tokio current_thread", ITERS, bench_spawn_tokio_current);
run_n("tokio multi-thread", ITERS, bench_spawn_tokio_multi);
run_n("tokio current_thread", ITERS, bench_spawn_tokio_current);
run_n("tokio multi-thread", ITERS, bench_spawn_tokio_multi);
}
+24 -7
View File
@@ -16,12 +16,20 @@ const WORKERS: u64 = 16;
const ITERATIONS: u32 = 5;
fn is_prime(n: u64) -> bool {
if n < 2 { return false; }
if n < 4 { return true; }
if n % 2 == 0 { return false; }
if n < 2 {
return false;
}
if n < 4 {
return true;
}
if n % 2 == 0 {
return false;
}
let mut i = 3u64;
while i * i <= n {
if n % i == 0 { return false; }
if n % i == 0 {
return false;
}
i += 2;
}
true
@@ -30,7 +38,9 @@ fn is_prime(n: u64) -> bool {
fn count_primes_in(lo: u64, hi: u64) -> u64 {
let mut count = 0u64;
for n in lo..hi {
if is_prime(n) { count += 1; }
if is_prime(n) {
count += 1;
}
}
count
}
@@ -38,7 +48,11 @@ fn count_primes_in(lo: u64, hi: u64) -> u64 {
fn slice(worker: u64) -> (u64, u64) {
let per = N / WORKERS;
let lo = worker * per;
let hi = if worker + 1 == WORKERS { N } else { (worker + 1) * per };
let hi = if worker + 1 == WORKERS {
N
} else {
(worker + 1) * per
};
(lo, hi)
}
@@ -125,7 +139,10 @@ fn main() {
"Counting primes in [2, {}) across {} workers, {} iterations each\n",
N, WORKERS, ITERATIONS
);
println!("{:>12} | {:>15} | {:>16} | {:>15} | {:>15}", "runtime", "primes found", "median", "min", "max");
println!(
"{:>12} | {:>15} | {:>16} | {:>15} | {:>15}",
"runtime", "primes found", "median", "min", "max"
);
println!("{}", "-".repeat(80));
run_n("baseline", ITERATIONS, bench_baseline);
+44 -7
View File
@@ -27,12 +27,19 @@ use std::sync::Arc;
use std::time::Instant;
fn env_usize(key: &str, default: usize) -> usize {
std::env::var(key).ok().and_then(|v| v.parse().ok()).unwrap_or(default)
std::env::var(key)
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(default)
}
fn env_threads() -> Vec<usize> {
std::env::var("SMARM_BENCH_THREADS")
.map(|v| v.split_whitespace().filter_map(|t| t.parse().ok()).collect())
.map(|v| {
v.split_whitespace()
.filter_map(|t| t.parse().ok())
.collect()
})
.unwrap_or_else(|_| vec![1, 2, 4])
}
@@ -53,7 +60,11 @@ fn drive<Q: Send + Sync + 'static>(
for p in 0..producers {
let q = q.clone();
// Give the last producer the remainder.
let n = if p == producers - 1 { items - per * (producers - 1) } else { per };
let n = if p == producers - 1 {
items - per * (producers - 1)
} else {
per
};
hs.push(std::thread::spawn(move || {
let pid = Pid::new(p as u32, 0);
for _ in 0..n {
@@ -132,7 +143,12 @@ fn main() {
for &t in &threads_sweep {
for (p, c) in ratios_for(t) {
for s in ["mutex", "mpmc", "striped"] {
cases.push(Case { structure: s, threads: t, producers: p, consumers: c });
cases.push(Case {
structure: s,
threads: t,
producers: p,
consumers: c,
});
}
}
}
@@ -147,7 +163,14 @@ fn main() {
if case.threads < 2 {
drive_single(&*q, MutexQueue::push, MutexQueue::pop, items)
} else {
drive(q, MutexQueue::push, MutexQueue::pop, case.producers, case.consumers, items)
drive(
q,
MutexQueue::push,
MutexQueue::pop,
case.producers,
case.consumers,
items,
)
}
}
"mpmc" => {
@@ -155,7 +178,14 @@ fn main() {
if case.threads < 2 {
drive_single(&*q, MpmcRing::push, MpmcRing::pop, items)
} else {
drive(q, MpmcRing::push, MpmcRing::pop, case.producers, case.consumers, items)
drive(
q,
MpmcRing::push,
MpmcRing::pop,
case.producers,
case.consumers,
items,
)
}
}
"striped" => {
@@ -163,7 +193,14 @@ fn main() {
if case.threads < 2 {
drive_single(&*q, StripedRing::push, StripedRing::pop, items)
} else {
drive(q, StripedRing::push, StripedRing::pop, case.producers, case.consumers, items)
drive(
q,
StripedRing::push,
StripedRing::pop,
case.producers,
case.consumers,
items,
)
}
}
_ => unreachable!(),
+21 -4
View File
@@ -54,12 +54,19 @@ fn variant() -> &'static str {
}
fn env_usize(key: &str, default: usize) -> usize {
std::env::var(key).ok().and_then(|v| v.parse().ok()).unwrap_or(default)
std::env::var(key)
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(default)
}
fn env_threads() -> Vec<usize> {
std::env::var("SMARM_BENCH_THREADS")
.map(|v| v.split_whitespace().filter_map(|t| t.parse().ok()).collect())
.map(|v| {
v.split_whitespace()
.filter_map(|t| t.parse().ok())
.collect()
})
.unwrap_or_else(|_| vec![1, 2, 4])
}
@@ -238,12 +245,22 @@ fn main() {
);
println!(
"RQCSV,runtime,{},{},{},{},{},{},{}",
variant(), slot_str, name, t, work, mid.us, per_s
variant(),
slot_str,
name,
t,
work,
mid.us,
per_s
);
if slot {
println!(
"RQSLOT,{},{},{},{},{}",
variant(), name, t, mid.hits, mid.displacements
variant(),
name,
t,
mid.hits,
mid.displacements
);
}
}
+55 -19
View File
@@ -37,7 +37,9 @@ use std::time::Instant;
const ITERS: u32 = 15;
fn available_threads() -> usize {
std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1)
std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(1)
}
fn env_sets() -> u32 {
@@ -116,7 +118,9 @@ fn bench_recurse_smarm(threads: usize) -> (u64, u128) {
fn bench_recurse_tokio_current() -> (u64, u128) {
let counter = Arc::new(AtomicU64::new(0));
let c2 = counter.clone();
let rt = tokio::runtime::Builder::new_current_thread().build().unwrap();
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 {
@@ -199,7 +203,9 @@ fn bench_hot_smarm() -> (u64, u128) {
}
fn bench_hot_tokio_current() -> (u64, u128) {
let rt = tokio::runtime::Builder::new_current_thread().build().unwrap();
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 {
@@ -249,7 +255,9 @@ fn bench_unc_smarm() -> (u64, u128) {
}
fn bench_unc_tokio_current() -> (u64, u128) {
let rt = tokio::runtime::Builder::new_current_thread().build().unwrap();
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 {
@@ -297,8 +305,12 @@ fn bench_panic_smarm(threads: usize) -> (u64, u128) {
}
for h in handles {
match h.join() {
Ok(()) => { ok2.fetch_add(1, Ordering::Relaxed); }
Err(_) => { err2.fetch_add(1, Ordering::Relaxed); }
Ok(()) => {
ok2.fetch_add(1, Ordering::Relaxed);
}
Err(_) => {
err2.fetch_add(1, Ordering::Relaxed);
}
}
}
});
@@ -312,7 +324,9 @@ fn bench_panic_tokio_current() -> (u64, u128) {
let err = Arc::new(AtomicU64::new(0));
let ok2 = ok.clone();
let err2 = err.clone();
let rt = tokio::runtime::Builder::new_current_thread().build().unwrap();
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
let prev_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(|_| {}));
let start = Instant::now();
@@ -328,8 +342,12 @@ fn bench_panic_tokio_current() -> (u64, u128) {
}
for h in handles {
match h.await {
Ok(()) => { ok2.fetch_add(1, Ordering::Relaxed); }
Err(_) => { err2.fetch_add(1, Ordering::Relaxed); }
Ok(()) => {
ok2.fetch_add(1, Ordering::Relaxed);
}
Err(_) => {
err2.fetch_add(1, Ordering::Relaxed);
}
}
}
});
@@ -361,8 +379,12 @@ fn bench_panic_tokio_multi() -> (u64, u128) {
}
for h in handles {
match h.await {
Ok(()) => { ok2.fetch_add(1, Ordering::Relaxed); }
Err(_) => { err2.fetch_add(1, Ordering::Relaxed); }
Ok(()) => {
ok2.fetch_add(1, Ordering::Relaxed);
}
Err(_) => {
err2.fetch_add(1, Ordering::Relaxed);
}
}
}
});
@@ -375,7 +397,6 @@ fn bench_panic_tokio_multi() -> (u64, u128) {
// main
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// Knob helper — reads SMARM_ALLOC_INTERVAL / SMARM_TIMESLICE_CYCLES env vars
// so the sweep script can override the preemption knobs without recompiling.
@@ -384,10 +405,14 @@ fn bench_panic_tokio_multi() -> (u64, u128) {
fn bench_cfg(threads: usize) -> smarm::runtime::Config {
let mut cfg = smarm::runtime::Config::exact(threads);
if let Ok(v) = std::env::var("SMARM_ALLOC_INTERVAL") {
if let Ok(n) = v.parse::<u32>() { cfg = cfg.alloc_interval(n); }
if let Ok(n) = v.parse::<u32>() {
cfg = cfg.alloc_interval(n);
}
}
if let Ok(v) = std::env::var("SMARM_TIMESLICE_CYCLES") {
if let Ok(n) = v.parse::<u64>() { cfg = cfg.timeslice_cycles(n); }
if let Ok(n) = v.parse::<u64>() {
cfg = cfg.timeslice_cycles(n);
}
}
cfg
}
@@ -397,7 +422,10 @@ fn main() {
println!("smarm smarm-favored benchmarks");
println!("available parallelism: {n} threads");
let sets = env_sets();
println!("ITERS={ITERS}×{sets} sets = {} samples (+1 warmup, discarded)", ITERS * sets);
println!(
"ITERS={ITERS}×{sets} sets = {} samples (+1 warmup, discarded)",
ITERS * sets
);
println!(
"RECURSE_DEPTH={RECURSE_DEPTH}, HOT_YIELDS={HOT_YIELDS}×2, \
UNCONT_MSGS={UNCONT_MSGS}, PANIC_TASKS={PANIC_TASKS}"
@@ -406,22 +434,30 @@ fn main() {
// ---- 9. deep_recursion ----
print_header(&format!("deep_recursion: depth {RECURSE_DEPTH}"));
run_n("smarm 1-thread", ITERS, || bench_recurse_smarm(1));
run_n(&format!("smarm {n}-thread"), ITERS, || bench_recurse_smarm(n));
run_n(&format!("smarm {n}-thread"), ITERS, || {
bench_recurse_smarm(n)
});
run_n("tokio current_thread", ITERS, bench_recurse_tokio_current);
run_n("tokio multi-thread", ITERS, bench_recurse_tokio_multi);
// ---- 10. yield_in_hot_loop ----
print_header(&format!("yield_in_hot_loop: 2 actors × {HOT_YIELDS} yields (single thread)"));
print_header(&format!(
"yield_in_hot_loop: 2 actors × {HOT_YIELDS} yields (single thread)"
));
run_n("smarm 1-thread", ITERS, bench_hot_smarm);
run_n("tokio current_thread", ITERS, bench_hot_tokio_current);
// ---- 11. uncontended_channel ----
print_header(&format!("uncontended_channel: 1→1, {UNCONT_MSGS} msgs (single thread)"));
print_header(&format!(
"uncontended_channel: 1→1, {UNCONT_MSGS} msgs (single thread)"
));
run_n("smarm 1-thread", ITERS, bench_unc_smarm);
run_n("tokio current_thread", ITERS, bench_unc_tokio_current);
// ---- 12. catch_unwind_panics ----
print_header(&format!("catch_unwind_panics: {PANIC_TASKS} tasks, 50% panic"));
print_header(&format!(
"catch_unwind_panics: {PANIC_TASKS} tasks, 50% panic"
));
run_n("smarm 1-thread", ITERS, || bench_panic_smarm(1));
run_n(&format!("smarm {n}-thread"), ITERS, || bench_panic_smarm(n));
run_n("tokio current_thread", ITERS, bench_panic_tokio_current);
+30 -5
View File
@@ -73,7 +73,10 @@ fn variant() -> &'static str {
}
fn env_usize(key: &str, default: usize) -> usize {
std::env::var(key).ok().and_then(|v| v.parse().ok()).unwrap_or(default)
std::env::var(key)
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(default)
}
// --------------------------------------------------------------------------
@@ -226,7 +229,11 @@ fn main() {
let mean_cyc = pooled_cyc.iter().map(|&v| v as f64).sum::<f64>() / n.max(1) as f64;
// Derived effective frequency: cycles per ns = GHz. Cross-checks the two
// lenses against the box's known base clock.
let derived_ghz = if mean_ns > 0.0 { mean_cyc / mean_ns } else { 0.0 };
let derived_ghz = if mean_ns > 0.0 {
mean_cyc / mean_ns
} else {
0.0
};
let p50 = pct(&pooled_ns, 50.0);
let p90 = pct(&pooled_ns, 90.0);
@@ -241,8 +248,14 @@ fn main() {
" rounds={} warmup={} runs={} (instrumentation floor: {} ns / {} cyc, subtracted)",
rounds, warmup, runs, floor_ns, floor_cyc
);
println!(" {:<10} {:<10} {:<10} {:<10} {:<10}", "p50 ns", "p90 ns", "p99 ns", "min ns", "max ns");
println!(" {:<10} {:<10} {:<10} {:<10} {:<10}", p50, p90, p99, lo, hi);
println!(
" {:<10} {:<10} {:<10} {:<10} {:<10}",
"p50 ns", "p90 ns", "p99 ns", "min ns", "max ns"
);
println!(
" {:<10} {:<10} {:<10} {:<10} {:<10}",
p50, p90, p99, lo, hi
);
println!(
" mean {:.1} ns | mean {:.0} cyc | derived {:.3} GHz",
mean_ns, mean_cyc, derived_ghz
@@ -251,6 +264,18 @@ fn main() {
// Greppable line — same spirit as SPINCSV.
println!(
"SWITCHCSV,{},{},{},{},{},{},{},{},{},{},{:.1},{:.0},{:.3}",
variant(), mode, rounds, runs, n, p50, p90, p99, lo, hi, mean_ns, mean_cyc, derived_ghz
variant(),
mode,
rounds,
runs,
n,
p50,
p90,
p99,
lo,
hi,
mean_ns,
mean_cyc,
derived_ghz
);
}
+107 -35
View File
@@ -36,7 +36,9 @@ use std::time::{Duration, Instant};
const ITERS: u32 = 15;
fn available_threads() -> usize {
std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1)
std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(1)
}
fn env_sets() -> u32 {
@@ -84,8 +86,8 @@ fn run_n<F: FnMut() -> (u64, u128)>(name: &str, n: u32, mut f: F) {
// 5. spawn_storm_busy — workers loaded, then storm of zero-work spawns
// ---------------------------------------------------------------------------
const STORM_BACKGROUND: u64 = 8; // number of background "busy" actors
const STORM_SPAWN: u64 = 10_000; // zero-work spawns to time
const STORM_BACKGROUND: u64 = 8; // number of background "busy" actors
const STORM_SPAWN: u64 = 10_000; // zero-work spawns to time
fn bench_storm_smarm(threads: usize) -> (u64, u128) {
let counter = Arc::new(AtomicU64::new(0));
@@ -114,11 +116,15 @@ fn bench_storm_smarm(threads: usize) -> (u64, u128) {
cc.fetch_add(1, Ordering::Relaxed);
}));
}
for h in handles { h.join().unwrap(); }
for h in handles {
h.join().unwrap();
}
// Tear down background.
s2.store(true, Ordering::Relaxed);
for h in bg_handles { h.join().unwrap(); }
for h in bg_handles {
h.join().unwrap();
}
});
(counter.load(Ordering::Relaxed), start.elapsed().as_micros())
}
@@ -129,7 +135,9 @@ fn bench_storm_tokio_current() -> (u64, u128) {
let c2 = counter.clone();
let s2 = stop.clone();
let rt = tokio::runtime::Builder::new_current_thread().build().unwrap();
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 {
@@ -149,9 +157,13 @@ fn bench_storm_tokio_current() -> (u64, u128) {
cc.fetch_add(1, Ordering::Relaxed);
}));
}
for h in handles { let _ = h.await; }
for h in handles {
let _ = h.await;
}
s2.store(true, Ordering::Relaxed);
for h in bg_handles { let _ = h.await; }
for h in bg_handles {
let _ = h.await;
}
});
(counter.load(Ordering::Relaxed), start.elapsed().as_micros())
}
@@ -184,9 +196,13 @@ fn bench_storm_tokio_multi() -> (u64, u128) {
cc.fetch_add(1, Ordering::Relaxed);
}));
}
for h in handles { let _ = h.await; }
for h in handles {
let _ = h.await;
}
s2.store(true, Ordering::Relaxed);
for h in bg_handles { let _ = h.await; }
for h in bg_handles {
let _ = h.await;
}
});
(counter.load(Ordering::Relaxed), start.elapsed().as_micros())
}
@@ -219,14 +235,21 @@ fn bench_mpsc_smarm(threads: usize) -> (u64, u128) {
}
let _ = count; // discard; run() closure must return ()
});
for h in prod_handles { h.join().unwrap(); }
for h in prod_handles {
h.join().unwrap();
}
let _ = consumer.join().unwrap();
});
(MPSC_PRODUCERS * MPSC_PER_PRODUCER, start.elapsed().as_micros())
(
MPSC_PRODUCERS * MPSC_PER_PRODUCER,
start.elapsed().as_micros(),
)
}
fn bench_mpsc_tokio_current() -> (u64, u128) {
let rt = tokio::runtime::Builder::new_current_thread().build().unwrap();
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 {
@@ -248,10 +271,15 @@ fn bench_mpsc_tokio_current() -> (u64, u128) {
}
count
});
for h in prod_handles { let _ = h.await; }
for h in prod_handles {
let _ = h.await;
}
let _ = consumer.await;
});
(MPSC_PRODUCERS * MPSC_PER_PRODUCER, start.elapsed().as_micros())
(
MPSC_PRODUCERS * MPSC_PER_PRODUCER,
start.elapsed().as_micros(),
)
}
fn bench_mpsc_tokio_multi() -> (u64, u128) {
@@ -279,10 +307,15 @@ fn bench_mpsc_tokio_multi() -> (u64, u128) {
}
count
});
for h in prod_handles { let _ = h.await; }
for h in prod_handles {
let _ = h.await;
}
let _ = consumer.await;
});
(MPSC_PRODUCERS * MPSC_PER_PRODUCER, start.elapsed().as_micros())
(
MPSC_PRODUCERS * MPSC_PER_PRODUCER,
start.elapsed().as_micros(),
)
}
// ---------------------------------------------------------------------------
@@ -308,7 +341,9 @@ fn bench_timers_smarm(threads: usize) -> (u64, u128) {
smarm::sleep(Duration::from_millis(ms));
}));
}
for h in handles { h.join().unwrap(); }
for h in handles {
h.join().unwrap();
}
});
(TIMER_ACTORS, start.elapsed().as_micros())
}
@@ -328,7 +363,9 @@ fn bench_timers_tokio_current() -> (u64, u128) {
tokio::time::sleep(Duration::from_millis(ms)).await;
}));
}
for h in handles { let _ = h.await; }
for h in handles {
let _ = h.await;
}
});
(TIMER_ACTORS, start.elapsed().as_micros())
}
@@ -348,7 +385,9 @@ fn bench_timers_tokio_multi() -> (u64, u128) {
tokio::time::sleep(Duration::from_millis(ms)).await;
}));
}
for h in handles { let _ = h.await; }
for h in handles {
let _ = h.await;
}
});
(TIMER_ACTORS, start.elapsed().as_micros())
}
@@ -361,11 +400,22 @@ const SCALING_N: u64 = 400_000;
const SCALING_WORKERS: u64 = 64;
fn is_prime(n: u64) -> bool {
if n < 2 { return false; }
if n < 4 { return true; }
if n % 2 == 0 { return false; }
if n < 2 {
return false;
}
if n < 4 {
return true;
}
if n % 2 == 0 {
return false;
}
let mut i = 3u64;
while i * i <= n { if n % i == 0 { return false; } i += 2; }
while i * i <= n {
if n % i == 0 {
return false;
}
i += 2;
}
true
}
@@ -376,7 +426,11 @@ fn count_primes(lo: u64, hi: u64) -> u64 {
fn scaling_slice(w: u64) -> (u64, u64) {
let per = SCALING_N / SCALING_WORKERS;
let lo = w * per;
let hi = if w + 1 == SCALING_WORKERS { SCALING_N } else { lo + per };
let hi = if w + 1 == SCALING_WORKERS {
SCALING_N
} else {
lo + per
};
(lo, hi)
}
@@ -393,7 +447,9 @@ fn bench_scaling_smarm(threads: usize) -> (u64, u128) {
tc.fetch_add(count_primes(lo, hi), Ordering::Relaxed);
}));
}
for h in handles { h.join().unwrap(); }
for h in handles {
h.join().unwrap();
}
});
(total.load(Ordering::Relaxed), start.elapsed().as_micros())
}
@@ -415,7 +471,9 @@ fn bench_scaling_tokio_multi(threads: usize) -> (u64, u128) {
tc.fetch_add(count_primes(lo, hi), Ordering::Relaxed);
}));
}
for h in handles { let _ = h.await; }
for h in handles {
let _ = h.await;
}
});
(total.load(Ordering::Relaxed), start.elapsed().as_micros())
}
@@ -424,7 +482,6 @@ fn bench_scaling_tokio_multi(threads: usize) -> (u64, u128) {
// main
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// Knob helper — reads SMARM_ALLOC_INTERVAL / SMARM_TIMESLICE_CYCLES env vars
// so the sweep script can override the preemption knobs without recompiling.
@@ -433,10 +490,14 @@ fn bench_scaling_tokio_multi(threads: usize) -> (u64, u128) {
fn bench_cfg(threads: usize) -> smarm::runtime::Config {
let mut cfg = smarm::runtime::Config::exact(threads);
if let Ok(v) = std::env::var("SMARM_ALLOC_INTERVAL") {
if let Ok(n) = v.parse::<u32>() { cfg = cfg.alloc_interval(n); }
if let Ok(n) = v.parse::<u32>() {
cfg = cfg.alloc_interval(n);
}
}
if let Ok(v) = std::env::var("SMARM_TIMESLICE_CYCLES") {
if let Ok(n) = v.parse::<u64>() { cfg = cfg.timeslice_cycles(n); }
if let Ok(n) = v.parse::<u64>() {
cfg = cfg.timeslice_cycles(n);
}
}
cfg
}
@@ -446,7 +507,10 @@ fn main() {
println!("smarm tokio-favored benchmarks");
println!("available parallelism: {n} threads");
let sets = env_sets();
println!("ITERS={ITERS}×{sets} sets = {} samples (+1 warmup, discarded)", ITERS * sets);
println!(
"ITERS={ITERS}×{sets} sets = {} samples (+1 warmup, discarded)",
ITERS * sets
);
println!(
"STORM_BACKGROUND={STORM_BACKGROUND}, STORM_SPAWN={STORM_SPAWN}, \
MPSC={MPSC_PRODUCERS}×{MPSC_PER_PRODUCER}, \
@@ -477,7 +541,9 @@ fn main() {
"many_timers: {TIMER_ACTORS} actors sleeping {TIMER_MIN_MS}–{TIMER_MAX_MS} ms"
));
run_n("smarm 1-thread", ITERS, || bench_timers_smarm(1));
run_n(&format!("smarm {n}-thread"), ITERS, || bench_timers_smarm(n));
run_n(&format!("smarm {n}-thread"), ITERS, || {
bench_timers_smarm(n)
});
run_n("tokio current_thread", ITERS, bench_timers_tokio_current);
run_n("tokio multi-thread", ITERS, bench_timers_tokio_multi);
@@ -487,13 +553,19 @@ fn main() {
));
let sweep: Vec<usize> = {
let mut v = vec![1usize, 2, 4];
if n > 4 && !v.contains(&n) { v.push(n); }
if n > 4 && !v.contains(&n) {
v.push(n);
}
v.into_iter().filter(|t| *t <= n).collect()
};
for t in &sweep {
run_n(&format!("smarm {t}-thread"), ITERS, || bench_scaling_smarm(*t));
run_n(&format!("smarm {t}-thread"), ITERS, || {
bench_scaling_smarm(*t)
});
}
for t in &sweep {
run_n(&format!("tokio multi {t}-thread"), ITERS, || bench_scaling_tokio_multi(*t));
run_n(&format!("tokio multi {t}-thread"), ITERS, || {
bench_scaling_tokio_multi(*t)
});
}
}