Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Becnher.iter_reuse #830

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions src/bencher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,81 @@ impl<'a, M: Measurement> Bencher<'a, M> {
self.elapsed_time = time_start.elapsed();
}

/// Times a `routine` by executing it many times and timing the total elapsed time.
///
/// Prefer this timing loop when `routine` returns a value that can be reused in
/// the next iteration.
///
/// # Timing model
///
/// Note that the `Bencher` also times the time required to move a value from one call to another.
/// Therefore prefer this timing loop when the runtime of this movement is negligible.
///
/// ```text
/// elapsed = Instant::now + iters * (routine + Range::next)
/// ```
///
/// # Example
///
/// ```rust
/// use criterion::{criterion_group, criterion_main, Criterion};
///
/// const HANDLE_COUNT: usize = 100;
///
/// fn spawn_task(f: impl FnOnce() -> Vec<usize>) {
/// todo!()
/// }
///
/// fn await_task() -> Vec<usize> {
/// todo!()
/// }
///
/// // The function to benchmark that accept, use and returns a empty vector of a certain length
/// fn spawn_and_await(mut vec: Vec<usize>) -> Vec<usize> {
/// // move to closure
/// spawn_task(move || {
/// // use vec here and return
/// vec
/// });
///
/// // move back
/// let mut vec = await_task();
/// vec.clear();
///
/// vec
/// }
///
/// fn bench(c: &mut Criterion) {
/// c.bench_function("iter", move |b| {
/// b.iter_reuse(Vec::with_capacity(HANDLE_COUNT), |vec| spawn_and_await(vec))
/// });
/// }
///
/// criterion_group!(benches, bench);
/// criterion_main!(benches);
/// ```
///
#[inline(never)]
pub fn iter_reuse<IO, R>(&mut self, input: IO, mut routine: R)
where
R: FnMut(IO) -> IO,
{
self.iterated = true;

let time_start = Instant::now();
let start = self.measurement.start();

let mut input_output = input;
for _ in 0..self.iters {
input_output = black_box(routine(input_output));
}

self.value = self.measurement.end(start);
self.elapsed_time = time_start.elapsed();

black_box(drop(input_output));
}

/// Times a `routine` by executing it many times and relying on `routine` to measure its own execution time.
///
/// Prefer this timing loop in cases where `routine` has to do its own measurements to
Expand Down
3 changes: 3 additions & 0 deletions tests/criterion_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,9 @@ fn test_timing_loops() {
let dir = temp_dir();
let mut c = short_benchmark(&dir);
let mut group = c.benchmark_group("test_timing_loops");
group.bench_function("iter_reuse", |b| {
b.iter_reuse(vec![10], |v| v);
});
group.bench_function("iter_with_setup", |b| {
b.iter_with_setup(|| vec![10], |v| v[0]);
});
Expand Down