Low-level api
I'm not going into full detail of the low-level api here since it is fully documented in the api Documentation.
The basic structure
The entry point of the low-level api is the binary_benchmark_group
extern crate iai_callgrind; macro_rules! env { ($m:tt) => {{ "/some/path" }} } use iai_callgrind::{ binary_benchmark, binary_benchmark_attribute, binary_benchmark_group, main, BinaryBenchmark, Bench }; binary_benchmark_group!( name = my_group; benchmarks = |group: &mut BinaryBenchmarkGroup| { group.binary_benchmark(BinaryBenchmark::new("bench_binary") .bench(Bench::new("some_id") .command(iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo")) .arg("foo.txt") .build() ) ) ) } ); fn main() { main!(binary_benchmark_groups = my_group); }
The low-level api mirrors the high-level api, "structifying" the macros.
The binary_benchmark_group!
is also a struct now, the BinaryBenchmarkGroup
.
It cannot be instantiated. Instead, it is passed as argument to the expression
of the benchmarks
parameter in a binary_benchmark_group
. You can choose any
name instead of group
, we just have used group
throughout the examples.
There's the shorter benchmarks = |group| /* ... */
instead of benchmarks = |group: &mut BinaryBenchmarkGroup| /* ... */
. We use the more verbose variant
in the examples because it is more informative for benchmarking starters.
Furthermore, the #[library_benchmark]
macro correlates with
iai_callgrind::LibraryBenchmark
and #[bench]
with iai_callgrind::Bench
.
The parameters of the macros are now functions in the respective structs. The
return value of the benchmark function, the iai-callgrind::Command
, is now
also a function iai-callgrind::Bench::command
.
Note there is no iai-callgrind::Benches
struct since specifying multiple
commands with iai_callgrind::Bench::command
behaves exactly the same way as
the #[benches]
attribute. So, the file
parameter of #[benches]
is a part
of iai-callgrind::Bench
and can be used with the iai-callgrind::Bench::file
function.
Intermixing high-level and low-level api
It is recommended to start with the high-level api using the
#[binary_benchmark]
attribute, since you can fall back to the low-level api in
a few steps with the binary_benchmark_attribute!
macro as shown below. The
other way around is much more involved.
extern crate iai_callgrind; macro_rules! env { ($m:tt) => {{ "/some/path" }} } use iai_callgrind::{ binary_benchmark, binary_benchmark_attribute, binary_benchmark_group, main, BinaryBenchmark, Bench }; #[binary_benchmark] #[bench::some_id("foo")] fn attribute_benchmark(arg: &str) -> iai_callgrind::Command { iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-binary")) .arg(arg) .build() } binary_benchmark_group!( name = low_level; benchmarks = |group: &mut BinaryBenchmarkGroup| { group .binary_benchmark(binary_benchmark_attribute!(attribute_benchmark)) .binary_benchmark( BinaryBenchmark::new("low_level_benchmark") .bench( Bench::new("some_id").command( iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-binary")) .arg("bar") .build() ) ) ) } ); fn main() { main!(binary_benchmark_groups = low_level); }
As shown above, there's no need to transcribe the function attribute_benchmark
with the #[binary_benchmark]
attribute into the low-level api structures. Just
keep it as it is and add it to a the group
with
group.binary_benchmark(binary_benchmark_attribute(attribute_benchmark))
.
That's it! You can continue hacking on your benchmarks in the low-level api.