#![expect(
clippy::unwrap_used,
reason = "benchmark code — panics on failure are acceptable"
)]
use std::sync::Mutex;
use criterion::{Criterion, black_box, criterion_group, criterion_main};
use aro_core::entity::Entity;
use aro_core::error::RepoError;
use aro_core::repository::{BoxFuture, Repository};
#[derive(Debug, Clone, PartialEq)]
struct Item {
id: u64,
name: String,
}
impl Entity for Item {
type Id = u64;
fn id(&self) -> Self::Id {
self.id
}
}
struct BoxedRepo {
items: Mutex<Vec<Item>>,
}
impl Repository<Item> for BoxedRepo {
fn find_by_id(&self, id: u64) -> BoxFuture<'_, Result<Item, RepoError>> {
Box::pin(async move {
self.items
.lock()
.unwrap()
.iter()
.find(|i| i.id == id)
.cloned()
.ok_or(RepoError::NotFound)
})
}
fn find_all(&self) -> BoxFuture<'_, Result<Vec<Item>, RepoError>> {
Box::pin(async move { Ok(self.items.lock().unwrap().clone()) })
}
fn create(&self, entity: Item) -> BoxFuture<'_, Result<Item, RepoError>> {
Box::pin(async move { Ok(entity) })
}
fn update(&self, entity: Item) -> BoxFuture<'_, Result<Item, RepoError>> {
Box::pin(async move { Ok(entity) })
}
fn delete(&self, _id: u64) -> BoxFuture<'_, Result<(), RepoError>> {
Box::pin(async move { Ok(()) })
}
}
struct DirectRepo {
items: Mutex<Vec<Item>>,
}
impl DirectRepo {
#[expect(
clippy::unused_async,
reason = "async is needed for benchmark comparison with BoxFuture"
)]
async fn find_by_id(&self, id: u64) -> Result<Item, RepoError> {
self.items
.lock()
.unwrap()
.iter()
.find(|i| i.id == id)
.cloned()
.ok_or(RepoError::NotFound)
}
}
fn bench_boxfuture_find_by_id(c: &mut Criterion) {
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
let repo = BoxedRepo {
items: Mutex::new(vec![Item {
id: 1,
name: "Widget".into(),
}]),
};
c.bench_function("BoxFuture find_by_id", |b| {
b.iter(|| {
rt.block_on(async {
let item = repo.find_by_id(black_box(1)).await.unwrap();
black_box(item);
});
});
});
}
fn bench_direct_find_by_id(c: &mut Criterion) {
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
let repo = DirectRepo {
items: Mutex::new(vec![Item {
id: 1,
name: "Widget".into(),
}]),
};
c.bench_function("Direct async find_by_id", |b| {
b.iter(|| {
rt.block_on(async {
let item = repo.find_by_id(black_box(1)).await.unwrap();
black_box(item);
});
});
});
}
fn bench_dyn_dispatch_find_by_id(c: &mut Criterion) {
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
let repo = BoxedRepo {
items: Mutex::new(vec![Item {
id: 1,
name: "Widget".into(),
}]),
};
let dyn_repo: &dyn Repository<Item> = &repo;
c.bench_function("dyn Repository find_by_id", |b| {
b.iter(|| {
rt.block_on(async {
let item = dyn_repo.find_by_id(black_box(1)).await.unwrap();
black_box(item);
});
});
});
}
criterion_group!(
benches,
bench_boxfuture_find_by_id,
bench_direct_find_by_id,
bench_dyn_dispatch_find_by_id
);
criterion_main!(benches);