Benchmarking With Inputs

Criterion.rs can run benchmarks with one or more different input values to investigate how the performance behavior changes with different inputs.

Benchmarking With One Input

If you only have one input to your function, you can use a simple interface on the Criterion struct to run that benchmark.


#![allow(unused)]
fn main() {
use criterion::BenchmarkId;
use criterion::Criterion;
use criterion::{criterion_group, criterion_main};

fn do_something(size: usize) {
    // Do something with the size
}

fn from_elem(c: &mut Criterion) {
    let size: usize = 1024;

    c.bench_with_input(BenchmarkId::new("input_example", size), &size, |b, &s| {
        b.iter(|| do_something(s));
    });
}

criterion_group!(benches, from_elem);
criterion_main!(benches);
}

This is convenient in that it automatically passes the input through a black_box so that you don't need to call that directly. It also includes the size in the benchmark description.

Benchmarking With A Range Of Values

Criterion.rs can compare the performance of a function over a range of inputs using a BenchmarkGroup.


#![allow(unused)]
fn main() {
use std::iter;

use criterion::BenchmarkId;
use criterion::Criterion;
use criterion::Throughput;

fn from_elem(c: &mut Criterion) {
    static KB: usize = 1024;

    let mut group = c.benchmark_group("from_elem");
    for size in [KB, 2 * KB, 4 * KB, 8 * KB, 16 * KB].iter() {
        group.throughput(Throughput::Bytes(*size as u64));
        group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| {
            b.iter(|| iter::repeat(0u8).take(size).collect::<Vec<_>>());
        });
    }
    group.finish();
}

criterion_group!(benches, from_elem);
criterion_main!(benches);
}

In this example, we're benchmarking the time it takes to collect an iterator producing a sequence of N bytes into a Vec. First, we create a benchmark group, which is a way of telling Criterion.rs that a set of benchmarks are all related. Criterion.rs will generate extra summary pages for benchmark groups. Then we simply iterate over a set of desired inputs; we could just as easily unroll this loop manually, generate inputs of a particular size, etc.

Inside the loop, we call the throughput function which informs Criterion.rs that the benchmark operates on size bytes per iteration. Criterion.rs will use this to estimate the number of bytes per second that our function can process. Next we call bench_with_input, providing a unique benchmark ID (in this case it's just the size, but you could generate custom strings as needed), passing in the size and a lambda that takes the size and a Bencher and performs the actual measurement.

Finally, we finish the benchmark group; this generates the summary pages for that group. It is recommended to call finish explicitly, but if you forget it will be called automatically when the group is dropped.

Line Chart

Here we can see that there is a approximately-linear relationship between the length of an iterator and the time taken to collect it into a Vec.