use std::hint::black_box;
use std::time::Duration;
use almost_enough::{
ChildStopper, FnStop, OrStop, Stop, StopExt, StopReason, StopSource, Stopper, SyncStopper,
TimeoutExt, Unstoppable, WithTimeout,
};
const HOT_LOOP_ITERS: usize = 10_000;
const CHECK_INTERVAL: usize = 64;
#[inline(always)]
fn trivial_work(i: usize) -> usize {
black_box(i.wrapping_mul(2654435761))
}
#[inline(never)]
fn check_generic(stop: &impl Stop) -> Result<(), StopReason> {
stop.check()
}
#[inline(never)]
fn check_dyn(stop: &dyn Stop) -> Result<(), StopReason> {
stop.check()
}
fn main() {
let result = zenbench::run(|suite| {
suite.compare("check", |group| {
group.config().sort_by_speed(true).cache_firewall(false);
group.baseline("unstoppable");
group.bench("unstoppable", |b| {
let stop = Unstoppable;
b.iter(|| black_box(&stop).check())
});
group.bench("stop_source", |b| {
let stop = StopSource::new();
b.iter(|| black_box(&stop).check())
});
group.bench("stop_ref", |b| {
let source = StopSource::new();
let stop = source.as_ref();
b.iter(|| black_box(&stop).check())
});
group.bench("stopper", |b| {
let stop = Stopper::new();
b.iter(|| black_box(&stop).check())
});
group.bench("sync_stopper", |b| {
let stop = SyncStopper::new();
b.iter(|| black_box(&stop).check())
});
group.bench("fn_stop", |b| {
let stop = FnStop::new(|| false);
b.iter(|| black_box(&stop).check())
});
group.bench("boxed_stopper", |b| {
let stop = Stopper::new().into_boxed();
b.iter(|| black_box(&stop).check())
});
group.bench("dyn_stopper", |b| {
let stop = Stopper::new().into_token();
b.iter(|| black_box(&stop).check())
});
group.bench("or_stop", |b| {
let a = StopSource::new();
let b_src = StopSource::new();
let stop: OrStop<_, _> = a.as_ref().or(b_src.as_ref());
b.iter(|| black_box(&stop).check())
});
group.bench("child_depth_1", |b| {
let parent = ChildStopper::new();
let stop = parent.child();
b.iter(|| black_box(&stop).check())
});
group.bench("child_depth_3", |b| {
let g0 = ChildStopper::new();
let g1 = g0.child();
let g2 = g1.child();
let stop = g2.child();
b.iter(|| black_box(&stop).check())
});
group.bench("with_timeout", |b| {
let source = StopSource::new();
let stop: WithTimeout<_> = source.as_ref().with_timeout(Duration::from_secs(3600));
b.iter(|| black_box(&stop).check())
});
});
suite.compare("dispatch", |group| {
group.config().sort_by_speed(true).cache_firewall(false);
group.baseline("stopper_generic");
group.bench("stopper_generic", |b| {
let stop = Stopper::new();
b.iter(|| check_generic(black_box(&stop)))
});
group.bench("stopper_dyn", |b| {
let stop = Stopper::new();
b.iter(|| check_dyn(black_box(&stop)))
});
group.bench("stopper_dynstop", |b| {
let stop = Stopper::new().into_token();
b.iter(|| black_box(&stop).check())
});
group.bench("stopper_dynstop_as_dyn", |b| {
let stop = Stopper::new().into_token();
b.iter(|| check_dyn(black_box(&stop) as &dyn Stop))
});
group.bench("unstoppable_generic", |b| {
let stop = Unstoppable;
b.iter(|| check_generic(black_box(&stop)))
});
group.bench("unstoppable_dyn", |b| {
let stop = Unstoppable;
b.iter(|| check_dyn(black_box(&stop)))
});
});
suite.compare("check_cancelled", |group| {
group.config().sort_by_speed(true).cache_firewall(false);
group.baseline("stopper");
group.bench("stopper", |b| {
let stop = Stopper::cancelled();
b.iter(|| black_box(&stop).check())
});
group.bench("sync_stopper", |b| {
let stop = SyncStopper::cancelled();
b.iter(|| black_box(&stop).check())
});
});
suite.compare("hot_loop_stopper", |group| {
group.config().sort_by_speed(true).cache_firewall(false);
group.baseline("impl_stop");
group.throughput(zenbench::Throughput::Elements(HOT_LOOP_ITERS as u64));
group.bench("impl_stop", |b| {
let stop = Stopper::new();
b.iter(|| {
let mut acc = 0usize;
for i in 0..HOT_LOOP_ITERS {
if i % CHECK_INTERVAL == 0 {
let _ = stop.check();
}
acc = acc.wrapping_add(trivial_work(i));
}
black_box(acc)
})
});
group.bench("stoptoken", |b| {
let stop = Stopper::new().into_token();
b.iter(|| {
let mut acc = 0usize;
for i in 0..HOT_LOOP_ITERS {
if i % CHECK_INTERVAL == 0 {
let _ = stop.check();
}
acc = acc.wrapping_add(trivial_work(i));
}
black_box(acc)
})
});
group.bench("dyn_may_stop", |b| {
let stop = Stopper::new();
let stop: &dyn Stop = &stop;
let stop = stop.may_stop().then_some(stop);
b.iter(|| {
let mut acc = 0usize;
for i in 0..HOT_LOOP_ITERS {
if i % CHECK_INTERVAL == 0 {
let _ = stop.check();
}
acc = acc.wrapping_add(trivial_work(i));
}
black_box(acc)
})
});
group.bench("dyn_raw", |b| {
let stop = Stopper::new();
let stop: &dyn Stop = &stop;
b.iter(|| {
let mut acc = 0usize;
for i in 0..HOT_LOOP_ITERS {
if i % CHECK_INTERVAL == 0 {
let _ = stop.check();
}
acc = acc.wrapping_add(trivial_work(i));
}
black_box(acc)
})
});
});
suite.compare("hot_loop_unstoppable", |group| {
group.config().sort_by_speed(true).cache_firewall(false);
group.baseline("impl_stop");
group.throughput(zenbench::Throughput::Elements(HOT_LOOP_ITERS as u64));
group.bench("impl_stop", |b| {
let stop = Unstoppable;
b.iter(|| {
let mut acc = 0usize;
for i in 0..HOT_LOOP_ITERS {
if i % CHECK_INTERVAL == 0 {
let _ = stop.check();
}
acc = acc.wrapping_add(trivial_work(i));
}
black_box(acc)
})
});
group.bench("stoptoken", |b| {
let stop = Unstoppable.into_token();
b.iter(|| {
let mut acc = 0usize;
for i in 0..HOT_LOOP_ITERS {
if i % CHECK_INTERVAL == 0 {
let _ = stop.check();
}
acc = acc.wrapping_add(trivial_work(i));
}
black_box(acc)
})
});
group.bench("dyn_may_stop", |b| {
let stop = Unstoppable;
let stop: &dyn Stop = &stop;
let stop = stop.may_stop().then_some(stop);
b.iter(|| {
let mut acc = 0usize;
for i in 0..HOT_LOOP_ITERS {
if i % CHECK_INTERVAL == 0 {
let _ = stop.check();
}
acc = acc.wrapping_add(trivial_work(i));
}
black_box(acc)
})
});
group.bench("dyn_raw", |b| {
let stop = Unstoppable;
let stop: &dyn Stop = &stop;
b.iter(|| {
let mut acc = 0usize;
for i in 0..HOT_LOOP_ITERS {
if i % CHECK_INTERVAL == 0 {
let _ = stop.check();
}
acc = acc.wrapping_add(trivial_work(i));
}
black_box(acc)
})
});
});
});
if let Err(e) = result.save("stop_check_results.json") {
eprintln!("Failed to save results: {e}");
}
}