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, you can specify callgrind arguments via
139/// [`crate::LibraryBenchmarkConfig::callgrind_args`]:
140///
141/// ```rust
142/// # use iai_callgrind::{main, library_benchmark_group, library_benchmark, LibraryBenchmarkConfig};
143/// # #[library_benchmark]
144/// # fn bench_fibonacci() { }
145/// # library_benchmark_group!(
146/// # name = some_group;
147/// # benchmarks = bench_fibonacci
148/// # );
149/// # fn main() {
150/// main!(
151/// config = LibraryBenchmarkConfig::default()
152/// .callgrind_args(
153/// ["--arg-with-flags=yes", "arg-without-flags=is_ok_too"]
154/// );
155/// library_benchmark_groups = some_group
156/// );
157/// # }
158/// ```
159///
160/// See also [Callgrind Command-line
161/// options](https://valgrind.org/docs/manual/cl-manual.html#cl-manual.options).
162///
163/// For an in-depth description of library benchmarks and more examples see the
164/// [README#Library
165/// Benchmarks](https://github.com/iai-callgrind/iai-callgrind#library-benchmarks) of this
166/// crate.
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::error::Errors> {
234 let mut this_args = std::env::args();
235 let exe = option_env!("IAI_CALLGRIND_RUNNER")
236 .unwrap_or_else(|| option_env!("CARGO_BIN_EXE_iai-callgrind-runner").unwrap_or("iai-callgrind-runner"));
237
238 let library_version = "0.14.0";
239
240 let mut cmd = std::process::Command::new(exe);
241
242 cmd.arg(library_version);
243 cmd.arg("--bin-bench");
244 cmd.arg(env!("CARGO_MANIFEST_DIR"));
245 cmd.arg(env!("CARGO_PKG_NAME"));
246 cmd.arg(file!());
247 cmd.arg(module_path!());
248 cmd.arg(this_args.next().unwrap()); // The executable benchmark binary
249
250 let mut config: Option<$crate::internal::InternalBinaryBenchmarkConfig> = None;
251 $(
252 config = Some($config.into());
253 )?
254
255 let mut internal_benchmark_groups = $crate::internal::InternalBinaryBenchmarkGroups {
256 config: config.unwrap_or_default(),
257 command_line_args: this_args.collect(),
258 has_setup: __run_setup(false),
259 has_teardown: __run_teardown(false),
260 ..Default::default()
261 };
262
263 let mut errors = $crate::error::Errors::default();
264
265 $(
266 if $group::__IS_ATTRIBUTE {
267 let mut internal_group = $crate::internal::InternalBinaryBenchmarkGroup {
268 id: stringify!($group).to_owned(),
269 config: $group::__get_config(),
270 binary_benchmarks: vec![],
271 has_setup: $group::__run_setup(false),
272 has_teardown: $group::__run_teardown(false),
273 compare_by_id: $group::__compare_by_id()
274 };
275 for (function_name, get_config, macro_bin_benches) in $group::__BENCHES {
276 let mut internal_binary_benchmark =
277 $crate::internal::InternalBinaryBenchmark {
278 benches: vec![],
279 config: get_config()
280 };
281 for macro_bin_bench in macro_bin_benches.iter() {
282 let bench = $crate::internal::InternalBinaryBenchmarkBench {
283 id: macro_bin_bench.id_display.map(|i| i.to_string()),
284 args: macro_bin_bench.args_display.map(|i| i.to_string()),
285 function_name: function_name.to_string(),
286 command: (macro_bin_bench.func)().into(),
287 config: macro_bin_bench.config.map(|f| f()),
288 has_setup: macro_bin_bench.setup.is_some(),
289 has_teardown: macro_bin_bench.teardown.is_some()
290 };
291 internal_binary_benchmark.benches.push(bench);
292 }
293 internal_group.binary_benchmarks.push(internal_binary_benchmark);
294 }
295
296 internal_benchmark_groups.groups.push(internal_group);
297 } else {
298 let mut group = $crate::BinaryBenchmarkGroup::default();
299 $group::$group(&mut group);
300
301 let module_path = module_path!();
302
303 let mut internal_group = $crate::internal::InternalBinaryBenchmarkGroup {
304 id: stringify!($group).to_owned(),
305 config: $group::__get_config(),
306 binary_benchmarks: vec![],
307 has_setup: $group::__run_setup(false),
308 has_teardown: $group::__run_teardown(false),
309 compare_by_id: $group::__compare_by_id()
310 };
311
312 let mut binary_benchmark_ids =
313 std::collections::HashSet::<$crate::BenchmarkId>::new();
314
315 if group.binary_benchmarks.is_empty() {
316 errors.add(
317 $crate::error::Error::GroupError(
318 module_path.to_owned(),
319 internal_group.id.clone(),
320 "This group needs at least one benchmark".to_owned()
321 )
322 );
323 }
324
325 for binary_benchmark in group.binary_benchmarks {
326 if let Err(message) = binary_benchmark.id.validate() {
327 errors.add(
328 $crate::error::Error::BinaryBenchmarkError(
329 module_path.to_owned(),
330 internal_group.id.clone(),
331 binary_benchmark.id.to_string(),
332 message
333 )
334 );
335 continue;
336 }
337 if !binary_benchmark_ids.insert(binary_benchmark.id.clone()) {
338 errors.add(
339 $crate::error::Error::BinaryBenchmarkError(
340 module_path.to_owned(),
341 internal_group.id.clone(),
342 binary_benchmark.id.to_string(),
343 "Duplicate binary benchmark id".to_owned()
344 )
345 );
346 continue;
347 }
348
349 let mut internal_binary_benchmark =
350 $crate::internal::InternalBinaryBenchmark {
351 benches: vec![],
352 config: binary_benchmark.config.map(Into::into)
353 };
354
355 let mut bench_ids =
356 std::collections::HashSet::<$crate::BenchmarkId>::new();
357
358 if binary_benchmark.benches.is_empty() {
359 errors.add(
360 $crate::error::Error::BinaryBenchmarkError(
361 module_path.to_owned(),
362 internal_group.id.clone(),
363 binary_benchmark.id.to_string(),
364 "This binary benchmark needs at least one bench".to_owned()
365 )
366 );
367 }
368
369 for bench in binary_benchmark.benches {
370 match bench.commands.as_slice() {
371 [] => {
372 errors.add(
373 $crate::error::Error::BenchError(
374 module_path.to_owned(),
375 internal_group.id.clone(),
376 binary_benchmark.id.to_string(),
377 bench.id.to_string(),
378 "Missing command".to_owned()
379 )
380 );
381 },
382 [command] => {
383 if let Err(message) = bench.id.validate() {
384 errors.add(
385 $crate::error::Error::BenchError(
386 module_path.to_owned(),
387 internal_group.id.clone(),
388 binary_benchmark.id.to_string(),
389 bench.id.to_string(),
390 message
391 )
392 );
393 }
394 if !bench_ids.insert(bench.id.clone()) {
395 errors.add(
396 $crate::error::Error::BenchError(
397 module_path.to_owned(),
398 internal_group.id.clone(),
399 binary_benchmark.id.to_string(),
400 bench.id.to_string(),
401 format!("Duplicate id: '{}'", bench.id)
402 )
403 );
404 }
405 let internal_bench =
406 $crate::internal::InternalBinaryBenchmarkBench {
407 id: Some(bench.id.into()),
408 args: None,
409 function_name: binary_benchmark.id.clone().into(),
410 command: command.into(),
411 config: bench.config.clone(),
412 has_setup: bench.setup.is_some()
413 || binary_benchmark.setup.is_some(),
414 has_teardown: bench.teardown.is_some()
415 || binary_benchmark.teardown.is_some(),
416 };
417 internal_binary_benchmark.benches.push(internal_bench);
418 },
419 commands => {
420 for (index, command) in commands.iter().enumerate() {
421 let bench_id: $crate::BenchmarkId = format!("{}_{}", bench.id, index).into();
422 if let Err(message) = bench_id.validate() {
423 errors.add(
424 $crate::error::Error::BenchError(
425 module_path.to_owned(),
426 internal_group.id.clone(),
427 binary_benchmark.id.to_string(),
428 bench_id.to_string(),
429 message
430 )
431 );
432 continue;
433 }
434 if !bench_ids.insert(bench_id.clone()) {
435 errors.add(
436 $crate::error::Error::BenchError(
437 module_path.to_owned(),
438 internal_group.id.clone(),
439 binary_benchmark.id.to_string(),
440 bench.id.to_string(),
441 format!("Duplicate id: '{}'", bench_id)
442 )
443 );
444 continue;
445 }
446 let internal_bench =
447 $crate::internal::InternalBinaryBenchmarkBench {
448 id: Some(bench_id.into()),
449 args: None,
450 function_name: binary_benchmark.id.to_string(),
451 command: command.into(),
452 config: bench.config.clone(),
453 has_setup: bench.setup.is_some()
454 || binary_benchmark.setup.is_some(),
455 has_teardown: bench.teardown.is_some()
456 || binary_benchmark.teardown.is_some(),
457 };
458 internal_binary_benchmark.benches.push(internal_bench);
459 }
460 }
461 }
462 }
463 internal_group.binary_benchmarks.push(internal_binary_benchmark);
464 }
465
466 internal_benchmark_groups.groups.push(internal_group);
467 }
468 )+
469
470 if !errors.is_empty() {
471 return Err(errors);
472 }
473
474 let encoded = $crate::bincode::serialize(&internal_benchmark_groups).expect("Encoded benchmark");
475 let mut child = cmd
476 .arg(encoded.len().to_string())
477 .stdin(std::process::Stdio::piped())
478 .spawn()
479 .expect("Failed to run benchmarks. \
480 Is iai-callgrind-runner installed and iai-callgrind-runner in your $PATH?. \
481 You can also set the environment variable IAI_CALLGRIND_RUNNER to the \
482 absolute path of the iai-callgrind-runner executable.");
483
484 let mut stdin = child.stdin.take().expect("Opening stdin to submit encoded benchmark");
485 std::thread::spawn(move || {
486 use std::io::Write;
487 stdin.write_all(&encoded).expect("Writing encoded benchmark to stdin");
488 });
489
490 let status = child.wait().expect("Wait for child process to exit");
491 if !status.success() {
492 std::process::exit(1);
493 }
494
495 Ok(())
496 }
497
498 fn __run_setup(__run: bool) -> bool {
499 let mut __has_setup = false;
500 $(
501 __has_setup = true;
502 if __run {
503 $setup;
504 }
505 )?
506 __has_setup
507 }
508
509 fn __run_teardown(__run: bool) -> bool {
510 let mut __has_teardown = false;
511 $(
512 __has_teardown = true;
513 if __run {
514 $teardown;
515 }
516 )?
517 __has_teardown
518 }
519
520 fn main() {
521 let mut args_iter = std::env::args().skip(1);
522 if args_iter
523 .next()
524 .as_ref()
525 .map_or(false, |value| value == "--iai-run")
526 {
527 let mut current = args_iter.next().expect("Expecting a function type");
528 let next = args_iter.next();
529 match (current.as_str(), next) {
530 ("setup", None) => {
531 __run_setup(true);
532 },
533 ("teardown", None) => {
534 __run_teardown(true);
535 },
536 $(
537 (group @ stringify!($group), Some(next)) => {
538 let current = next;
539 let next = args_iter.next();
540
541 match (current.as_str(), next) {
542 ("setup", None) => {
543 $group::__run_setup(true);
544 },
545 ("teardown", None) => {
546 $group::__run_teardown(true);
547 }
548 (key @ ("setup" | "teardown"), Some(next)) => {
549 let group_index = next
550 .parse::<usize>()
551 .expect("The group index should be a number");
552 let bench_index = args_iter
553 .next()
554 .expect("The bench index should be present")
555 .parse::<usize>()
556 .expect("The bench index should be a number");
557 if key == "setup" {
558 $group::__run_bench_setup(group_index, bench_index);
559 } else {
560 $group::__run_bench_teardown(group_index, bench_index);
561 }
562 }
563 (name, _) => panic!("Invalid function '{}' in group '{}'", name, group)
564 }
565 }
566 )+
567 (name, _) => panic!("function '{}' not found in this scope", name)
568 }
569 } else {
570 if let Err(errors) = __run() {
571 eprintln!("{errors}");
572 std::process::exit(1);
573 }
574 };
575 }
576 };
577 (
578 $( config = $config:expr; $(;)* )?
579 $( setup = $setup:expr ; $(;)* )?
580 $( teardown = $teardown:expr ; $(;)* )?
581 library_benchmark_groups =
582 ) => {
583 compile_error!("The library_benchmark_groups argument needs at least one `name` of a `library_benchmark_group!`");
584 };
585 (
586 $( config = $config:expr ; $(;)* )?
587 $( setup = $setup:expr ; $(;)* )?
588 $( teardown = $teardown:expr ; $(;)* )?
589 library_benchmark_groups = $( $group:ident ),+ $(,)*
590 ) => {
591 #[inline(never)]
592 fn __run() {
593 let mut this_args = std::env::args();
594 let exe = option_env!("IAI_CALLGRIND_RUNNER")
595 .unwrap_or_else(|| option_env!("CARGO_BIN_EXE_iai-callgrind-runner").unwrap_or("iai-callgrind-runner"));
596
597 let library_version = "0.14.0";
598
599 let mut cmd = std::process::Command::new(exe);
600
601 cmd.arg(library_version);
602 cmd.arg("--lib-bench");
603 cmd.arg(env!("CARGO_MANIFEST_DIR"));
604 cmd.arg(env!("CARGO_PKG_NAME"));
605 cmd.arg(file!());
606 cmd.arg(module_path!());
607 cmd.arg(this_args.next().unwrap()); // The executable benchmark binary
608
609 let mut config: Option<$crate::internal::InternalLibraryBenchmarkConfig> = None;
610 $(
611 config = Some($config.into());
612 )?
613
614 let mut internal_benchmark_groups = $crate::internal::InternalLibraryBenchmarkGroups {
615 config: config.unwrap_or_default(),
616 command_line_args: this_args.collect(),
617 has_setup: __run_setup(false),
618 has_teardown: __run_teardown(false),
619 ..Default::default()
620 };
621
622 $(
623 let mut internal_group = $crate::internal::InternalLibraryBenchmarkGroup {
624 id: stringify!($group).to_owned(),
625 config: $group::__get_config(),
626 compare_by_id: $group::__compare_by_id(),
627 library_benchmarks: vec![],
628 has_setup: $group::__run_setup(false),
629 has_teardown: $group::__run_teardown(false),
630 };
631 for (function_name, get_config, macro_lib_benches) in $group::__BENCHES {
632 let mut benches = $crate::internal::InternalLibraryBenchmarkBenches {
633 benches: vec![],
634 config: get_config()
635 };
636 for macro_lib_bench in macro_lib_benches.iter() {
637 let bench = $crate::internal::InternalLibraryBenchmarkBench {
638 id: macro_lib_bench.id_display.map(|i| i.to_string()),
639 args: macro_lib_bench.args_display.map(|i| i.to_string()),
640 function_name: function_name.to_string(),
641 config: macro_lib_bench.config.map(|f| f()),
642 };
643 benches.benches.push(bench);
644 }
645 internal_group.library_benchmarks.push(benches);
646 }
647
648 internal_benchmark_groups.groups.push(internal_group);
649 )+
650
651 let encoded = $crate::bincode::serialize(&internal_benchmark_groups).expect("Encoded benchmark");
652 let mut child = cmd
653 .arg(encoded.len().to_string())
654 .stdin(std::process::Stdio::piped())
655 .spawn()
656 .expect("Failed to run benchmarks. \
657 Is iai-callgrind-runner installed and iai-callgrind-runner in your $PATH?. \
658 You can also set the environment variable IAI_CALLGRIND_RUNNER to the \
659 absolute path of the iai-callgrind-runner executable.");
660
661 let mut stdin = child.stdin.take().expect("Opening stdin to submit encoded benchmark");
662 std::thread::spawn(move || {
663 use std::io::Write;
664 stdin.write_all(&encoded).expect("Writing encoded benchmark to stdin");
665 });
666
667 let status = child.wait().expect("Wait for child process to exit");
668 if !status.success() {
669 std::process::exit(1);
670 }
671 }
672
673 #[inline(never)]
674 fn __run_setup(__run: bool) -> bool {
675 let mut __has_setup = false;
676 $(
677 __has_setup = true;
678 if __run {
679 $setup;
680 }
681 )?
682 __has_setup
683 }
684
685 #[inline(never)]
686 fn __run_teardown(__run: bool) -> bool {
687 let mut __has_teardown = false;
688 $(
689 __has_teardown = true;
690 if __run {
691 $teardown;
692 }
693 )?
694 __has_teardown
695 }
696
697 fn main() {
698 let mut args_iter = std::hint::black_box(std::env::args()).skip(1);
699 if args_iter
700 .next()
701 .as_ref()
702 .map_or(false, |value| value == "--iai-run")
703 {
704 let current = std::hint::black_box(args_iter.next().expect("Expecting a function type"));
705 let next = std::hint::black_box(args_iter.next());
706 match current.as_str() {
707 "setup" if next.is_none() => {
708 __run_setup(true);
709 },
710 "teardown" if next.is_none() => {
711 __run_teardown(true);
712 },
713 $(
714 stringify!($group) => {
715 match std::hint::black_box(
716 next
717 .expect("An argument `setup`, `teardown` or an index should be present")
718 .as_str()
719 ) {
720 "setup" => {
721 $group::__run_setup(true);
722 },
723 "teardown" => {
724 $group::__run_teardown(true);
725 }
726 value => {
727 let group_index = std::hint::black_box(
728 value
729 .parse::<usize>()
730 .expect("Expecting a valid group index")
731 );
732 let bench_index = std::hint::black_box(
733 args_iter
734 .next()
735 .expect("A bench index should be present")
736 .parse::<usize>()
737 .expect("Expecting a valid bench index")
738 );
739 $group::__run(group_index, bench_index);
740 }
741 }
742 }
743 )+
744 name => panic!("function '{}' not found in this scope", name)
745 }
746 } else {
747 std::hint::black_box(__run());
748 };
749 }
750 };
751 (
752 callgrind_args = $( $args:literal ),* $(,)*; $(;)*
753 functions = $( $func_name:ident ),+ $(,)*
754 ) => {
755 compile_error!(
756 "You are using a deprecated syntax of the main! macro to set up library benchmarks. \
757 See the README (https://github.com/iai-callgrind/iai-callgrind) and \
758 docs (https://docs.rs/iai-callgrind/latest/iai_callgrind/) for further details."
759 );
760 pub fn main() {}
761 };
762 ( $( $func_name:ident ),+ $(,)* ) => {
763 compile_error!(
764 "You are using a deprecated syntax of the main! macro to set up library benchmarks. \
765 See the README (https://github.com/iai-callgrind/iai-callgrind) and \
766 docs (https://docs.rs/iai-callgrind/latest/iai_callgrind/) for further details."
767 );
768 pub fn main() {}
769 };
770}
771
772/// Macro used to define a group of binary benchmarks
773///
774/// There are two apis to set up binary benchmarks. The recommended way is to [use the
775/// `#[binary_benchmark]` attribute](#using-the-high-level-api-with-the-binary-benchmark-attribute).
776/// But, if you find yourself in the situation that the attribute isn't enough you can fall back to
777/// the [low level api](#the-low-level-api) or even [intermix both
778/// styles](#intermixing-both-apis).
779///
780/// # The macro's arguments in detail:
781///
782/// The following top-level arguments are accepted (in this order):
783///
784/// ```rust
785/// # use iai_callgrind::{binary_benchmark, binary_benchmark_group, BinaryBenchmarkGroup, BinaryBenchmarkConfig};
786/// # fn run_setup() {}
787/// # fn run_teardown() {}
788/// # #[binary_benchmark]
789/// # fn bench_binary() -> iai_callgrind::Command { iai_callgrind::Command::new("some") }
790/// binary_benchmark_group!(
791/// name = my_group;
792/// config = BinaryBenchmarkConfig::default();
793/// compare_by_id = false;
794/// setup = run_setup();
795/// teardown = run_teardown();
796/// benchmarks = bench_binary
797/// );
798/// # fn main() {
799/// # my_group::my_group(&mut BinaryBenchmarkGroup::default());
800/// # }
801/// ```
802///
803/// * __`name`__ (mandatory): A unique name used to identify the group for the `main!` macro
804/// * __`config`__ (optional): A [`crate::BinaryBenchmarkConfig`]
805/// * __`compare_by_id`__ (optional): The default is false. If true, all commands from the functions
806/// specified in the `benchmarks` argument, are compared with each other as long as the ids (the
807/// part after the `::` in `#[bench::id(...)]`) match.
808/// * __`setup`__ (optional): A function which is executed before all benchmarks in this group
809/// * __`teardown`__ (optional): A function which is executed after all benchmarks in this group
810/// * __`benchmarks`__ (mandatory): A `,`-separated list of `#[binary_benchmark]` annotated function
811/// names you want to put into this group. Or, if you want to use the low level api
812///
813/// `|IDENTIFIER: &mut BinaryBenchmarkGroup| EXPRESSION`
814///
815/// or the shorter `|IDENTIFIER| EXPRESSION`
816///
817/// where `IDENTIFIER` is the identifier of your choice for the `BinaryBenchmarkGroup` (we use
818/// `group` throughout our examples) and `EXPRESSION` is the code where you make use of the
819/// `BinaryBenchmarkGroup` to set up the binary benchmarks
820///
821/// # Using the high-level api with the `#[binary benchmark]` attribute
822///
823/// A small introductory example which demonstrates the basic setup (assuming a crate's binary is
824/// named `my-foo`):
825///
826/// ```rust
827/// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
828/// use iai_callgrind::{binary_benchmark_group, BinaryBenchmarkGroup, binary_benchmark};
829///
830/// #[binary_benchmark]
831/// #[bench::hello_world("hello world")]
832/// #[bench::foo("foo")]
833/// #[benches::multiple("bar", "baz")]
834/// fn bench_binary(arg: &str) -> iai_callgrind::Command {
835/// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
836/// .arg(arg)
837/// .build()
838/// }
839///
840/// binary_benchmark_group!(
841/// name = my_group;
842/// benchmarks = bench_binary
843/// );
844///
845/// # fn main() {
846/// iai_callgrind::main!(binary_benchmark_groups = my_group);
847/// # }
848/// ```
849///
850/// To be benchmarked a `binary_benchmark_group` has to be added to the `main!` macro by adding its
851/// name to the `binary_benchmark_groups` argument of the `main!` macro. See there for further
852/// details about the [`crate::main`] macro. See the documentation of [`crate::binary_benchmark`]
853/// for more details about the attribute itself and the inner attributes `#[bench]` and
854/// `#[benches]`.
855///
856/// # The low-level api
857///
858/// Using the low-level api has advantages but when it comes to stability in terms of usability, the
859/// low level api might be considered less stable. What does this mean? If we have to make changes
860/// to the inner workings of iai-callgrind which not necessarily change the high-level api it is
861/// more likely that the low-level api has to be adjusted. This implies you might have to adjust
862/// your benchmarks more often with a version update of `iai-callgrind`. Hence, it is recommended to
863/// use the high-level api as much as possible and only use the low-level api under special
864/// circumstances. You can also [intermix both styles](#intermixing-both-apis)!
865///
866/// The low-level api mirrors the high-level constructs as close as possible. The
867/// [`crate::BinaryBenchmarkGroup`] is a special case, since we use the information from the
868/// `binary_benchmark_group!` macro [arguments](#the-macros-arguments-in-detail) (__`name`__,
869/// __`config`__, ...) to create the `BinaryBenchmarkGroup` and pass it to the `benchmarks`
870/// argument.
871///
872/// That being said, here's the basic usage:
873///
874/// ```rust
875/// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
876/// use iai_callgrind::{binary_benchmark_group, BinaryBenchmark, Bench};
877///
878/// binary_benchmark_group!(
879/// // All the other options from the `binary_benchmark_group` are used as usual
880/// name = my_group;
881///
882/// // Note there's also the shorter form `benchmarks = |group|` but in the examples we want
883/// // to be more explicit
884/// benchmarks = |group: &mut BinaryBenchmarkGroup| {
885///
886/// // We have chosen `group` to be our identifier but it can be anything
887/// group.binary_benchmark(
888///
889/// // This is the equivalent of the `#[binary_benchmark]` attribute. The `id`
890/// // mirrors the function name of the `#[binary_benchmark]` annotated function.
891/// BinaryBenchmark::new("some_id")
892/// .bench(
893///
894/// // The equivalent of the `#[bench]` attribute.
895/// Bench::new("my_bench_id")
896/// .command(
897///
898/// // The `Command` stays the same
899/// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
900/// .arg("foo").build()
901/// )
902/// )
903/// )
904/// }
905/// );
906/// # fn main() {}
907/// ```
908///
909/// Depending on your IDE, it's nicer to work with the code after the `|group: &mut
910/// BinaryBenchmarkGroup|` if it resides in a separate function rather than the macro itself as in
911///
912/// ```rust
913/// use iai_callgrind::{binary_benchmark_group, BinaryBenchmark, Bench, BinaryBenchmarkGroup};
914///
915/// fn setup_my_group(group: &mut BinaryBenchmarkGroup) {
916/// // Enjoy all the features of your IDE ...
917/// }
918///
919/// binary_benchmark_group!(
920/// name = my_group;
921/// benchmarks = |group: &mut BinaryBenchmarkGroup| setup_my_group(group)
922/// );
923/// # fn main() {}
924/// ```
925///
926/// The list of all structs and macros used exclusively in the low-level api:
927/// * [`crate::BinaryBenchmarkGroup`]
928/// * [`crate::BinaryBenchmark`]: Mirrors the `#[binary_benchmark]` attribute
929/// * [`crate::Bench`]: Mirrors the `#[bench]` attribute
930/// * [`crate::binary_benchmark_attribute`]: Used to add a `#[binary_benchmark]` attributed function
931/// in [`crate::BinaryBenchmarkGroup::binary_benchmark`]
932/// * [`crate::BenchmarkId`]: The benchmark id is for example used in
933/// [`crate::BinaryBenchmark::new`] and [`crate::Bench::new`]
934///
935/// Note there's no equivalent for the `#[benches]` attribute. The [`crate::Bench`] behaves exactly
936/// as the `#[benches]` attribute if more than a single [`crate::Command`] is added.
937///
938/// # Intermixing both apis
939///
940/// For example, if you started with the `#[binary_benchmark]` attribute and noticed you are limited
941/// by it to set up all the [`crate::Command`]s the way you want, you can intermix both styles:
942///
943/// ```rust
944/// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
945/// use iai_callgrind::{
946/// binary_benchmark, binary_benchmark_group, BinaryBenchmark, Bench, BinaryBenchmarkGroup,
947/// binary_benchmark_attribute
948/// };
949///
950/// #[binary_benchmark]
951/// #[bench::foo("foo")]
952/// #[benches::multiple("bar", "baz")]
953/// fn bench_binary(arg: &str) -> iai_callgrind::Command {
954/// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
955/// .arg(arg)
956/// .build()
957/// }
958///
959/// fn setup_my_group(group: &mut BinaryBenchmarkGroup) {
960/// group
961/// // Simply add what you already have with the `binary_benchmark_attribute!` macro.
962/// // This macro returns a `BinaryBenchmark`, so you could even add more `Bench`es
963/// // to it instead of creating a new one as we do below
964/// .binary_benchmark(binary_benchmark_attribute!(bench_binary))
965/// .binary_benchmark(
966/// BinaryBenchmark::new("did_not_work_with_attribute")
967/// .bench(Bench::new("low_level")
968/// .command(
969/// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
970/// .arg("foo")
971/// .build()
972/// )
973/// )
974/// );
975/// }
976///
977/// binary_benchmark_group!(
978/// name = my_group;
979/// benchmarks = |group: &mut BinaryBenchmarkGroup| setup_my_group(group)
980/// );
981/// # fn main() {}
982/// ```
983#[macro_export]
984macro_rules! binary_benchmark_group {
985 (
986 name = $name:ident; $(;)*
987 $(before = $before:ident $(,bench = $bench_before:literal)? ; $(;)*)?
988 $(after = $after:ident $(,bench = $bench_after:literal)? ; $(;)*)?
989 $(setup = $setup:ident $(,bench = $bench_setup:literal)? ; $(;)*)?
990 $(teardown = $teardown:ident $(,bench = $bench_teardown:literal)? ; $(;)*)?
991 $( config = $config:expr ; $(;)* )?
992 benchmark = |$cmd:literal, $group:ident: &mut BinaryBenchmarkGroup| $body:expr
993 ) => {
994 compile_error!(
995 "You are using a deprecated syntax of the binary_benchmark_group! macro to set up binary \
996 benchmarks. See the README (https://github.com/iai-callgrind/iai-callgrind), the \
997 CHANGELOG on the same page and docs (https://docs.rs/iai-callgrind/latest/iai_callgrind) \
998 for further details."
999 );
1000 };
1001 (
1002 name = $name:ident; $(;)*
1003 $( before = $before:ident $(,bench = $bench_before:literal)? ; $(;)* )?
1004 $( after = $after:ident $(,bench = $bench_after:literal)? ; $(;)* )?
1005 $( setup = $setup:ident $(,bench = $bench_setup:literal)? ; $(;)* )?
1006 $( teardown = $teardown:ident $(,bench = $bench_teardown:literal )? ; $(;)* )?
1007 $( config = $config:expr ; $(;)* )?
1008 benchmark = |$group:ident: &mut BinaryBenchmarkGroup| $body:expr
1009 ) => {
1010 compile_error!(
1011 "You are using a deprecated syntax of the binary_benchmark_group! macro to set up binary \
1012 benchmarks. See the README (https://github.com/iai-callgrind/iai-callgrind), the \
1013 CHANGELOG on the same page and docs (https://docs.rs/iai-callgrind/latest/iai_callgrind) \
1014 for further details."
1015 );
1016 };
1017 (
1018 $( config = $config:expr ; $(;)* )?
1019 $( compare_by_id = $compare:literal ; $(;)* )?
1020 $( setup = $setup:expr; $(;)* )?
1021 $( teardown = $teardown:expr; $(;)* )?
1022 benchmarks = $( $function:ident ),+ $(,)*
1023 ) => {
1024 compile_error!(
1025 "A binary_benchmark_group! needs a unique name. See the documentation of this macro for \
1026 further details.\n\n\
1027 hint = binary_benchmark_group!(name = some_ident; benchmarks = some_binary_benchmark);"
1028 );
1029 };
1030 (
1031 name = $name:ident; $(;)*
1032 $( config = $config:expr; $(;)* )?
1033 $( compare_by_id = $compare:literal; $(;)* )?
1034 $( setup = $setup:expr; $(;)* )?
1035 $( teardown = $teardown:expr; $(;)* )?
1036 benchmarks =
1037 ) => {
1038 compile_error!(
1039 "A binary_benchmark_group! needs at least 1 benchmark function which is annotated with \
1040 #[binary_benchmark] or you can use the low level syntax. See the documentation of this \
1041 macro for further details.\n\n\
1042 hint = binary_benchmark_group!(name = some_ident; benchmarks = some_binary_benchmark);"
1043 );
1044 };
1045 (
1046 name = $name:ident; $(;)*
1047 $( config = $config:expr ; $(;)* )?
1048 $( compare_by_id = $compare:literal ; $(;)* )?
1049 $( setup = $setup:expr; $(;)* )?
1050 $( teardown = $teardown:expr; $(;)* )?
1051 ) => {
1052 compile_error!(
1053 "A binary_benchmark_group! needs at least 1 benchmark function which is annotated with \
1054 #[binary_benchmark] or you can use the low level syntax. See the documentation of this \
1055 macro for further details.\n\n\
1056 hint = binary_benchmark_group!(name = some_ident; benchmarks = some_binary_benchmark);"
1057 );
1058 };
1059 (
1060 name = $name:ident; $(;)*
1061 $( config = $config:expr ; $(;)* )?
1062 $( compare_by_id = $compare:literal ; $(;)* )?
1063 $( setup = $setup:expr; $(;)* )?
1064 $( teardown = $teardown:expr; $(;)* )?
1065 benchmarks = $( $function:ident ),+ $(,)*
1066 ) => {
1067 pub mod $name {
1068 use super::*;
1069
1070 pub const __IS_ATTRIBUTE: bool = true;
1071
1072 pub const __BENCHES: &[&(
1073 &'static str,
1074 fn() -> Option<$crate::internal::InternalBinaryBenchmarkConfig>,
1075 &[$crate::internal::InternalMacroBinBench]
1076 )]= &[
1077 $(
1078 &(
1079 stringify!($function),
1080 super::$function::__get_config,
1081 super::$function::__BENCHES
1082 )
1083 ),+
1084 ];
1085
1086 pub fn __run_setup(__run: bool) -> bool {
1087 let mut __has_setup = false;
1088 $(
1089 __has_setup = true;
1090 if __run {
1091 $setup;
1092 }
1093 )?
1094 __has_setup
1095 }
1096
1097 pub fn __run_teardown(__run: bool) -> bool {
1098 let mut __has_teardown = false;
1099 $(
1100 __has_teardown = true;
1101 if __run {
1102 $teardown;
1103 }
1104 )?
1105 __has_teardown
1106 }
1107
1108 pub fn __compare_by_id() -> Option<bool> {
1109 let mut comp = None;
1110 $(
1111 comp = Some($compare);
1112 )?
1113 comp
1114 }
1115
1116 pub fn __get_config() -> Option<$crate::internal::InternalBinaryBenchmarkConfig> {
1117 let mut config = None;
1118 $(
1119 config = Some($config.into());
1120 )?
1121 config
1122 }
1123
1124 pub fn __run_bench_setup(group_index: usize, bench_index: usize) {
1125 if let Some(setup) = __BENCHES[group_index].2[bench_index].setup {
1126 setup();
1127 };
1128 }
1129
1130 pub fn __run_bench_teardown(group_index: usize, bench_index: usize) {
1131 if let Some(teardown) = __BENCHES[group_index].2[bench_index].teardown {
1132 teardown();
1133 };
1134 }
1135
1136 pub fn $name(_: &mut $crate::BinaryBenchmarkGroup) {}
1137 }
1138 };
1139 (
1140 $( config = $config:expr; $(;)* )?
1141 $( compare_by_id = $compare:literal ; $(;)* )?
1142 $( setup = $setup:expr; $(;)* )?
1143 $( teardown = $teardown:expr; $(;)* )?
1144 benchmarks = |$group:ident: &mut BinaryBenchmarkGroup| $body:expr
1145 ) => {
1146 compile_error!(
1147 "A binary_benchmark_group! needs a unique name. See the documentation of this macro for \
1148 further details.\n\n\
1149 hint = binary_benchmark_group!(name = some_ident; benchmarks = |group: &mut BinaryBenchmarkGroup| ... );"
1150 );
1151 };
1152 (
1153 $( config = $config:expr; $(;)* )?
1154 $( compare_by_id = $compare:literal ; $(;)* )?
1155 $( setup = $setup:expr; $(;)* )?
1156 $( teardown = $teardown:expr; $(;)* )?
1157 benchmarks = |$group:ident| $body:expr
1158 ) => {
1159 compile_error!(
1160 "A binary_benchmark_group! needs a unique name. See the documentation of this macro for \
1161 further details.\n\n\
1162 hint = binary_benchmark_group!(name = some_ident; benchmarks = |group| ... );"
1163 );
1164 };
1165 (
1166 name = $name:ident; $(;)*
1167 $( config = $config:expr; $(;)* )?
1168 $( compare_by_id = $compare:literal ; $(;)* )?
1169 $( setup = $setup:expr; $(;)* )?
1170 $( teardown = $teardown:expr; $(;)* )?
1171 benchmarks = |$group:ident|
1172 ) => {
1173 compile_error!(
1174 "This low level form of the binary_benchmark_group! needs you to use the \
1175 `BinaryBenchmarkGroup` to setup benchmarks. See the documentation of this macro for \
1176 further details.\n\n\
1177 hint = binary_benchmark_group!(name = some_ident; benchmarks = |group| { \
1178 group.binary_benchmark(/* BinaryBenchmark::new */); });"
1179 );
1180 };
1181 (
1182 name = $name:ident; $(;)*
1183 $( config = $config:expr; $(;)* )?
1184 $( compare_by_id = $compare:literal ; $(;)* )?
1185 $( setup = $setup:expr; $(;)* )?
1186 $( teardown = $teardown:expr; $(;)* )?
1187 benchmarks = |$group:ident: &mut BinaryBenchmarkGroup|
1188 ) => {
1189 compile_error!(
1190 "This low level form of the binary_benchmark_group! needs you to use the \
1191 `BinaryBenchmarkGroup` to setup benchmarks. See the documentation of this macro for \
1192 further details.\n\n\
1193 hint = binary_benchmark_group!(name = some_ident; benchmarks = |group: &mut \
1194 BinaryBenchmarkGroup| { group.binary_benchmark(/* BinaryBenchmark::new */); });"
1195 );
1196 };
1197 (
1198 name = $name:ident; $(;)*
1199 $( config = $config:expr; $(;)* )?
1200 $( compare_by_id = $compare:literal ; $(;)* )?
1201 $( setup = $setup:expr; $(;)* )?
1202 $( teardown = $teardown:expr; $(;)* )?
1203 benchmarks = |$group:ident: &mut BinaryBenchmarkGroup| $body:expr
1204 ) => {
1205 pub mod $name {
1206 use super::*;
1207
1208 pub const __IS_ATTRIBUTE: bool = false;
1209
1210 pub const __BENCHES: &[&(
1211 &'static str,
1212 fn() -> Option<$crate::internal::InternalBinaryBenchmarkConfig>,
1213 &[$crate::internal::InternalMacroBinBench]
1214 )]= &[];
1215
1216 pub fn __run_setup(__run: bool) -> bool {
1217 let mut __has_setup = false;
1218 $(
1219 __has_setup = true;
1220 if __run {
1221 $setup;
1222 }
1223 )?
1224 __has_setup
1225 }
1226
1227 pub fn __run_teardown(__run: bool) -> bool {
1228 let mut __has_teardown = false;
1229 $(
1230 __has_teardown = true;
1231 if __run {
1232 $teardown;
1233 }
1234 )?
1235 __has_teardown
1236 }
1237
1238 pub fn __get_config() -> Option<$crate::internal::InternalBinaryBenchmarkConfig> {
1239 let mut config = None;
1240 $(
1241 config = Some($config.into());
1242 )?
1243 config
1244 }
1245
1246 pub fn __compare_by_id() -> Option<bool> {
1247 let mut comp = None;
1248 $(
1249 comp = Some($compare);
1250 )?
1251 comp
1252 }
1253
1254 pub fn __run_bench_setup(group_index: usize, bench_index: usize) {
1255 let mut group = $crate::BinaryBenchmarkGroup::default();
1256 $name(&mut group);
1257
1258 let bench = group
1259 .binary_benchmarks
1260 .iter()
1261 .nth(group_index)
1262 .expect("The group index for setup should be present");
1263 // In the runner each command is a `BinBench` and it is the index of the command
1264 // which we're getting back from the runner. So, we have to iterate over the
1265 // commands of each Bench to extract the correct setup function.
1266 //
1267 // commands => bench_index => The correct setup function
1268 // bench.benches[0].commands = [a, b] => 0, 1 => bench.benches[0].setup
1269 // bench.benches[1].commands = [c] => 2 => bench.benches[1].setup
1270 // bench.benches[2].commands = [d, e] => 3, 4 => bench.benches[2].setup
1271 //
1272 // We also need to take care of that there can be a global setup function
1273 // `BinaryBenchmark::setup`, which can be overridden by a `Bench::setup`
1274 if let Some(setup) = bench
1275 .benches
1276 .iter()
1277 .flat_map(|b| b.commands.iter().map(|c| (b.setup, c)))
1278 .nth(bench_index)
1279 .map(|(setup, _)| setup)
1280 .expect("The bench index for setup should be present") {
1281 setup();
1282 } else if let Some(setup) = bench.setup {
1283 setup();
1284 } else {
1285 // This branch should be unreachable so we do nothing
1286 }
1287 }
1288
1289 pub fn __run_bench_teardown(group_index: usize, bench_index: usize) {
1290 let mut group = $crate::BinaryBenchmarkGroup::default();
1291 $name(&mut group);
1292
1293 let bench = group
1294 .binary_benchmarks
1295 .iter()
1296 .nth(group_index)
1297 .expect("The group index for teardown should be present");
1298 if let Some(teardown) = bench
1299 .benches
1300 .iter()
1301 .flat_map(|b| b.commands.iter().map(|c| (b.teardown, c)))
1302 .nth(bench_index)
1303 .map(|(teardown, _)| teardown)
1304 .expect("The bench index for teardown should be present") {
1305 teardown();
1306 } else if let Some(teardown) = bench.teardown {
1307 teardown();
1308 } else {
1309 // This branch should be unreachable so we do nothing
1310 }
1311 }
1312
1313 #[inline(never)]
1314 pub fn $name($group: &mut $crate::BinaryBenchmarkGroup) {
1315 $body;
1316 }
1317 }
1318 };
1319 (
1320 name = $name:ident; $(;)*
1321 $( config = $config:expr; $(;)* )?
1322 $( compare_by_id = $compare:literal ; $(;)* )?
1323 $( setup = $setup:expr; $(;)* )?
1324 $( teardown = $teardown:expr; $(;)* )?
1325 benchmarks = |$group:ident| $body:expr
1326 ) => {
1327 binary_benchmark_group!(
1328 name = $name;
1329 $( config = $config; )?
1330 $( compare_by_id = $compare; )?
1331 $( setup = $setup; )?
1332 $( teardown = $teardown; )?
1333 benchmarks = |$group: &mut BinaryBenchmarkGroup| $body
1334 );
1335 };
1336}
1337
1338/// Macro used to define a group of library benchmarks
1339///
1340/// A small introductory example which shows the basic setup. This macro only accepts benchmarks
1341/// annotated with `#[library_benchmark]` ([`crate::library_benchmark`]).
1342///
1343/// ```rust
1344/// use iai_callgrind::{library_benchmark_group, library_benchmark};
1345///
1346/// #[library_benchmark]
1347/// fn bench_something() -> u64 {
1348/// 42
1349/// }
1350///
1351/// library_benchmark_group!(
1352/// name = my_group;
1353/// benchmarks = bench_something
1354/// );
1355///
1356/// # fn main() {
1357/// iai_callgrind::main!(library_benchmark_groups = my_group);
1358/// # }
1359/// ```
1360///
1361/// To be benchmarked a `library_benchmark_group` has to be added to the `main!` macro by adding its
1362/// name to the `library_benchmark_groups` argument of the `main!` macro. See there for further
1363/// details about the [`crate::main`] macro.
1364///
1365/// The following top-level arguments are accepted in this order:
1366///
1367/// ```rust
1368/// # use iai_callgrind::{library_benchmark, library_benchmark_group, LibraryBenchmarkConfig};
1369/// # #[library_benchmark]
1370/// # fn some_func() {}
1371/// fn group_setup() {}
1372/// fn group_teardown() {}
1373/// library_benchmark_group!(
1374/// name = my_group;
1375/// config = LibraryBenchmarkConfig::default();
1376/// compare_by_id = false;
1377/// setup = group_setup();
1378/// teardown = group_teardown();
1379/// benchmarks = some_func
1380/// );
1381/// # fn main() {
1382/// # }
1383/// ```
1384///
1385/// * __`name`__ (mandatory): A unique name used to identify the group for the `main!` macro
1386/// * __`config`__ (optional): A [`crate::LibraryBenchmarkConfig`] which is applied to all
1387/// benchmarks within the same group.
1388/// * __`compare_by_id`__ (optional): The default is false. If true, all benches in the benchmark
1389/// functions specified with the `benchmarks` argument, across any benchmark groups, are compared
1390/// with each other as long as the ids (the part after the `::` in `#[bench::id(...)]`) match.
1391/// * __`setup`__ (optional): A setup function or any valid expression which is run before all
1392/// benchmarks of this group
1393/// * __`teardown`__ (optional): A teardown function or any valid expression which is run after all
1394/// benchmarks of this group
1395/// * __`benchmarks`__ (mandatory): A list of comma separated benchmark functions which must be
1396/// annotated with `#[library_benchmark]`
1397#[macro_export]
1398macro_rules! library_benchmark_group {
1399 (
1400 $( config = $config:expr ; $(;)* )?
1401 $( compare_by_id = $compare:literal ; $(;)* )?
1402 $( setup = $setup:expr ; $(;)* )?
1403 $( teardown = $teardown:expr ; $(;)* )?
1404 benchmarks = $( $function:ident ),+
1405 ) => {
1406 compile_error!("A library_benchmark_group! needs a name\n\nlibrary_benchmark_group!(name = some_ident; benchmarks = ...);");
1407 };
1408 (
1409 name = $name:ident;
1410 $( config = $config:expr ; $(;)* )?
1411 $( compare_by_id = $compare:literal ; $(;)* )?
1412 $( setup = $setup:expr ; $(;)* )?
1413 $( teardown = $teardown:expr ; $(;)* )?
1414 benchmarks =
1415 ) => {
1416 compile_error!(
1417 "A library_benchmark_group! needs at least 1 benchmark function \
1418 annotated with #[library_benchmark]\n\n\
1419 library_benchmark_group!(name = some_ident; benchmarks = some_library_benchmark);");
1420 };
1421 (
1422 name = $name:ident; $(;)*
1423 $( config = $config:expr ; $(;)* )?
1424 $( compare_by_id = $compare:literal ; $(;)* )?
1425 $( setup = $setup:expr ; $(;)* )?
1426 $( teardown = $teardown:expr ; $(;)* )?
1427 benchmarks = $( $function:ident ),+ $(,)*
1428 ) => {
1429 pub mod $name {
1430 use super::*;
1431
1432 pub const __BENCHES: &[&(
1433 &'static str,
1434 fn() -> Option<$crate::internal::InternalLibraryBenchmarkConfig>,
1435 &[$crate::internal::InternalMacroLibBench]
1436 )]= &[
1437 $(
1438 &(
1439 stringify!($function),
1440 super::$function::__get_config,
1441 super::$function::__BENCHES
1442 )
1443 ),+
1444 ];
1445
1446 #[inline(never)]
1447 pub fn __get_config() -> Option<$crate::internal::InternalLibraryBenchmarkConfig> {
1448 let mut config: Option<$crate::internal::InternalLibraryBenchmarkConfig> = None;
1449 $(
1450 config = Some($config.into());
1451 )?
1452 config
1453 }
1454
1455 #[inline(never)]
1456 pub fn __compare_by_id() -> Option<bool> {
1457 let mut comp = None;
1458 $(
1459 comp = Some($compare);
1460 )?
1461 comp
1462 }
1463
1464 #[inline(never)]
1465 pub fn __run_setup(__run: bool) -> bool {
1466 let mut __has_setup = false;
1467 $(
1468 __has_setup = true;
1469 if __run {
1470 $setup;
1471 }
1472 )?
1473 __has_setup
1474 }
1475
1476 #[inline(never)]
1477 pub fn __run_teardown(__run: bool) -> bool {
1478 let mut __has_teardown = false;
1479 $(
1480 __has_teardown = true;
1481 if __run {
1482 $teardown;
1483 }
1484 )?
1485 __has_teardown
1486 }
1487
1488 #[inline(never)]
1489 pub fn __run(group_index: usize, bench_index: usize) {
1490 (__BENCHES[group_index].2[bench_index].func)();
1491 }
1492 }
1493 };
1494}