Files
2026-08-01 16:11:49 +03:00

214 lines
5.7 KiB
Rust

//! Benchmarks for asynchronous Semaphore implementations
use criterion::{criterion_group, criterion_main, Benchmark, Criterion};
use futures_intrusive::sync::{
Semaphore as IntrusiveSemaphore,
SemaphoreReleaser as IntrusiveSemaphoreReleaser,
};
use tokio::sync::{
Semaphore as TokioSemaphore, SemaphorePermit as TokioSemaphorePermit,
};
use std::future::Future;
use std::sync::Arc;
use std::time::Duration;
mod utils;
use utils::Yield;
/// How often each task should acquire the semaphore
const NR_ACQUIRES: usize = 50;
/// How many tasks are used
const TASKS: usize = 200;
/// The amount of available permits when we are testing strong contention
const CONTENTION_PERMITS: usize = 100;
/// The amount of available permits when testing light contention
const NORMAL_PERMITS: usize = 180;
/// The amount of available permits when testing no contention
const UNCONTENDED_PERMITS: usize = TASKS;
/// The number of yields we perform after the Semaphore was acquired
const NR_YIELDS: usize = 4;
/// Extension trait to add support for `block_on` for runtimes which not
/// natively support it as member function
trait Block {
fn block_on<F: Future<Output = ()>>(&self, f: F);
}
fn create_intrusive_fair_semaphore(permits: usize) -> IntrusiveSemaphore {
IntrusiveSemaphore::new(true, permits)
}
fn create_intrusive_unfair_semaphore(permits: usize) -> IntrusiveSemaphore {
IntrusiveSemaphore::new(false, permits)
}
fn create_tokio_semaphore(permits: usize) -> TokioSemaphore {
TokioSemaphore::new(permits)
}
async fn acquire_intrusive_semaphore(
sem: &IntrusiveSemaphore,
) -> IntrusiveSemaphoreReleaser<'_> {
sem.acquire(1).await
}
async fn acquire_tokio_semaphore(
sem: &TokioSemaphore,
) -> TokioSemaphorePermit<'_> {
sem.acquire().await.unwrap()
}
macro_rules! run_with_semaphore {
(
$nr_tasks: expr,
$nr_iterations: expr,
$nr_permits: expr,
$spawn_fn: expr,
$create_semaphore_fn: ident,
$acquire_fn: ident,
) => {
let semaphore = Arc::new($create_semaphore_fn($nr_permits));
let mut tasks = Vec::new();
let sem = Arc::new(IntrusiveSemaphore::new(false, 0));
for _ in 0..$nr_tasks {
let semaphore = semaphore.clone();
let s = sem.clone();
tasks.push($spawn_fn(async move {
for _count in 0..$nr_iterations {
let _releaser = $acquire_fn(&*semaphore).await;
Yield::new(NR_YIELDS).await;
}
s.release(1);
}));
}
sem.acquire($nr_tasks).await;
};
}
macro_rules! bench {
(
$b: ident,
$rt_setup: expr,
$spawn_fn: expr,
$nr_iterations: expr,
$nr_permits: expr,
$create_semaphore_fn: ident,
$acquire_fn: ident,
) => {
#[allow(unused_mut)] // mut is only required for some runtimes
let mut rt = $rt_setup;
$b.iter(|| {
rt.block_on(async {
run_with_semaphore!(
TASKS,
$nr_iterations,
$nr_permits,
$spawn_fn,
$create_semaphore_fn,
$acquire_fn,
);
})
});
};
}
macro_rules! benchmarks {
(
$c: ident,
$rt_name: literal,
$rt_setup: expr,
$spawn_fn: expr,
$semaphore_name: literal,
$create_semaphore_fn: ident,
$acquire_fn: ident,
) => {
$c.bench(
concat!($rt_name, "/", $semaphore_name),
Benchmark::new("heavy contention", |b| {
bench!(
b,
$rt_setup,
$spawn_fn,
NR_ACQUIRES,
CONTENTION_PERMITS,
$create_semaphore_fn,
$acquire_fn,
);
})
.with_function("normal contention", |b| {
bench!(
b,
$rt_setup,
$spawn_fn,
NR_ACQUIRES,
NORMAL_PERMITS,
$create_semaphore_fn,
$acquire_fn,
);
})
.with_function("no contention", |b| {
bench!(
b,
$rt_setup,
$spawn_fn,
NR_ACQUIRES,
UNCONTENDED_PERMITS,
$create_semaphore_fn,
$acquire_fn,
);
}),
);
};
}
fn tokio_rt_intrusive_fair_benchmarks(c: &mut Criterion) {
benchmarks!(
c,
"tokio_rt",
tokio::runtime::Runtime::new().unwrap(),
tokio::spawn,
"futures_intrusive(fair=true)",
create_intrusive_fair_semaphore,
acquire_intrusive_semaphore,
);
}
fn tokio_rt_intrusive_unfair_benchmarks(c: &mut Criterion) {
benchmarks!(
c,
"tokio_rt",
tokio::runtime::Runtime::new().unwrap(),
tokio::spawn,
"futures_intrusive(fair=false)",
create_intrusive_unfair_semaphore,
acquire_intrusive_semaphore,
);
}
fn tokio_rt_tokio_benchmarks(c: &mut Criterion) {
benchmarks!(
c,
"tokio_rt",
tokio::runtime::Runtime::new().unwrap(),
tokio::spawn,
"tokio",
create_tokio_semaphore,
acquire_tokio_semaphore,
);
}
criterion_group! {
name = benches;
config = Criterion::default().measurement_time(Duration::from_secs(10));
targets =
tokio_rt_intrusive_fair_benchmarks,
tokio_rt_intrusive_unfair_benchmarks,
tokio_rt_tokio_benchmarks,
}
criterion_main!(benches);