use affinitypool::Threadpool;
use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
use std::hint::black_box;
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::runtime::Builder as TokioBuilder;
fn affinitypool_runtime() -> tokio::runtime::Runtime {
TokioBuilder::new_current_thread().enable_all().build().unwrap()
}
fn tokio_runtime(blocking_threads: usize) -> tokio::runtime::Runtime {
TokioBuilder::new_current_thread()
.enable_all()
.max_blocking_threads(blocking_threads)
.build()
.unwrap()
}
#[inline(never)]
fn cpu_task(iters: usize) -> usize {
let mut acc: usize = black_box(0);
for i in 0..iters {
acc = black_box(acc).wrapping_add(black_box(i).wrapping_mul(17));
}
black_box(acc)
}
fn bench_burst_drain(c: &mut Criterion) {
let mut group = c.benchmark_group("burst_drain");
group.sample_size(10);
group.measurement_time(Duration::from_secs(15));
for &count in &[100_000usize, 1_000_000] {
group.throughput(Throughput::Elements(count as u64));
let workers = 4usize;
group.bench_with_input(BenchmarkId::new("affinitypool", count), &count, |b, &count| {
let rt = affinitypool_runtime();
b.iter_custom(|iters| {
rt.block_on(async move {
let pool = Threadpool::new(workers);
let _ = pool.spawn(|| 0u32).await;
let mut total = Duration::ZERO;
for _ in 0..iters {
let start = Instant::now();
let mut handles = Vec::with_capacity(count);
for i in 0..count {
handles.push(pool.spawn(move || black_box(i)));
}
for h in handles {
black_box(h.await);
}
total += start.elapsed();
}
total
})
});
});
group.bench_with_input(BenchmarkId::new("tokio", count), &count, |b, &count| {
let rt = tokio_runtime(workers);
b.iter_custom(|iters| {
rt.block_on(async move {
let _ = tokio::task::spawn_blocking(|| 0u32).await;
let mut total = Duration::ZERO;
for _ in 0..iters {
let start = Instant::now();
let mut handles = Vec::with_capacity(count);
for i in 0..count {
handles.push(tokio::task::spawn_blocking(move || black_box(i)));
}
for h in handles {
black_box(h.await.unwrap());
}
total += start.elapsed();
}
total
})
});
});
}
group.finish();
}
fn bench_sustained_throughput(c: &mut Criterion) {
let mut group = c.benchmark_group("sustained_throughput");
const BATCH: usize = 100_000;
group.throughput(Throughput::Elements(BATCH as u64));
group.measurement_time(Duration::from_secs(20));
group.sample_size(20);
let workers = 4usize;
group.bench_function("affinitypool", |b| {
let rt = affinitypool_runtime();
b.iter_custom(|iters| {
rt.block_on(async move {
let pool = Threadpool::new(workers);
let _ = pool.spawn(|| 0u32).await;
let mut total = Duration::ZERO;
for _ in 0..iters {
let start = Instant::now();
let mut handles = Vec::with_capacity(BATCH);
for i in 0..BATCH {
handles.push(pool.spawn(move || black_box(i)));
}
for h in handles {
black_box(h.await);
}
total += start.elapsed();
}
total
})
});
});
group.bench_function("tokio", |b| {
let rt = tokio_runtime(workers);
b.iter_custom(|iters| {
rt.block_on(async move {
let _ = tokio::task::spawn_blocking(|| 0u32).await;
let mut total = Duration::ZERO;
for _ in 0..iters {
let start = Instant::now();
let mut handles = Vec::with_capacity(BATCH);
for i in 0..BATCH {
handles.push(tokio::task::spawn_blocking(move || black_box(i)));
}
for h in handles {
black_box(h.await.unwrap());
}
total += start.elapsed();
}
total
})
});
});
group.finish();
}
fn bench_realistic_cost(c: &mut Criterion) {
let mut group = c.benchmark_group("realistic_cost");
group.measurement_time(Duration::from_secs(15));
group.sample_size(15);
let count = 10_000usize;
let workers = 4usize;
group.throughput(Throughput::Elements(count as u64));
for &cost in &[0usize, 100, 1_000, 10_000, 100_000] {
group.bench_with_input(BenchmarkId::new("affinitypool", cost), &cost, |b, &cost| {
let rt = affinitypool_runtime();
b.iter_custom(|iters| {
rt.block_on(async move {
let pool = Threadpool::new(workers);
let _ = pool.spawn(move || cpu_task(cost)).await;
let mut total = Duration::ZERO;
for _ in 0..iters {
let start = Instant::now();
let mut handles = Vec::with_capacity(count);
for _ in 0..count {
handles.push(pool.spawn(move || cpu_task(cost)));
}
for h in handles {
black_box(h.await);
}
total += start.elapsed();
}
total
})
});
});
group.bench_with_input(BenchmarkId::new("tokio", cost), &cost, |b, &cost| {
let rt = tokio_runtime(workers);
b.iter_custom(|iters| {
rt.block_on(async move {
let _ = tokio::task::spawn_blocking(move || cpu_task(cost)).await;
let mut total = Duration::ZERO;
for _ in 0..iters {
let start = Instant::now();
let mut handles = Vec::with_capacity(count);
for _ in 0..count {
handles.push(tokio::task::spawn_blocking(move || cpu_task(cost)));
}
for h in handles {
black_box(h.await.unwrap());
}
total += start.elapsed();
}
total
})
});
});
}
group.finish();
}
fn bench_concurrent_pipeline(c: &mut Criterion) {
let mut group = c.benchmark_group("concurrent_pipeline");
const PRODUCERS: usize = 8;
const PER_PRODUCER: usize = 100_000;
const TOTAL: usize = PRODUCERS * PER_PRODUCER;
group.throughput(Throughput::Elements(TOTAL as u64));
group.measurement_time(Duration::from_secs(20));
group.sample_size(10);
let workers = 8usize;
group.bench_function("affinitypool", |b| {
let rt = TokioBuilder::new_multi_thread()
.worker_threads(PRODUCERS)
.enable_all()
.build()
.unwrap();
b.iter_custom(|iters| {
rt.block_on(async move {
let pool = Arc::new(Threadpool::new(workers));
let _ = pool.spawn(|| 0u32).await;
let mut total = Duration::ZERO;
for _ in 0..iters {
let start = Instant::now();
let mut producers = Vec::with_capacity(PRODUCERS);
for _ in 0..PRODUCERS {
let pool = pool.clone();
producers.push(tokio::spawn(async move {
let mut hs = Vec::with_capacity(PER_PRODUCER);
for i in 0..PER_PRODUCER {
hs.push(pool.spawn(move || black_box(i)));
}
for h in hs {
black_box(h.await);
}
}));
}
for p in producers {
p.await.unwrap();
}
total += start.elapsed();
}
total
})
});
});
group.bench_function("tokio", |b| {
let rt = TokioBuilder::new_multi_thread()
.worker_threads(PRODUCERS)
.max_blocking_threads(workers)
.enable_all()
.build()
.unwrap();
b.iter_custom(|iters| {
rt.block_on(async move {
let _ = tokio::task::spawn_blocking(|| 0u32).await;
let mut total = Duration::ZERO;
for _ in 0..iters {
let start = Instant::now();
let mut producers = Vec::with_capacity(PRODUCERS);
for _ in 0..PRODUCERS {
producers.push(tokio::spawn(async move {
let mut hs = Vec::with_capacity(PER_PRODUCER);
for i in 0..PER_PRODUCER {
hs.push(tokio::task::spawn_blocking(move || black_box(i)));
}
for h in hs {
black_box(h.await.unwrap());
}
}));
}
for p in producers {
p.await.unwrap();
}
total += start.elapsed();
}
total
})
});
});
group.finish();
}
criterion_group!(
high_load,
bench_burst_drain,
bench_sustained_throughput,
bench_realistic_cost,
bench_concurrent_pipeline
);
criterion_main!(high_load);