From 1f8a7540e78f9a5a49fddff0dfee58513ba6859e Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 2 Aug 2025 17:19:58 +0200 Subject: [PATCH] feat(criterion): make `Bencher` implement `Send + Sync` `criterion`'s `Bencher` implements `Send + Sync`, which allows running the benchmark itself on another thread. This allows something like `|b| /* my rayon thread pool */.install(|| b.iter(|| /* bench */))`. --- crates/criterion_compat/src/compat/bencher.rs | 61 +++++++++---------- crates/criterion_compat/src/compat/group.rs | 7 ++- 2 files changed, 33 insertions(+), 35 deletions(-) diff --git a/crates/criterion_compat/src/compat/bencher.rs b/crates/criterion_compat/src/compat/bencher.rs index c50af643..73310e4f 100644 --- a/crates/criterion_compat/src/compat/bencher.rs +++ b/crates/criterion_compat/src/compat/bencher.rs @@ -1,5 +1,3 @@ -use std::{cell::RefCell, rc::Rc}; - use codspeed::codspeed::{black_box, CodSpeed}; use colored::Colorize; use criterion::BatchSize; @@ -10,19 +8,14 @@ use criterion::async_executor::AsyncExecutor; use std::future::Future; pub struct Bencher<'a> { - codspeed: Rc>, + codspeed: &'a mut CodSpeed, uri: String, - _marker: std::marker::PhantomData<&'a ()>, } #[allow(clippy::needless_lifetimes)] impl<'a> Bencher<'a> { - pub fn new(codspeed: Rc>, uri: String) -> Self { - Bencher { - codspeed, - uri, - _marker: std::marker::PhantomData, - } + pub(crate) fn new(codspeed: &'a mut CodSpeed, uri: String) -> Self { + Bencher { codspeed, uri } } #[inline(never)] @@ -30,16 +23,15 @@ impl<'a> Bencher<'a> { where R: FnMut() -> O, { - let mut codspeed = self.codspeed.borrow_mut(); // NOTE: this structure hardens our benchmark against dead code elimination // https://godbolt.org/z/KnYeKMd1o for i in 0..codspeed::codspeed::WARMUP_RUNS + 1 { if i < codspeed::codspeed::WARMUP_RUNS { black_box(routine()); } else { - codspeed.start_benchmark(self.uri.as_str()); + self.codspeed.start_benchmark(self.uri.as_str()); black_box(routine()); - codspeed.end_benchmark(); + self.codspeed.end_benchmark(); } } } @@ -62,17 +54,15 @@ impl<'a> Bencher<'a> { S: FnMut() -> I, R: FnMut(I) -> O, { - let mut codspeed = self.codspeed.borrow_mut(); - for i in 0..codspeed::codspeed::WARMUP_RUNS + 1 { let input = black_box(setup()); let output = if i < codspeed::codspeed::WARMUP_RUNS { routine(input) } else { let input = black_box(setup()); - codspeed.start_benchmark(self.uri.as_str()); + self.codspeed.start_benchmark(self.uri.as_str()); let output = routine(input); - codspeed.end_benchmark(); + self.codspeed.end_benchmark(); output }; drop(black_box(output)); @@ -108,16 +98,14 @@ impl<'a> Bencher<'a> { S: FnMut() -> I, R: FnMut(&mut I) -> O, { - let mut codspeed = self.codspeed.borrow_mut(); - for i in 0..codspeed::codspeed::WARMUP_RUNS + 1 { let mut input = black_box(setup()); let output = if i < codspeed::codspeed::WARMUP_RUNS { black_box(routine(&mut input)) } else { - codspeed.start_benchmark(self.uri.as_str()); + self.codspeed.start_benchmark(self.uri.as_str()); let output = black_box(routine(&mut input)); - codspeed.end_benchmark(); + self.codspeed.end_benchmark(); output }; drop(black_box(output)); @@ -149,14 +137,13 @@ impl<'a, 'b, A: AsyncExecutor> AsyncBencher<'a, 'b, A> { { let AsyncBencher { b, runner } = self; runner.block_on(async { - let mut codspeed = b.codspeed.borrow_mut(); for i in 0..codspeed::codspeed::WARMUP_RUNS + 1 { if i < codspeed::codspeed::WARMUP_RUNS { black_box(routine().await); } else { - codspeed.start_benchmark(b.uri.as_str()); + b.codspeed.start_benchmark(b.uri.as_str()); black_box(routine().await); - codspeed.end_benchmark(); + b.codspeed.end_benchmark(); } } }); @@ -214,16 +201,14 @@ impl<'a, 'b, A: AsyncExecutor> AsyncBencher<'a, 'b, A> { { let AsyncBencher { b, runner } = self; runner.block_on(async { - let mut codspeed = b.codspeed.borrow_mut(); - for i in 0..codspeed::codspeed::WARMUP_RUNS + 1 { let input = black_box(setup()); let output = if i < codspeed::codspeed::WARMUP_RUNS { routine(input).await } else { - codspeed.start_benchmark(b.uri.as_str()); + b.codspeed.start_benchmark(b.uri.as_str()); let output = routine(input).await; - codspeed.end_benchmark(); + b.codspeed.end_benchmark(); output }; drop(black_box(output)); @@ -245,16 +230,14 @@ impl<'a, 'b, A: AsyncExecutor> AsyncBencher<'a, 'b, A> { { let AsyncBencher { b, runner } = self; runner.block_on(async { - let mut codspeed = b.codspeed.borrow_mut(); - for i in 0..codspeed::codspeed::WARMUP_RUNS + 1 { let mut input = black_box(setup()); let output = if i < codspeed::codspeed::WARMUP_RUNS { black_box(routine(&mut input).await) } else { - codspeed.start_benchmark(b.uri.as_str()); + b.codspeed.start_benchmark(b.uri.as_str()); let output = black_box(routine(&mut input).await); - codspeed.end_benchmark(); + b.codspeed.end_benchmark(); output }; drop(black_box(output)); @@ -263,3 +246,17 @@ impl<'a, 'b, A: AsyncExecutor> AsyncBencher<'a, 'b, A> { }); } } + +#[cfg(test)] +mod tests { + use super::*; + + fn assert_send() {} + fn assert_sync() {} + + #[test] + fn test_auto_traits() { + assert_send::(); + assert_sync::(); + } +} diff --git a/crates/criterion_compat/src/compat/group.rs b/crates/criterion_compat/src/compat/group.rs index ab8d3d9c..75ae283d 100644 --- a/crates/criterion_compat/src/compat/group.rs +++ b/crates/criterion_compat/src/compat/group.rs @@ -19,8 +19,8 @@ pub struct BenchmarkGroup<'a, M: Measurement = WallTime> { #[allow(clippy::needless_lifetimes)] impl<'a, M: Measurement> BenchmarkGroup<'a, M> { - pub fn new(criterion: &mut Criterion, group_name: String) -> BenchmarkGroup { - BenchmarkGroup:: { + pub(crate) fn new(criterion: &'a mut Criterion, group_name: String) -> Self { + Self { codspeed: criterion .codspeed .as_ref() @@ -73,7 +73,8 @@ impl<'a, M: Measurement> BenchmarkGroup<'a, M> { if let Some(parameter) = id.parameter { uri = format!("{uri}[{parameter}]"); } - let mut b = Bencher::new(self.codspeed.clone(), uri); + let mut codspeed = self.codspeed.borrow_mut(); + let mut b = Bencher::new(&mut codspeed, uri); f(&mut b, input); } }