use std::pin::pin;
use std::task::{Context, Waker};
use std::time::Instant;
use alloc_tracker::Allocator;
use arty_executor::CycleOutcome;
use arty_executor::testing::new_guarded_executor;
use criterion::measurement::WallTime;
use criterion::{BenchmarkGroup, Criterion, criterion_group, criterion_main};
use testing_aids::YieldFuture;
criterion_group!(benches, entrypoint);
criterion_main!(benches);
#[global_allocator]
static ALLOCATOR: Allocator<std::alloc::System> = Allocator::system();
fn entrypoint(c: &mut Criterion) {
let allocs = alloc_tracker::Session::new();
let time = all_the_time::Session::new();
let mut group = c.benchmark_group("ae_basic_operations/basic");
bench_spawn_and_complete_one(&mut group, &allocs, &time);
bench_yield_one(&mut group, &allocs, &time);
bench_noop(&mut group, &allocs, &time);
group.finish();
let mut group = c.benchmark_group("ae_basic_operations/slow");
bench_spawn_and_complete_one_times_many(&mut group, &allocs, &time);
bench_spawn_and_complete_10k(&mut group, &allocs, &time);
bench_yield_10k(&mut group, &allocs, &time);
group.finish();
}
fn bench_spawn_and_complete_one(group: &mut BenchmarkGroup<'_, WallTime>, allocs: &alloc_tracker::Session, time: &all_the_time::Session) {
const NAME: &str = "spawn_and_complete_one";
let executor = new_guarded_executor(Waker::noop().clone());
let tasks = executor.tasks();
let body: Box<dyn Fn()> = Box::new({
move || {
let mut join_handle = pin!(tasks.add(async move {}));
assert_ne!(executor.execute_cycle(), CycleOutcome::Shutdown);
let mut cx = Context::from_waker(Waker::noop());
let result = join_handle.as_mut().poll(&mut cx);
assert!(result.is_ready());
}
});
body();
let allocs_op = allocs.operation(NAME);
let time_op = time.operation(NAME);
group.bench_function(NAME, |b| {
b.iter_custom(|iters| {
let _allocs_span = allocs_op.measure_thread().iterations(iters);
let _time_span = time_op.measure_thread().iterations(iters);
let start = Instant::now();
for _ in 0..iters {
body();
}
start.elapsed()
});
});
}
fn bench_spawn_and_complete_one_times_many(
group: &mut BenchmarkGroup<'_, WallTime>,
allocs: &alloc_tracker::Session,
time: &all_the_time::Session,
) {
const NAME: &str = "spawn_and_complete_one_times_many";
const MANY: usize = 1000;
let executor = new_guarded_executor(Waker::noop().clone());
let tasks = executor.tasks();
let body: Box<dyn Fn()> = Box::new({
move || {
for _ in 0..MANY {
let mut join_handle = pin!(tasks.add(async move {}));
assert_ne!(executor.execute_cycle(), CycleOutcome::Shutdown);
let mut cx = Context::from_waker(Waker::noop());
let result = join_handle.as_mut().poll(&mut cx);
assert!(result.is_ready());
}
}
});
body();
let allocs_op = allocs.operation(NAME);
let time_op = time.operation(NAME);
group.bench_function(NAME, |b| {
b.iter_custom(|iters| {
let _allocs_span = allocs_op.measure_thread().iterations(iters);
let _time_span = time_op.measure_thread().iterations(iters);
let start = Instant::now();
for _ in 0..iters {
body();
}
start.elapsed()
});
});
}
fn bench_spawn_and_complete_10k(group: &mut BenchmarkGroup<'_, WallTime>, allocs: &alloc_tracker::Session, time: &all_the_time::Session) {
const NAME: &str = "spawn_and_complete_10k";
let executor = new_guarded_executor(Waker::noop().clone());
let tasks = executor.tasks();
let mut join_handles = Vec::with_capacity(10_000);
let mut body: Box<dyn FnMut()> = Box::new({
move || {
join_handles.clear();
for _ in 0..10_000 {
join_handles.push(tasks.add(async move {}));
}
assert_ne!(executor.execute_cycle(), CycleOutcome::Shutdown);
let mut cx = Context::from_waker(Waker::noop());
for join_handle in &mut join_handles {
let mut join_handle = pin!(join_handle);
let result = join_handle.as_mut().poll(&mut cx);
assert!(result.is_ready());
}
}
});
body();
let allocs_op = allocs.operation(NAME);
let time_op = time.operation(NAME);
group.bench_function(NAME, |b| {
b.iter_custom(|iters| {
let _allocs_span = allocs_op.measure_thread().iterations(iters);
let _time_span = time_op.measure_thread().iterations(iters);
let start = Instant::now();
for _ in 0..iters {
body();
}
start.elapsed()
});
});
}
fn bench_yield_one(group: &mut BenchmarkGroup<'_, WallTime>, allocs: &alloc_tracker::Session, time: &all_the_time::Session) {
const NAME: &str = "yield_one";
let executor = new_guarded_executor(Waker::noop().clone());
let tasks = executor.tasks();
let body: Box<dyn Fn()> = Box::new({
move || {
let mut join_handle = pin!(tasks.add(YieldFuture::default()));
assert_eq!(executor.execute_cycle(), CycleOutcome::Continue);
assert_eq!(executor.execute_cycle(), CycleOutcome::Suspend);
let mut cx = Context::from_waker(Waker::noop());
let result = join_handle.as_mut().poll(&mut cx);
assert!(result.is_ready());
}
});
body();
let allocs_op = allocs.operation(NAME);
let time_op = time.operation(NAME);
group.bench_function(NAME, |b| {
b.iter_custom(|iters| {
let _allocs_span = allocs_op.measure_thread().iterations(iters);
let _time_span = time_op.measure_thread().iterations(iters);
let start = Instant::now();
for _ in 0..iters {
body();
}
start.elapsed()
});
});
}
fn bench_yield_10k(group: &mut BenchmarkGroup<'_, WallTime>, allocs: &alloc_tracker::Session, time: &all_the_time::Session) {
const NAME: &str = "yield_10k";
let executor = new_guarded_executor(Waker::noop().clone());
let tasks = executor.tasks();
let mut join_handles = Vec::with_capacity(10_000);
let mut body: Box<dyn FnMut()> = Box::new({
move || {
join_handles.clear();
for _ in 0..10_000 {
join_handles.push(tasks.add(YieldFuture::default()));
}
assert_eq!(executor.execute_cycle(), CycleOutcome::Continue);
assert_eq!(executor.execute_cycle(), CycleOutcome::Suspend);
let mut cx = Context::from_waker(Waker::noop());
for join_handle in &mut join_handles {
let mut join_handle = pin!(join_handle);
let result = join_handle.as_mut().poll(&mut cx);
assert!(result.is_ready());
}
}
});
body();
let allocs_op = allocs.operation(NAME);
let time_op = time.operation(NAME);
group.bench_function(NAME, |b| {
b.iter_custom(|iters| {
let _allocs_span = allocs_op.measure_thread().iterations(iters);
let _time_span = time_op.measure_thread().iterations(iters);
let start = Instant::now();
for _ in 0..iters {
body();
}
start.elapsed()
});
});
}
fn bench_noop(group: &mut BenchmarkGroup<'_, WallTime>, allocs: &alloc_tracker::Session, time: &all_the_time::Session) {
const NAME: &str = "noop";
let executor = new_guarded_executor(Waker::noop().clone());
let body: Box<dyn Fn()> = Box::new({
move || {
assert_eq!(executor.execute_cycle(), CycleOutcome::Suspend);
}
});
body();
let allocs_op = allocs.operation(NAME);
let time_op = time.operation(NAME);
group.bench_function(NAME, |b| {
b.iter_custom(|iters| {
let _allocs_span = allocs_op.measure_thread().iterations(iters);
let _time_span = time_op.measure_thread().iterations(iters);
let start = Instant::now();
for _ in 0..iters {
body();
}
start.elapsed()
});
});
}