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}