Structure of a Library Benchmark
We're reusing our example from the Quickstart section.
extern crate gungraun; use gungraun::prelude::*; use std::hint::black_box; fn fibonacci(n: u64) -> u64 { match n { 0 => 1, 1 => 1, n => fibonacci(n - 1) + fibonacci(n - 2), } } #[library_benchmark] #[bench::short(10)] #[bench::long(30)] fn bench_fibonacci(value: u64) -> u64 { black_box(fibonacci(black_box(value))) } library_benchmark_group!( name = bench_fibonacci_group, benchmarks = bench_fibonacci ); fn main() { main!(library_benchmark_groups = bench_fibonacci_group); }
First of all, you need a public function in your library which you want to
benchmark. In this example this is the fibonacci function which, for the sake
of simplicity, lives in the benchmark file itself but doesn't have to. If it had
been located in my_lib::fibonacci, you simply import that function with
use my_lib::fibonacci and go on as shown above. Next, you need a
library_benchmark_group! in which you specify the names of the benchmark
functions. Finally, the benchmark harness is created by the main! macro.
The Benchmark Function
The benchmark function has to be annotated with the
#[library_benchmark] attribute. The #[bench]
attribute is an inner attribute of the #[library_benchmark] attribute. It
consists of a mandatory id (the ID part in #[bench::ID(/* ... */)]) and in
its most basic form, an optional list of arguments which are passed to the
benchmark function as parameters. Naturally, the parameters of the benchmark
function must match the parameter list of the #[bench] attribute. It is always
a good idea to return something from the benchmark function, here it is the
computed u64 value from the fibonacci function wrapped in a black_box. See
the docs of std::hint::black_box for more information about
its usage. Wrap both input and output values in black_box to prevent the
compiler from optimizing away computations.
The bench attribute takes any expression which includes function calls. The
following would have worked too and is one way to avoid the costs of the setup
code being attributed to the benchmarked function.
extern crate gungraun; use gungraun::prelude::*; use std::hint::black_box; fn some_setup_func(value: u64) -> u64 { value + 10 } fn fibonacci(n: u64) -> u64 { match n { 0 => 1, 1 => 1, n => fibonacci(n - 1) + fibonacci(n - 2), } } #[library_benchmark] #[bench::short(10)] // Note the usage of the `some_setup_func` in the argument list of this #[bench] #[bench::long(some_setup_func(20))] fn bench_fibonacci(value: u64) -> u64 { black_box(fibonacci(black_box(value))) } library_benchmark_group!( name = bench_fibonacci_group, benchmarks = bench_fibonacci ); fn main() { main!(library_benchmark_groups = bench_fibonacci_group); }
The perhaps most crucial part in setting up library benchmarks is to keep the body of benchmark functions clean from any setup or teardown code. There are other ways to avoid setup and teardown code in the benchmark function, which are discussed in full detail in the setup and teardown section.
The Group
The benchmark functions to run, in this case only bench_fibonacci, need to be
specified in a library_benchmark_group! in the benchmarks parameter. You can
create as many groups as you like, and you can use it to organize related
benchmarks. Each group needs a unique name.
The main! Macro
Each group you want to be benchmarked needs to be specified in the
library_benchmark_groups parameter of the main! macro and you're all set.