Attribute Macro iai_callgrind_macros::library_benchmark
source · #[library_benchmark]
Expand description
The #[library_benchmark]
attribute let’s you define a benchmark function which you can later
use in the library_benchmark_groups!
macro.
This attribute can be applied in two ways.
Using the #[library_benchmark]
attribute as a standalone is fine for simple function calls
without parameters.
However, we mostly need to benchmark cases which would need to be setup for example with a
vector, but everything we setup within the benchmark function itself would be attributed to the
event counts. The second form of this attribute macro uses the bench
attribute to setup
benchmarks with different cases. The main advantage is, that the setup costs and event counts
aren’t attributed to the benchmark (and opposed to the old api we don’t have to deal with
callgrind arguments, toggles, inline(never), …)
The bench
attribute consist of the attribute name itself, an unique id after ::
and
optionally one or more arguments with expressions which are passed to the benchmark function as
parameter as shown below:
// Assume this is a more complicated function in your library which you want to benchmark
fn some_func(value: u64) -> u64 {
42
}
#[library_benchmark]
#[bench::some_id(42)]
fn bench_some_func(value: u64) -> u64 {
std::hint::black_box(some_func(value))
}
Assuming the same function some_func
, the benches
attribute lets you define multiple
benchmarks in one go:
#[library_benchmark]
#[benches::some_id(21, 42, 84)]
fn bench_some_func(value: u64) -> u64 {
std::hint::black_box(some_func(value))
}
Examples
The #[library_benchmark]
attribute as a standalone
fn some_func() -> u64 {
42
}
#[library_benchmark]
// If possible, it's best to return something from a benchmark function
fn bench_my_library_function() -> u64 {
// The `black_box` is needed to tell the compiler to not optimize what's inside the
// black_box or else the benchmarks might return inaccurate results.
std::hint::black_box(some_func())
}
In the following example we pass a single argument with Vec<i32>
type to the benchmark. All
arguments are already wrapped in a black box and don’t need to be put in a black_box
again.
// Our function we want to test. Just assume this is a public function in your
// library.
fn some_func_with_array(array: Vec<i32>) -> Vec<i32> {
// do something with the array and return a new array
}
// This function is used to create a worst case array for our `some_func_with_array`
fn setup_worst_case_array(start: i32) -> Vec<i32> {
if start.is_negative() {
(start..0).rev().collect()
} else {
(0..start).rev().collect()
}
}
// This benchmark is setting up multiple benchmark cases with the advantage that the setup
// costs for creating a vector (even if it is empty) aren't attributed to the benchmark and
// that the `array` is already wrapped in a black_box.
#[library_benchmark]
#[bench::empty(vec![])]
#[bench::worst_case_6(vec![6, 5, 4, 3, 2, 1])]
// Function calls are fine too
#[bench::worst_case_4000(setup_worst_case_array(4000))]
// The argument of the benchmark function defines the type of the argument from the `bench`
// cases.
fn bench_some_func_with_array(array: Vec<i32>) -> Vec<i32> {
// Note `array` does not need to be put in a `black_box` because that's already done for
// you.
std::hint::black_box(some_func_with_array(array))
}
// The following benchmark uses the `#[benches]` attribute to setup multiple benchmark cases
// in one go
#[library_benchmark]
#[benches::multiple(vec![1], vec![5])]
// Reroute the `args` to a `setup` function and use the setup function's return value as
// input for the benchmarking function
#[benches::with_setup(args = [1, 5], setup = setup_worst_case_array)]
fn bench_using_the_benches_attribute(array: Vec<i32>) -> Vec<i32> {
std::hint::black_box(some_func_with_array(array))
}