use std::sync::Arc;
use crew::{Crew, Round};
use criterion::{
BenchmarkGroup, Criterion, Throughput, criterion_group, criterion_main, measurement::WallTime,
};
use futures::executor::block_on;
use zruntime::{LocalRuntime, lock::Mutex};
#[path = "common/channel.rs"]
mod channel;
#[path = "common/cpus.rs"]
mod cpus;
#[path = "common/crew.rs"]
mod crew;
#[path = "common/rounds.rs"]
mod rounds;
fn channel_benches(c: &mut Criterion) {
let mut group = c.benchmark_group("mpmc");
group.sample_size(10);
let crews = vec![
bench_threads(&mut group, "spsc-cap16", 1, 1, Some(16)),
bench_threads(&mut group, "spsc-cap1024", 1, 1, Some(1024)),
bench_threads(&mut group, "mpsc-4to1-cap16", 4, 1, Some(16)),
bench_threads(&mut group, "mpmc-4to4-cap16", 4, 4, Some(16)),
bench_threads(&mut group, "mpmc-4to4-unbounded", 4, 4, None),
];
let runtime = LocalRuntime::new().unwrap();
bench_tasks(&mut group, &runtime, "tasks-mpsc-4to1-cap16", 4, 1, 16);
bench_tasks(&mut group, &runtime, "tasks-mpmc-4to4-cap16", 4, 4, 16);
group.finish();
for crew in crews {
crew.join();
}
}
fn bench_threads(
group: &mut BenchmarkGroup<'_, WallTime>,
id: &str,
senders: usize,
receivers: usize,
cap: Option<usize>,
) -> Crew {
let share = rounds::receiver_share(senders, receivers);
group.throughput(Throughput::Elements(senders as u64 * rounds::MESSAGES));
let (s, r) = channel::new(cap);
let sender_rounds = (0..senders).map(|_| {
let s = s.clone();
Box::new(move || block_on(rounds::send_n(&s, rounds::MESSAGES))) as Round
});
let receiver_rounds = (0..receivers).map(|_| {
let r = r.clone();
Box::new(move || block_on(rounds::recv_n(&r, share))) as Round
});
let crew = Crew::spawn(sender_rounds.chain(receiver_rounds));
group.bench_function(id, |b| b.iter_custom(|iters| crew.time(iters)));
crew
}
fn bench_tasks(
group: &mut BenchmarkGroup<'_, WallTime>,
runtime: &LocalRuntime,
id: &str,
senders: usize,
receivers: usize,
cap: usize,
) {
let share = rounds::receiver_share(senders, receivers);
group.throughput(Throughput::Elements(senders as u64 * rounds::MESSAGES));
let (s, r) = channel::new(Some(cap));
let mut idle_senders: Vec<_> = (0..senders).map(|_| s.clone()).collect();
let mut idle_receivers: Vec<_> = (0..receivers).map(|_| r.clone()).collect();
group.bench_function(id, |b| {
b.iter(|| {
runtime.block_on(async {
let sender_tasks: Vec<_> = idle_senders
.drain(..)
.map(|s| {
runtime.spawn("sender", async move {
rounds::send_n(&s, rounds::MESSAGES).await;
s
})
})
.collect();
let receiver_tasks: Vec<_> = idle_receivers
.drain(..)
.map(|r| {
runtime.spawn("receiver", async move {
rounds::recv_n(&r, share).await;
r
})
})
.collect();
for task in sender_tasks {
idle_senders.push(task.await.unwrap());
}
for task in receiver_tasks {
idle_receivers.push(task.await.unwrap());
}
})
})
});
}
fn mutex_bench(c: &mut Criterion) {
let lock = Arc::new(Mutex::new(()));
let crew = Crew::spawn((0..MUTEX_THREADS).map(|_| {
let lock = lock.clone();
Box::new(move || {
for _ in 0..MUTEX_ROUNDS {
drop(block_on(lock.lock()));
}
}) as Round
}));
let mut group = c.benchmark_group("event");
group.sample_size(10);
group.throughput(Throughput::Elements((MUTEX_THREADS * MUTEX_ROUNDS) as u64));
group.bench_function("mutex/4-threads", |b| {
b.iter_custom(|iters| crew.time(iters))
});
group.finish();
crew.join();
}
const MUTEX_THREADS: usize = 4;
const MUTEX_ROUNDS: usize = 1000;
criterion_group!(benches, channel_benches, mutex_bench);
criterion_main!(benches);