iai_callgrind/macros.rs
1//! Contains macros which together define a benchmark harness that can be used in place of the
2//! standard benchmark harness. This allows the user to run Iai benchmarks with `cargo bench`.
3
4/// [low level api](`crate::binary_benchmark_group`) only: Use to add a `#[binary_benchmark]` to a
5/// [`crate::BinaryBenchmarkGroup`]
6///
7/// # Examples
8///
9/// ```rust
10/// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
11/// use iai_callgrind::{binary_benchmark_attribute, binary_benchmark_group, binary_benchmark};
12///
13/// #[binary_benchmark]
14/// fn bench_binary() -> iai_callgrind::Command {
15/// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
16/// .arg("foo")
17/// .build()
18/// }
19///
20/// binary_benchmark_group!(
21/// name = my_group;
22/// benchmarks = |group: &mut BinaryBenchmarkGroup| {
23/// group.binary_benchmark(binary_benchmark_attribute!(bench_binary));
24/// }
25/// );
26/// # fn main() {}
27/// ```
28#[macro_export]
29macro_rules! binary_benchmark_attribute {
30 ($name:ident) => {{
31 let mut binary_benchmark = $crate::BinaryBenchmark::new(stringify!($name));
32 binary_benchmark.config = $name::__get_config();
33
34 for internal_bench in $name::__BENCHES {
35 let mut bench = if let Some(id) = internal_bench.id_display {
36 $crate::Bench::new(id)
37 } else {
38 $crate::Bench::new(stringify!($name))
39 };
40 let mut bench = bench.command((internal_bench.func)());
41 if let Some(setup) = internal_bench.setup {
42 bench.setup(setup);
43 }
44 if let Some(teardown) = internal_bench.teardown {
45 bench.teardown(teardown);
46 }
47 if let Some(config) = internal_bench.config {
48 bench.config(config());
49 }
50 binary_benchmark.bench(bench);
51 }
52 binary_benchmark
53 }};
54}
55
56/// The `iai_callgrind::main` macro expands to a `main` function which runs all the benchmarks.
57///
58/// Using Iai-Callgrind requires disabling the benchmark harness. This can be done like so in the
59/// `Cargo.toml` file:
60///
61/// ```toml
62/// [[bench]]
63/// name = "my_bench"
64/// harness = false
65/// ```
66///
67/// To be able to run any iai-callgrind benchmarks, you'll also need the `iai-callgrind-runner`
68/// installed with the binary somewhere in your `$PATH` for example with
69///
70/// ```shell
71/// cargo install iai-callgrind-runner
72/// ```
73///
74/// `my_bench` has to be a rust file inside the 'benches' directory.
75///
76/// # Library Benchmarks
77///
78/// The [`crate::main`] macro has one form to run library benchmarks:
79///
80/// ```rust
81/// # use iai_callgrind::{main, library_benchmark_group, library_benchmark};
82/// # #[library_benchmark]
83/// # fn bench_fibonacci() { }
84/// # library_benchmark_group!(
85/// # name = some_group;
86/// # benchmarks = bench_fibonacci
87/// # );
88/// # fn main() {
89/// main!(library_benchmark_groups = some_group);
90/// # }
91/// ```
92///
93/// which accepts the following top-level arguments in this order (separated by a semicolon):
94///
95/// * __`config`__ (optional): Optionally specify a [`crate::LibraryBenchmarkConfig`] valid for all
96/// benchmark groups
97/// * __`setup`__ (optional): A setup function or any valid expression which is run before all
98/// benchmarks
99/// * __`teardown`__ (optional): A setup function or any valid expression which is run after all
100/// benchmarks
101/// * __`library_benchmark_groups`__ (mandatory): The __name__ of one or more
102/// [`library_benchmark_group!`](crate::library_benchmark_group) macros. Multiple __names__ are
103/// expected to be a comma separated list
104///
105/// A library benchmark consists of
106/// [`library_benchmark_groups`](crate::library_benchmark_group) and with
107/// [`#[library_benchmark]`](crate::library_benchmark) annotated benchmark functions.
108///
109/// ```rust
110/// use iai_callgrind::{main, library_benchmark_group, library_benchmark};
111/// use std::hint::black_box;
112///
113/// fn fibonacci(n: u64) -> u64 {
114/// match n {
115/// 0 => 1,
116/// 1 => 1,
117/// n => fibonacci(n - 1) + fibonacci(n - 2),
118/// }
119/// }
120///
121/// #[library_benchmark]
122/// #[bench::short(10)]
123/// #[bench::long(30)]
124/// fn bench_fibonacci(value: u64) -> u64 {
125/// black_box(fibonacci(value))
126/// }
127///
128/// library_benchmark_group!(
129/// name = bench_fibonacci_group;
130/// benchmarks = bench_fibonacci
131/// );
132///
133/// # fn main() {
134/// main!(library_benchmark_groups = bench_fibonacci_group);
135/// # }
136/// ```
137///
138/// If you need to pass arguments to valgrind's callgrind or other tools, you can specify arguments
139/// via [`crate::Callgrind::args`], [`crate::Dhat::args`], ...:
140///
141/// ```rust
142/// # use iai_callgrind::{
143/// # main, library_benchmark_group, library_benchmark, LibraryBenchmarkConfig, Callgrind
144/// # };
145/// # #[library_benchmark]
146/// # fn bench_fibonacci() { }
147/// # library_benchmark_group!(
148/// # name = some_group;
149/// # benchmarks = bench_fibonacci
150/// # );
151/// # fn main() {
152/// main!(
153/// config = LibraryBenchmarkConfig::default()
154/// .tool(Callgrind::with_args(
155/// ["--arg-with-flags=yes", "arg-without-flags=is_ok_too"]
156/// ));
157/// library_benchmark_groups = some_group
158/// );
159/// # }
160/// ```
161///
162/// See also [Callgrind Command-line
163/// options](https://valgrind.org/docs/manual/cl-manual.html#cl-manual.options).
164///
165/// For an in-depth description of library benchmarks and more examples see the
166/// [guide](https://iai-callgrind.github.io/iai-callgrind/latest/html/benchmarks/library_benchmarks.html).
167///
168/// # Binary Benchmarks
169///
170/// Setting up binary benchmarks is almost the same as setting up library benchmarks but using the
171/// `#[binary_benchmark]` macro. For example, if you're crate's binary is called `my-foo`:
172///
173/// ```rust
174/// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
175/// use iai_callgrind::{main, binary_benchmark_group, binary_benchmark};
176///
177/// #[binary_benchmark]
178/// #[bench::hello_world("hello world")]
179/// #[bench::foo("foo")]
180/// fn bench_binary(arg: &str) -> iai_callgrind::Command {
181/// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
182/// .arg(arg)
183/// .build()
184/// }
185///
186/// binary_benchmark_group!(
187/// name = my_group;
188/// benchmarks = bench_binary
189/// );
190///
191/// # fn main() {
192/// main!(binary_benchmark_groups = my_group);
193/// # }
194/// ```
195///
196/// See the documentation of [`crate::binary_benchmark_group`] and [`crate::Command`] for more
197/// details.
198#[macro_export]
199macro_rules! main {
200 ( $( options = $( $options:literal ),+ $(,)*; )?
201 $( before = $before:ident $(, bench = $bench_before:literal )? ; )?
202 $( after = $after:ident $(, bench = $bench_after:literal )? ; )?
203 $( setup = $setup:ident $(, bench = $bench_setup:literal )? ; )?
204 $( teardown = $teardown:ident $(, bench = $bench_teardown:literal )? ; )?
205 $( sandbox = $sandbox:literal; )?
206 $( fixtures = $fixtures:literal $(, follow_symlinks = $follow_symlinks:literal )? ; )?
207 $( run = cmd = $cmd:expr
208 $(, envs = [ $( $envs:literal ),* $(,)* ] )?,
209 $( id = $id:literal, args = [ $( $args:literal ),* $(,)* ] ),+ $(,)*
210 );+ $(;)*
211 ) => {
212 compile_error!(
213 "You are using a deprecated syntax of the main! macro to set up binary benchmarks. \
214 See the README (https://github.com/iai-callgrind/iai-callgrind) and \
215 docs (https://docs.rs/iai-callgrind/latest/iai_callgrind/) for further details."
216 );
217 pub fn main() {}
218 };
219 (
220 $( config = $config:expr; $(;)* )?
221 $( setup = $setup:expr ; $(;)* )?
222 $( teardown = $teardown:expr ; $(;)* )?
223 binary_benchmark_groups =
224 ) => {
225 compile_error!("The binary_benchmark_groups argument needs at least one `name` of a `binary_benchmark_group!`");
226 };
227 (
228 $( config = $config:expr; $(;)* )?
229 $( setup = $setup:expr ; $(;)* )?
230 $( teardown = $teardown:expr ; $(;)* )?
231 binary_benchmark_groups = $( $group:ident ),+ $(,)*
232 ) => {
233 fn __run() -> Result<(), $crate::__internal::error::Errors> {
234 let mut this_args = std::env::args();
235 let mut runner = $crate::__internal::Runner::new(
236 option_env!("IAI_CALLGRIND_RUNNER").or_else(||
237 option_env!("CARGO_BIN_EXE_iai-callgrind-runner")
238 ),
239 &$crate::__internal::BenchmarkKind::BinaryBenchmark,
240 env!("CARGO_MANIFEST_DIR"),
241 env!("CARGO_PKG_NAME"),
242 file!(),
243 module_path!(),
244 this_args.next().unwrap(),
245 );
246
247 let mut config: Option<$crate::__internal::InternalBinaryBenchmarkConfig> = None;
248 $(
249 config = Some($config.into());
250 )?
251
252 let mut groups_builder = $crate::__internal::bin_bench::GroupsBuilder::new(
253 config, this_args.collect(), __run_setup(false), __run_teardown(false),
254 );
255
256 $(
257 let mut group = $crate::BinaryBenchmarkGroup::default();
258 $group::$group(&mut group);
259
260 groups_builder.add_group(
261 group,
262 stringify!($group).to_owned(),
263 &module_path!(),
264 $group::__IS_ATTRIBUTE,
265 $group::__get_config(),
266 $group::__run_setup(false),
267 $group::__run_teardown(false),
268 $group::__compare_by_id(),
269 $group::__BENCHES
270 );
271 )+
272
273 let groups = groups_builder.build()?;
274 let encoded = $crate::bincode::serialize(&groups).expect("Encoded benchmark");
275 runner.exec(encoded)
276 }
277
278 fn __run_setup(__run: bool) -> bool {
279 let mut __has_setup = false;
280 $(
281 __has_setup = true;
282 if __run {
283 $setup;
284 }
285 )?
286 __has_setup
287 }
288
289 fn __run_teardown(__run: bool) -> bool {
290 let mut __has_teardown = false;
291 $(
292 __has_teardown = true;
293 if __run {
294 $teardown;
295 }
296 )?
297 __has_teardown
298 }
299
300 fn main() {
301 let mut args_iter = std::env::args().skip(1);
302 if args_iter
303 .next()
304 .as_ref()
305 .map_or(false, |value| value == "--iai-run")
306 {
307 let mut current = args_iter.next().expect("Expecting a function type");
308 let next = args_iter.next();
309 match (current.as_str(), next) {
310 ("setup", None) => {
311 __run_setup(true);
312 },
313 ("teardown", None) => {
314 __run_teardown(true);
315 },
316 $(
317 (group @ stringify!($group), Some(next)) => {
318 let current = next;
319 let next = args_iter.next();
320
321 match (current.as_str(), next) {
322 ("setup", None) => {
323 $group::__run_setup(true);
324 },
325 ("teardown", None) => {
326 $group::__run_teardown(true);
327 }
328 (key @ ("setup" | "teardown"), Some(next)) => {
329 let group_index = next
330 .parse::<usize>()
331 .expect("The group index should be a number");
332 let bench_index = args_iter
333 .next()
334 .expect("The bench index should be present")
335 .parse::<usize>()
336 .expect("The bench index should be a number");
337 if key == "setup" {
338 $group::__run_bench_setup(group_index, bench_index);
339 } else {
340 $group::__run_bench_teardown(group_index, bench_index);
341 }
342 }
343 (name, _) => panic!("Invalid function '{}' in group '{}'", name, group)
344 }
345 }
346 )+
347 (name, _) => panic!("function '{}' not found in this scope", name)
348 }
349 } else {
350 if let Err(errors) = __run() {
351 eprintln!("{errors}");
352 std::process::exit(1);
353 }
354 };
355 }
356 };
357 (
358 $( config = $config:expr; $(;)* )?
359 $( setup = $setup:expr ; $(;)* )?
360 $( teardown = $teardown:expr ; $(;)* )?
361 library_benchmark_groups =
362 ) => {
363 compile_error!("The library_benchmark_groups argument needs at least one `name` of a `library_benchmark_group!`");
364 };
365 (
366 $( config = $config:expr ; $(;)* )?
367 $( setup = $setup:expr ; $(;)* )?
368 $( teardown = $teardown:expr ; $(;)* )?
369 library_benchmark_groups = $( $group:ident ),+ $(,)*
370 ) => {
371 #[inline(never)]
372 fn __run() {
373 let mut this_args = std::env::args();
374 let mut runner = $crate::__internal::Runner::new(
375 option_env!("IAI_CALLGRIND_RUNNER").or_else(||
376 option_env!("CARGO_BIN_EXE_iai-callgrind-runner")
377 ),
378 &$crate::__internal::BenchmarkKind::LibraryBenchmark,
379 env!("CARGO_MANIFEST_DIR"),
380 env!("CARGO_PKG_NAME"),
381 file!(),
382 module_path!(),
383 this_args.next().unwrap(),
384 );
385
386 let mut config: Option<$crate::__internal::InternalLibraryBenchmarkConfig> = None;
387 $(
388 config = Some($config.into());
389 )?
390
391 let mut groups_builder = $crate::__internal::lib_bench::GroupsBuilder::new(
392 config, this_args.collect(), __run_setup(false), __run_teardown(false),
393 );
394
395 $(
396 groups_builder.add_group(
397 stringify!($group).to_owned(),
398 $group::__get_config(),
399 $group::__compare_by_id(),
400 $group::__run_setup(false),
401 $group::__run_teardown(false),
402 $group::__BENCHES
403 );
404 )+
405
406 let encoded = $crate::bincode::serialize(&groups_builder.build())
407 .expect("Encoded benchmark");
408
409 if let Err(errors) = runner.exec(encoded) {
410 eprintln!("{errors}");
411 std::process::exit(1);
412 }
413 }
414
415 #[inline(never)]
416 fn __run_setup(__run: bool) -> bool {
417 let mut __has_setup = false;
418 $(
419 __has_setup = true;
420 if __run {
421 $setup;
422 }
423 )?
424 __has_setup
425 }
426
427 #[inline(never)]
428 fn __run_teardown(__run: bool) -> bool {
429 let mut __has_teardown = false;
430 $(
431 __has_teardown = true;
432 if __run {
433 $teardown;
434 }
435 )?
436 __has_teardown
437 }
438
439 /// Keep the logic to run the benchmark function within the main function to avoid heap
440 /// allocations in functions other than main which are part of the benchmarking framework.
441 /// DHAT needs a fallback (matched with `file::*::*`) to the benchmark function (matched
442 /// with `*::__iai_callgrind_wrapper_mod::*`) since the benchmark function is sometimes
443 /// inlined even if annotated with `#[inline(never)]`.
444 fn main() {
445 let mut args_iter = std::hint::black_box(std::env::args()).skip(1);
446 if args_iter
447 .next()
448 .as_ref()
449 .map_or(false, |value| value == "--iai-run")
450 {
451 let current = std::hint::black_box(args_iter.next().expect("Expecting a function type"));
452 let next = std::hint::black_box(args_iter.next());
453 match current.as_str() {
454 "setup" if next.is_none() => {
455 __run_setup(true);
456 },
457 "teardown" if next.is_none() => {
458 __run_teardown(true);
459 },
460 $(
461 stringify!($group) => {
462 match std::hint::black_box(
463 next
464 .expect("An argument `setup`, `teardown` or an index should be present")
465 .as_str()
466 ) {
467 "setup" => {
468 $group::__run_setup(true);
469 },
470 "teardown" => {
471 $group::__run_teardown(true);
472 }
473 value => {
474 let group_index = std::hint::black_box(
475 value
476 .parse::<usize>()
477 .expect("Expecting a valid group index")
478 );
479 let bench_index = std::hint::black_box(
480 args_iter
481 .next()
482 .expect("A bench index should be present")
483 .parse::<usize>()
484 .expect("Expecting a valid bench index")
485 );
486 $group::__run(group_index, bench_index);
487 }
488 }
489 }
490 )+
491 name => panic!("function '{}' not found in this scope", name)
492 }
493 } else {
494 std::hint::black_box(__run());
495 };
496 }
497 };
498 (
499 callgrind_args = $( $args:literal ),* $(,)*; $(;)*
500 functions = $( $func_name:ident ),+ $(,)*
501 ) => {
502 compile_error!(
503 "You are using a deprecated syntax of the main! macro to set up library benchmarks. \
504 See the README (https://github.com/iai-callgrind/iai-callgrind) and \
505 docs (https://docs.rs/iai-callgrind/latest/iai_callgrind/) for further details."
506 );
507 pub fn main() {}
508 };
509 ( $( $func_name:ident ),+ $(,)* ) => {
510 compile_error!(
511 "You are using a deprecated syntax of the main! macro to set up library benchmarks. \
512 See the README (https://github.com/iai-callgrind/iai-callgrind) and \
513 docs (https://docs.rs/iai-callgrind/latest/iai_callgrind/) for further details."
514 );
515 pub fn main() {}
516 };
517}
518
519/// Macro used to define a group of binary benchmarks
520///
521/// There are two apis to set up binary benchmarks. The recommended way is to [use the
522/// `#[binary_benchmark]` attribute](#using-the-high-level-api-with-the-binary-benchmark-attribute).
523/// But, if you find yourself in the situation that the attribute isn't enough you can fall back to
524/// the [low level api](#the-low-level-api) or even [intermix both
525/// styles](#intermixing-both-apis).
526///
527/// # The macro's arguments in detail:
528///
529/// The following top-level arguments are accepted (in this order):
530///
531/// ```rust
532/// # use iai_callgrind::{binary_benchmark, binary_benchmark_group, BinaryBenchmarkGroup, BinaryBenchmarkConfig};
533/// # fn run_setup() {}
534/// # fn run_teardown() {}
535/// # #[binary_benchmark]
536/// # fn bench_binary() -> iai_callgrind::Command { iai_callgrind::Command::new("some") }
537/// binary_benchmark_group!(
538/// name = my_group;
539/// config = BinaryBenchmarkConfig::default();
540/// compare_by_id = false;
541/// setup = run_setup();
542/// teardown = run_teardown();
543/// benchmarks = bench_binary
544/// );
545/// # fn main() {
546/// # my_group::my_group(&mut BinaryBenchmarkGroup::default());
547/// # }
548/// ```
549///
550/// * __`name`__ (mandatory): A unique name used to identify the group for the `main!` macro
551/// * __`config`__ (optional): A [`crate::BinaryBenchmarkConfig`]
552/// * __`compare_by_id`__ (optional): The default is false. If true, all commands from the functions
553/// specified in the `benchmarks` argument, are compared with each other as long as the ids (the
554/// part after the `::` in `#[bench::id(...)]`) match.
555/// * __`setup`__ (optional): A function which is executed before all benchmarks in this group
556/// * __`teardown`__ (optional): A function which is executed after all benchmarks in this group
557/// * __`benchmarks`__ (mandatory): A `,`-separated list of `#[binary_benchmark]` annotated function
558/// names you want to put into this group. Or, if you want to use the low level api
559///
560/// `|IDENTIFIER: &mut BinaryBenchmarkGroup| EXPRESSION`
561///
562/// or the shorter `|IDENTIFIER| EXPRESSION`
563///
564/// where `IDENTIFIER` is the identifier of your choice for the `BinaryBenchmarkGroup` (we use
565/// `group` throughout our examples) and `EXPRESSION` is the code where you make use of the
566/// `BinaryBenchmarkGroup` to set up the binary benchmarks
567///
568/// # Using the high-level api with the `#[binary benchmark]` attribute
569///
570/// A small introductory example which demonstrates the basic setup (assuming a crate's binary is
571/// named `my-foo`):
572///
573/// ```rust
574/// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
575/// use iai_callgrind::{binary_benchmark_group, BinaryBenchmarkGroup, binary_benchmark};
576///
577/// #[binary_benchmark]
578/// #[bench::hello_world("hello world")]
579/// #[bench::foo("foo")]
580/// #[benches::multiple("bar", "baz")]
581/// fn bench_binary(arg: &str) -> iai_callgrind::Command {
582/// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
583/// .arg(arg)
584/// .build()
585/// }
586///
587/// binary_benchmark_group!(
588/// name = my_group;
589/// benchmarks = bench_binary
590/// );
591///
592/// # fn main() {
593/// iai_callgrind::main!(binary_benchmark_groups = my_group);
594/// # }
595/// ```
596///
597/// To be benchmarked a `binary_benchmark_group` has to be added to the `main!` macro by adding its
598/// name to the `binary_benchmark_groups` argument of the `main!` macro. See there for further
599/// details about the [`crate::main`] macro. See the documentation of [`crate::binary_benchmark`]
600/// for more details about the attribute itself and the inner attributes `#[bench]` and
601/// `#[benches]`.
602///
603/// # The low-level api
604///
605/// Using the low-level api has advantages but when it comes to stability in terms of usability, the
606/// low level api might be considered less stable. What does this mean? If we have to make changes
607/// to the inner workings of iai-callgrind which not necessarily change the high-level api it is
608/// more likely that the low-level api has to be adjusted. This implies you might have to adjust
609/// your benchmarks more often with a version update of `iai-callgrind`. Hence, it is recommended to
610/// use the high-level api as much as possible and only use the low-level api under special
611/// circumstances. You can also [intermix both styles](#intermixing-both-apis)!
612///
613/// The low-level api mirrors the high-level constructs as close as possible. The
614/// [`crate::BinaryBenchmarkGroup`] is a special case, since we use the information from the
615/// `binary_benchmark_group!` macro [arguments](#the-macros-arguments-in-detail) (__`name`__,
616/// __`config`__, ...) to create the `BinaryBenchmarkGroup` and pass it to the `benchmarks`
617/// argument.
618///
619/// That being said, here's the basic usage:
620///
621/// ```rust
622/// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
623/// use iai_callgrind::{binary_benchmark_group, BinaryBenchmark, Bench};
624///
625/// binary_benchmark_group!(
626/// // All the other options from the `binary_benchmark_group` are used as usual
627/// name = my_group;
628///
629/// // Note there's also the shorter form `benchmarks = |group|` but in the examples we want
630/// // to be more explicit
631/// benchmarks = |group: &mut BinaryBenchmarkGroup| {
632///
633/// // We have chosen `group` to be our identifier but it can be anything
634/// group.binary_benchmark(
635///
636/// // This is the equivalent of the `#[binary_benchmark]` attribute. The `id`
637/// // mirrors the function name of the `#[binary_benchmark]` annotated function.
638/// BinaryBenchmark::new("some_id")
639/// .bench(
640///
641/// // The equivalent of the `#[bench]` attribute.
642/// Bench::new("my_bench_id")
643/// .command(
644///
645/// // The `Command` stays the same
646/// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
647/// .arg("foo").build()
648/// )
649/// )
650/// )
651/// }
652/// );
653/// # fn main() {}
654/// ```
655///
656/// Depending on your IDE, it's nicer to work with the code after the `|group: &mut
657/// BinaryBenchmarkGroup|` if it resides in a separate function rather than the macro itself as in
658///
659/// ```rust
660/// use iai_callgrind::{binary_benchmark_group, BinaryBenchmark, Bench, BinaryBenchmarkGroup};
661///
662/// fn setup_my_group(group: &mut BinaryBenchmarkGroup) {
663/// // Enjoy all the features of your IDE ...
664/// }
665///
666/// binary_benchmark_group!(
667/// name = my_group;
668/// benchmarks = |group: &mut BinaryBenchmarkGroup| setup_my_group(group)
669/// );
670/// # fn main() {}
671/// ```
672///
673/// The list of all structs and macros used exclusively in the low-level api:
674/// * [`crate::BinaryBenchmarkGroup`]
675/// * [`crate::BinaryBenchmark`]: Mirrors the `#[binary_benchmark]` attribute
676/// * [`crate::Bench`]: Mirrors the `#[bench]` attribute
677/// * [`crate::binary_benchmark_attribute`]: Used to add a `#[binary_benchmark]` attributed function
678/// in [`crate::BinaryBenchmarkGroup::binary_benchmark`]
679/// * [`crate::BenchmarkId`]: The benchmark id is for example used in
680/// [`crate::BinaryBenchmark::new`] and [`crate::Bench::new`]
681///
682/// Note there's no equivalent for the `#[benches]` attribute. The [`crate::Bench`] behaves exactly
683/// as the `#[benches]` attribute if more than a single [`crate::Command`] is added.
684///
685/// # Intermixing both apis
686///
687/// For example, if you started with the `#[binary_benchmark]` attribute and noticed you are limited
688/// by it to set up all the [`crate::Command`]s the way you want, you can intermix both styles:
689///
690/// ```rust
691/// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
692/// use iai_callgrind::{
693/// binary_benchmark, binary_benchmark_group, BinaryBenchmark, Bench, BinaryBenchmarkGroup,
694/// binary_benchmark_attribute
695/// };
696///
697/// #[binary_benchmark]
698/// #[bench::foo("foo")]
699/// #[benches::multiple("bar", "baz")]
700/// fn bench_binary(arg: &str) -> iai_callgrind::Command {
701/// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
702/// .arg(arg)
703/// .build()
704/// }
705///
706/// fn setup_my_group(group: &mut BinaryBenchmarkGroup) {
707/// group
708/// // Simply add what you already have with the `binary_benchmark_attribute!` macro.
709/// // This macro returns a `BinaryBenchmark`, so you could even add more `Bench`es
710/// // to it instead of creating a new one as we do below
711/// .binary_benchmark(binary_benchmark_attribute!(bench_binary))
712/// .binary_benchmark(
713/// BinaryBenchmark::new("did_not_work_with_attribute")
714/// .bench(Bench::new("low_level")
715/// .command(
716/// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
717/// .arg("foo")
718/// .build()
719/// )
720/// )
721/// );
722/// }
723///
724/// binary_benchmark_group!(
725/// name = my_group;
726/// benchmarks = |group: &mut BinaryBenchmarkGroup| setup_my_group(group)
727/// );
728/// # fn main() {}
729/// ```
730#[macro_export]
731macro_rules! binary_benchmark_group {
732 (
733 name = $name:ident; $(;)*
734 $(before = $before:ident $(,bench = $bench_before:literal)? ; $(;)*)?
735 $(after = $after:ident $(,bench = $bench_after:literal)? ; $(;)*)?
736 $(setup = $setup:ident $(,bench = $bench_setup:literal)? ; $(;)*)?
737 $(teardown = $teardown:ident $(,bench = $bench_teardown:literal)? ; $(;)*)?
738 $( config = $config:expr ; $(;)* )?
739 benchmark = |$cmd:literal, $group:ident: &mut BinaryBenchmarkGroup| $body:expr
740 ) => {
741 compile_error!(
742 "You are using a deprecated syntax of the binary_benchmark_group! macro to set up binary \
743 benchmarks. See the README (https://github.com/iai-callgrind/iai-callgrind), the \
744 CHANGELOG on the same page and docs (https://docs.rs/iai-callgrind/latest/iai_callgrind) \
745 for further details."
746 );
747 };
748 (
749 name = $name:ident; $(;)*
750 $( before = $before:ident $(,bench = $bench_before:literal)? ; $(;)* )?
751 $( after = $after:ident $(,bench = $bench_after:literal)? ; $(;)* )?
752 $( setup = $setup:ident $(,bench = $bench_setup:literal)? ; $(;)* )?
753 $( teardown = $teardown:ident $(,bench = $bench_teardown:literal )? ; $(;)* )?
754 $( config = $config:expr ; $(;)* )?
755 benchmark = |$group:ident: &mut BinaryBenchmarkGroup| $body:expr
756 ) => {
757 compile_error!(
758 "You are using a deprecated syntax of the binary_benchmark_group! macro to set up binary \
759 benchmarks. See the README (https://github.com/iai-callgrind/iai-callgrind), the \
760 CHANGELOG on the same page and docs (https://docs.rs/iai-callgrind/latest/iai_callgrind) \
761 for further details."
762 );
763 };
764 (
765 $( config = $config:expr ; $(;)* )?
766 $( compare_by_id = $compare:literal ; $(;)* )?
767 $( setup = $setup:expr; $(;)* )?
768 $( teardown = $teardown:expr; $(;)* )?
769 benchmarks = $( $function:ident ),+ $(,)*
770 ) => {
771 compile_error!(
772 "A binary_benchmark_group! needs a unique name. See the documentation of this macro for \
773 further details.\n\n\
774 hint = binary_benchmark_group!(name = some_ident; benchmarks = some_binary_benchmark);"
775 );
776 };
777 (
778 name = $name:ident; $(;)*
779 $( config = $config:expr; $(;)* )?
780 $( compare_by_id = $compare:literal; $(;)* )?
781 $( setup = $setup:expr; $(;)* )?
782 $( teardown = $teardown:expr; $(;)* )?
783 benchmarks =
784 ) => {
785 compile_error!(
786 "A binary_benchmark_group! needs at least 1 benchmark function which is annotated with \
787 #[binary_benchmark] or you can use the low level syntax. See the documentation of this \
788 macro for further details.\n\n\
789 hint = binary_benchmark_group!(name = some_ident; benchmarks = some_binary_benchmark);"
790 );
791 };
792 (
793 name = $name:ident; $(;)*
794 $( config = $config:expr ; $(;)* )?
795 $( compare_by_id = $compare:literal ; $(;)* )?
796 $( setup = $setup:expr; $(;)* )?
797 $( teardown = $teardown:expr; $(;)* )?
798 ) => {
799 compile_error!(
800 "A binary_benchmark_group! needs at least 1 benchmark function which is annotated with \
801 #[binary_benchmark] or you can use the low level syntax. See the documentation of this \
802 macro for further details.\n\n\
803 hint = binary_benchmark_group!(name = some_ident; benchmarks = some_binary_benchmark);"
804 );
805 };
806 (
807 name = $name:ident; $(;)*
808 $( config = $config:expr ; $(;)* )?
809 $( compare_by_id = $compare:literal ; $(;)* )?
810 $( setup = $setup:expr; $(;)* )?
811 $( teardown = $teardown:expr; $(;)* )?
812 benchmarks = $( $function:ident ),+ $(,)*
813 ) => {
814 pub mod $name {
815 use super::*;
816
817 pub const __IS_ATTRIBUTE: bool = true;
818
819 pub const __BENCHES: &[&(
820 &'static str,
821 fn() -> Option<$crate::__internal::InternalBinaryBenchmarkConfig>,
822 &[$crate::__internal::InternalMacroBinBench]
823 )]= &[
824 $(
825 &(
826 stringify!($function),
827 super::$function::__get_config,
828 super::$function::__BENCHES
829 )
830 ),+
831 ];
832
833 pub fn __run_setup(__run: bool) -> bool {
834 let mut __has_setup = false;
835 $(
836 __has_setup = true;
837 if __run {
838 $setup;
839 }
840 )?
841 __has_setup
842 }
843
844 pub fn __run_teardown(__run: bool) -> bool {
845 let mut __has_teardown = false;
846 $(
847 __has_teardown = true;
848 if __run {
849 $teardown;
850 }
851 )?
852 __has_teardown
853 }
854
855 pub fn __compare_by_id() -> Option<bool> {
856 let mut comp = None;
857 $(
858 comp = Some($compare);
859 )?
860 comp
861 }
862
863 pub fn __get_config() -> Option<$crate::__internal::InternalBinaryBenchmarkConfig> {
864 let mut config = None;
865 $(
866 config = Some($config.into());
867 )?
868 config
869 }
870
871 pub fn __run_bench_setup(group_index: usize, bench_index: usize) {
872 if let Some(setup) = __BENCHES[group_index].2[bench_index].setup {
873 setup();
874 };
875 }
876
877 pub fn __run_bench_teardown(group_index: usize, bench_index: usize) {
878 if let Some(teardown) = __BENCHES[group_index].2[bench_index].teardown {
879 teardown();
880 };
881 }
882
883 pub fn $name(_: &mut $crate::BinaryBenchmarkGroup) {}
884 }
885 };
886 (
887 $( config = $config:expr; $(;)* )?
888 $( compare_by_id = $compare:literal ; $(;)* )?
889 $( setup = $setup:expr; $(;)* )?
890 $( teardown = $teardown:expr; $(;)* )?
891 benchmarks = |$group:ident: &mut BinaryBenchmarkGroup| $body:expr
892 ) => {
893 compile_error!(
894 "A binary_benchmark_group! needs a unique name. See the documentation of this macro for \
895 further details.\n\n\
896 hint = binary_benchmark_group!(name = some_ident; benchmarks = |group: &mut BinaryBenchmarkGroup| ... );"
897 );
898 };
899 (
900 $( config = $config:expr; $(;)* )?
901 $( compare_by_id = $compare:literal ; $(;)* )?
902 $( setup = $setup:expr; $(;)* )?
903 $( teardown = $teardown:expr; $(;)* )?
904 benchmarks = |$group:ident| $body:expr
905 ) => {
906 compile_error!(
907 "A binary_benchmark_group! needs a unique name. See the documentation of this macro for \
908 further details.\n\n\
909 hint = binary_benchmark_group!(name = some_ident; benchmarks = |group| ... );"
910 );
911 };
912 (
913 name = $name:ident; $(;)*
914 $( config = $config:expr; $(;)* )?
915 $( compare_by_id = $compare:literal ; $(;)* )?
916 $( setup = $setup:expr; $(;)* )?
917 $( teardown = $teardown:expr; $(;)* )?
918 benchmarks = |$group:ident|
919 ) => {
920 compile_error!(
921 "This low level form of the binary_benchmark_group! needs you to use the \
922 `BinaryBenchmarkGroup` to setup benchmarks. See the documentation of this macro for \
923 further details.\n\n\
924 hint = binary_benchmark_group!(name = some_ident; benchmarks = |group| { \
925 group.binary_benchmark(/* BinaryBenchmark::new */); });"
926 );
927 };
928 (
929 name = $name:ident; $(;)*
930 $( config = $config:expr; $(;)* )?
931 $( compare_by_id = $compare:literal ; $(;)* )?
932 $( setup = $setup:expr; $(;)* )?
933 $( teardown = $teardown:expr; $(;)* )?
934 benchmarks = |$group:ident: &mut BinaryBenchmarkGroup|
935 ) => {
936 compile_error!(
937 "This low level form of the binary_benchmark_group! needs you to use the \
938 `BinaryBenchmarkGroup` to setup benchmarks. See the documentation of this macro for \
939 further details.\n\n\
940 hint = binary_benchmark_group!(name = some_ident; benchmarks = |group: &mut \
941 BinaryBenchmarkGroup| { group.binary_benchmark(/* BinaryBenchmark::new */); });"
942 );
943 };
944 (
945 name = $name:ident; $(;)*
946 $( config = $config:expr; $(;)* )?
947 $( compare_by_id = $compare:literal ; $(;)* )?
948 $( setup = $setup:expr; $(;)* )?
949 $( teardown = $teardown:expr; $(;)* )?
950 benchmarks = |$group:ident: &mut BinaryBenchmarkGroup| $body:expr
951 ) => {
952 pub mod $name {
953 use super::*;
954
955 pub const __IS_ATTRIBUTE: bool = false;
956
957 pub const __BENCHES: &[&(
958 &'static str,
959 fn() -> Option<$crate::__internal::InternalBinaryBenchmarkConfig>,
960 &[$crate::__internal::InternalMacroBinBench]
961 )]= &[];
962
963 pub fn __run_setup(__run: bool) -> bool {
964 let mut __has_setup = false;
965 $(
966 __has_setup = true;
967 if __run {
968 $setup;
969 }
970 )?
971 __has_setup
972 }
973
974 pub fn __run_teardown(__run: bool) -> bool {
975 let mut __has_teardown = false;
976 $(
977 __has_teardown = true;
978 if __run {
979 $teardown;
980 }
981 )?
982 __has_teardown
983 }
984
985 pub fn __get_config() -> Option<$crate::__internal::InternalBinaryBenchmarkConfig> {
986 let mut config = None;
987 $(
988 config = Some($config.into());
989 )?
990 config
991 }
992
993 pub fn __compare_by_id() -> Option<bool> {
994 let mut comp = None;
995 $(
996 comp = Some($compare);
997 )?
998 comp
999 }
1000
1001 pub fn __run_bench_setup(group_index: usize, bench_index: usize) {
1002 let mut group = $crate::BinaryBenchmarkGroup::default();
1003 $name(&mut group);
1004
1005 let bench = group
1006 .binary_benchmarks
1007 .iter()
1008 .nth(group_index)
1009 .expect("The group index for setup should be present");
1010 // In the runner each command is a `BinBench` and it is the index of the command
1011 // which we're getting back from the runner. So, we have to iterate over the
1012 // commands of each Bench to extract the correct setup function.
1013 //
1014 // commands => bench_index => The correct setup function
1015 // bench.benches[0].commands = [a, b] => 0, 1 => bench.benches[0].setup
1016 // bench.benches[1].commands = [c] => 2 => bench.benches[1].setup
1017 // bench.benches[2].commands = [d, e] => 3, 4 => bench.benches[2].setup
1018 //
1019 // We also need to take care of that there can be a global setup function
1020 // `BinaryBenchmark::setup`, which can be overridden by a `Bench::setup`
1021 if let Some(setup) = bench
1022 .benches
1023 .iter()
1024 .flat_map(|b| b.commands.iter().map(|c| (b.setup, c)))
1025 .nth(bench_index)
1026 .map(|(setup, _)| setup)
1027 .expect("The bench index for setup should be present") {
1028 setup();
1029 } else if let Some(setup) = bench.setup {
1030 setup();
1031 } else {
1032 // This branch should be unreachable so we do nothing
1033 }
1034 }
1035
1036 pub fn __run_bench_teardown(group_index: usize, bench_index: usize) {
1037 let mut group = $crate::BinaryBenchmarkGroup::default();
1038 $name(&mut group);
1039
1040 let bench = group
1041 .binary_benchmarks
1042 .iter()
1043 .nth(group_index)
1044 .expect("The group index for teardown should be present");
1045 if let Some(teardown) = bench
1046 .benches
1047 .iter()
1048 .flat_map(|b| b.commands.iter().map(|c| (b.teardown, c)))
1049 .nth(bench_index)
1050 .map(|(teardown, _)| teardown)
1051 .expect("The bench index for teardown should be present") {
1052 teardown();
1053 } else if let Some(teardown) = bench.teardown {
1054 teardown();
1055 } else {
1056 // This branch should be unreachable so we do nothing
1057 }
1058 }
1059
1060 #[inline(never)]
1061 pub fn $name($group: &mut $crate::BinaryBenchmarkGroup) {
1062 $body;
1063 }
1064 }
1065 };
1066 (
1067 name = $name:ident; $(;)*
1068 $( config = $config:expr; $(;)* )?
1069 $( compare_by_id = $compare:literal ; $(;)* )?
1070 $( setup = $setup:expr; $(;)* )?
1071 $( teardown = $teardown:expr; $(;)* )?
1072 benchmarks = |$group:ident| $body:expr
1073 ) => {
1074 binary_benchmark_group!(
1075 name = $name;
1076 $( config = $config; )?
1077 $( compare_by_id = $compare; )?
1078 $( setup = $setup; )?
1079 $( teardown = $teardown; )?
1080 benchmarks = |$group: &mut BinaryBenchmarkGroup| $body
1081 );
1082 };
1083}
1084
1085/// Macro used to define a group of library benchmarks
1086///
1087/// A small introductory example which shows the basic setup. This macro only accepts benchmarks
1088/// annotated with `#[library_benchmark]` ([`crate::library_benchmark`]).
1089///
1090/// ```rust
1091/// use iai_callgrind::{library_benchmark_group, library_benchmark};
1092///
1093/// #[library_benchmark]
1094/// fn bench_something() -> u64 {
1095/// 42
1096/// }
1097///
1098/// library_benchmark_group!(
1099/// name = my_group;
1100/// benchmarks = bench_something
1101/// );
1102///
1103/// # fn main() {
1104/// iai_callgrind::main!(library_benchmark_groups = my_group);
1105/// # }
1106/// ```
1107///
1108/// To be benchmarked a `library_benchmark_group` has to be added to the `main!` macro by adding its
1109/// name to the `library_benchmark_groups` argument of the `main!` macro. See there for further
1110/// details about the [`crate::main`] macro.
1111///
1112/// The following top-level arguments are accepted in this order:
1113///
1114/// ```rust
1115/// # use iai_callgrind::{library_benchmark, library_benchmark_group, LibraryBenchmarkConfig};
1116/// # #[library_benchmark]
1117/// # fn some_func() {}
1118/// fn group_setup() {}
1119/// fn group_teardown() {}
1120/// library_benchmark_group!(
1121/// name = my_group;
1122/// config = LibraryBenchmarkConfig::default();
1123/// compare_by_id = false;
1124/// setup = group_setup();
1125/// teardown = group_teardown();
1126/// benchmarks = some_func
1127/// );
1128/// # fn main() {
1129/// # }
1130/// ```
1131///
1132/// * __`name`__ (mandatory): A unique name used to identify the group for the `main!` macro
1133/// * __`config`__ (optional): A [`crate::LibraryBenchmarkConfig`] which is applied to all
1134/// benchmarks within the same group.
1135/// * __`compare_by_id`__ (optional): The default is false. If true, all benches in the benchmark
1136/// functions specified with the `benchmarks` argument, across any benchmark groups, are compared
1137/// with each other as long as the ids (the part after the `::` in `#[bench::id(...)]`) match.
1138/// * __`setup`__ (optional): A setup function or any valid expression which is run before all
1139/// benchmarks of this group
1140/// * __`teardown`__ (optional): A teardown function or any valid expression which is run after all
1141/// benchmarks of this group
1142/// * __`benchmarks`__ (mandatory): A list of comma separated benchmark functions which must be
1143/// annotated with `#[library_benchmark]`
1144#[macro_export]
1145macro_rules! library_benchmark_group {
1146 (
1147 $( config = $config:expr ; $(;)* )?
1148 $( compare_by_id = $compare:literal ; $(;)* )?
1149 $( setup = $setup:expr ; $(;)* )?
1150 $( teardown = $teardown:expr ; $(;)* )?
1151 benchmarks = $( $function:ident ),+
1152 ) => {
1153 compile_error!("A library_benchmark_group! needs a name\n\nlibrary_benchmark_group!(name = some_ident; benchmarks = ...);");
1154 };
1155 (
1156 name = $name:ident;
1157 $( config = $config:expr ; $(;)* )?
1158 $( compare_by_id = $compare:literal ; $(;)* )?
1159 $( setup = $setup:expr ; $(;)* )?
1160 $( teardown = $teardown:expr ; $(;)* )?
1161 benchmarks =
1162 ) => {
1163 compile_error!(
1164 "A library_benchmark_group! needs at least 1 benchmark function \
1165 annotated with #[library_benchmark]\n\n\
1166 library_benchmark_group!(name = some_ident; benchmarks = some_library_benchmark);");
1167 };
1168 (
1169 name = $name:ident; $(;)*
1170 $( config = $config:expr ; $(;)* )?
1171 $( compare_by_id = $compare:literal ; $(;)* )?
1172 $( setup = $setup:expr ; $(;)* )?
1173 $( teardown = $teardown:expr ; $(;)* )?
1174 benchmarks = $( $function:ident ),+ $(,)*
1175 ) => {
1176 pub mod $name {
1177 use super::*;
1178
1179 pub const __BENCHES: &[&(
1180 &'static str,
1181 fn() -> Option<$crate::__internal::InternalLibraryBenchmarkConfig>,
1182 &[$crate::__internal::InternalMacroLibBench]
1183 )]= &[
1184 $(
1185 &(
1186 stringify!($function),
1187 super::$function::__get_config,
1188 super::$function::__BENCHES
1189 )
1190 ),+
1191 ];
1192
1193 #[inline(never)]
1194 pub fn __get_config() -> Option<$crate::__internal::InternalLibraryBenchmarkConfig> {
1195 let mut config: Option<$crate::__internal::InternalLibraryBenchmarkConfig> = None;
1196 $(
1197 config = Some($config.into());
1198 )?
1199 config
1200 }
1201
1202 #[inline(never)]
1203 pub fn __compare_by_id() -> Option<bool> {
1204 let mut comp = None;
1205 $(
1206 comp = Some($compare);
1207 )?
1208 comp
1209 }
1210
1211 #[inline(never)]
1212 pub fn __run_setup(__run: bool) -> bool {
1213 let mut __has_setup = false;
1214 $(
1215 __has_setup = true;
1216 if __run {
1217 $setup;
1218 }
1219 )?
1220 __has_setup
1221 }
1222
1223 #[inline(never)]
1224 pub fn __run_teardown(__run: bool) -> bool {
1225 let mut __has_teardown = false;
1226 $(
1227 __has_teardown = true;
1228 if __run {
1229 $teardown;
1230 }
1231 )?
1232 __has_teardown
1233 }
1234
1235 #[inline(never)]
1236 pub fn __run(group_index: usize, bench_index: usize) {
1237 (__BENCHES[group_index].2[bench_index].func)();
1238 }
1239 }
1240 };
1241}