1 | //! [bench_attr]: macro@bench |
2 | //! [bench_attr_examples]: macro@bench#examples |
3 | //! [bench_attr_threads]: macro@bench#threads |
4 | #![doc = include_str!("../README.md" )] |
5 | #![warn (missing_docs)] |
6 | #![allow ( |
7 | unknown_lints, |
8 | unused_unsafe, |
9 | clippy::needless_doctest_main, |
10 | clippy::needless_lifetimes, |
11 | clippy::new_without_default, |
12 | clippy::type_complexity, |
13 | clippy::missing_transmute_annotations |
14 | )] |
15 | |
16 | // Used by generated code. Not public API and thus not subject to SemVer. |
17 | #[doc (hidden)] |
18 | #[path = "private.rs" ] |
19 | pub mod __private; |
20 | |
21 | #[macro_use ] |
22 | mod util; |
23 | |
24 | mod alloc; |
25 | mod benchmark; // NOTE: "bench" would be imported into the prelude. |
26 | mod cli; |
27 | mod compile_fail; |
28 | mod config; |
29 | mod divan; |
30 | mod entry; |
31 | mod stats; |
32 | mod thread_pool; |
33 | mod time; |
34 | mod tree_painter; |
35 | |
36 | pub mod counter; |
37 | |
38 | /// `use divan::prelude::*;` to import common items. |
39 | pub mod prelude { |
40 | #[doc (no_inline)] |
41 | pub use crate::{bench , bench_group , black_box, black_box_drop, AllocProfiler, Bencher, Divan}; |
42 | } |
43 | |
44 | /// Prevents compiler optimizations on a value. |
45 | /// |
46 | /// `black_box` should only be used on [inputs](#benchmark-inputs) and |
47 | /// [outputs](#benchmark-outputs) of benchmarks. Newcomers to benchmarking may |
48 | /// be tempted to also use `black_box` within the implementation, but doing so |
49 | /// will overly pessimize the measured code without any benefit. |
50 | /// |
51 | /// ## Benchmark Inputs |
52 | /// |
53 | /// When benchmarking, it's good practice to ensure measurements are accurate by |
54 | /// preventing the compiler from optimizing based on assumptions about benchmark |
55 | /// inputs. |
56 | /// |
57 | /// The compiler can optimize code for indices it knows about, such as by |
58 | /// removing bounds checks or unrolling loops. If real-world use of your code |
59 | /// would not know indices up front, consider preventing optimizations on them |
60 | /// in benchmarks: |
61 | /// |
62 | /// ``` |
63 | /// use divan::black_box; |
64 | /// |
65 | /// const INDEX: usize = // ... |
66 | /// # 0; |
67 | /// const SLICE: &[u8] = // ... |
68 | /// # &[]; |
69 | /// |
70 | /// #[divan::bench] |
71 | /// fn bench() { |
72 | /// # fn work<T>(_: T) {} |
73 | /// work(&SLICE[black_box(INDEX)..]); |
74 | /// } |
75 | /// ``` |
76 | /// |
77 | /// The compiler may also optimize for the data itself, which can also be |
78 | /// avoided with `black_box`: |
79 | /// |
80 | /// ``` |
81 | /// # use divan::black_box; |
82 | /// # const INDEX: usize = 0; |
83 | /// # const SLICE: &[u8] = &[]; |
84 | /// #[divan::bench] |
85 | /// fn bench() { |
86 | /// # fn work<T>(_: T) {} |
87 | /// work(black_box(&SLICE[black_box(INDEX)..])); |
88 | /// } |
89 | /// ``` |
90 | /// |
91 | /// ## Benchmark Outputs |
92 | /// |
93 | /// When benchmarking, it's best to ensure that all of the code is actually |
94 | /// being run. If the compiler knows an output is unused, it may remove the code |
95 | /// that generated the output. This optimization can make benchmarks appear much |
96 | /// faster than they really are. |
97 | /// |
98 | /// At the end of a benchmark, we can force the compiler to treat outputs as if |
99 | /// they were actually used: |
100 | /// |
101 | /// ``` |
102 | /// # use divan::black_box; |
103 | /// #[divan::bench] |
104 | /// fn bench() { |
105 | /// # let value = 1; |
106 | /// black_box(value.to_string()); |
107 | /// } |
108 | /// ``` |
109 | /// |
110 | /// To make the code clearer to readers that the output is discarded, this code |
111 | /// could instead call [`black_box_drop`]. |
112 | /// |
113 | /// Alternatively, the output can be returned from the benchmark: |
114 | /// |
115 | /// ``` |
116 | /// #[divan::bench] |
117 | /// fn bench() -> String { |
118 | /// # let value = 1; |
119 | /// value.to_string() |
120 | /// } |
121 | /// ``` |
122 | /// |
123 | /// Returning the output will `black_box` it and also avoid measuring the time |
124 | /// to [drop](Drop) the output, which in this case is the time to deallocate a |
125 | /// [`String`]. Read more about this in the [`#[divan::bench]` |
126 | /// docs](macro@bench#drop). |
127 | /// |
128 | /// --- |
129 | /// |
130 | /// <h1>Standard Library Documentation</h1> |
131 | /// |
132 | #[doc (inline)] |
133 | pub use std::hint::black_box; |
134 | |
135 | #[doc (inline)] |
136 | pub use crate::{alloc::AllocProfiler, benchmark::Bencher, divan::Divan}; |
137 | |
138 | /// Runs all registered benchmarks. |
139 | /// |
140 | /// # Examples |
141 | /// |
142 | /// ``` |
143 | /// #[divan::bench] |
144 | /// fn add() -> i32 { |
145 | /// // ... |
146 | /// # 0 |
147 | /// } |
148 | /// |
149 | /// fn main() { |
150 | /// // Run `add` benchmark: |
151 | /// divan::main(); |
152 | /// } |
153 | /// ``` |
154 | /// |
155 | /// See [`#[divan::bench]`](macro@bench) for more examples. |
156 | pub fn main() { |
157 | Divan::from_args().main(); |
158 | } |
159 | |
160 | /// [`black_box`] + [`drop`] convenience function. |
161 | /// |
162 | /// # Examples |
163 | /// |
164 | /// This is useful when benchmarking a lazy [`Iterator`] to completion with |
165 | /// [`for_each`](Iterator::for_each): |
166 | /// |
167 | /// ``` |
168 | /// #[divan::bench] |
169 | /// fn parse_iter() { |
170 | /// let input: &str = // ... |
171 | /// # "" ; |
172 | /// |
173 | /// # struct Parser; |
174 | /// # impl Parser { |
175 | /// # fn new(_: &str) -> Parser { Parser } |
176 | /// # fn for_each(self, _: fn(&'static str)) {} |
177 | /// # } |
178 | /// Parser::new(input) |
179 | /// .for_each(divan::black_box_drop); |
180 | /// } |
181 | /// ``` |
182 | #[inline ] |
183 | pub fn black_box_drop<T>(dummy: T) { |
184 | _ = black_box(dummy); |
185 | } |
186 | |
187 | /// Registers a benchmarking function. |
188 | /// |
189 | /// # Examples |
190 | /// |
191 | /// The quickest way to get started is to benchmark the function as-is: |
192 | /// |
193 | /// ``` |
194 | /// use divan::black_box; |
195 | /// |
196 | /// #[divan::bench] |
197 | /// fn add() -> i32 { |
198 | /// black_box(1) + black_box(42) |
199 | /// } |
200 | /// |
201 | /// fn main() { |
202 | /// // Run `add` benchmark: |
203 | /// divan::main(); |
204 | /// } |
205 | /// ``` |
206 | /// |
207 | /// If benchmarks need to setup context before running, they can take a |
208 | /// [`Bencher`] and use [`Bencher::bench`]: |
209 | /// |
210 | /// ``` |
211 | /// use divan::{Bencher, black_box}; |
212 | /// |
213 | /// #[divan::bench] |
214 | /// fn copy_from_slice(bencher: Bencher) { |
215 | /// let src = (0..100).collect::<Vec<i32>>(); |
216 | /// let mut dst = vec![0; src.len()]; |
217 | /// |
218 | /// bencher.bench_local(move || { |
219 | /// black_box(&mut dst).copy_from_slice(black_box(&src)); |
220 | /// }); |
221 | /// } |
222 | /// ``` |
223 | /// |
224 | /// Applying this attribute multiple times to the same item will cause a compile |
225 | /// error: |
226 | /// |
227 | /// ```compile_fail |
228 | /// #[divan::bench] |
229 | /// #[divan::bench] |
230 | /// fn bench() { |
231 | /// // ... |
232 | /// } |
233 | /// ``` |
234 | /// |
235 | /// # Drop |
236 | /// |
237 | /// When a benchmarked function returns a value, it will not be [dropped][Drop] |
238 | /// until after the current sample loop is finished. This allows for more |
239 | /// precise timing measurements. |
240 | /// |
241 | /// Note that there is an inherent memory cost to defer drop, including |
242 | /// allocations inside not-yet-dropped values. Also, if the benchmark |
243 | /// [panics](macro@std::panic), the values will never be dropped. |
244 | /// |
245 | /// The following example benchmarks will only measure [`String`] construction |
246 | /// time, but not deallocation time: |
247 | /// |
248 | /// ``` |
249 | /// use divan::{Bencher, black_box}; |
250 | /// |
251 | /// #[divan::bench] |
252 | /// fn freestanding() -> String { |
253 | /// black_box("hello").to_uppercase() |
254 | /// } |
255 | /// |
256 | /// #[divan::bench] |
257 | /// fn contextual(bencher: Bencher) { |
258 | /// // Setup: |
259 | /// let s: String = // ... |
260 | /// # String::new(); |
261 | /// |
262 | /// bencher.bench(|| -> String { |
263 | /// black_box(&s).to_lowercase() |
264 | /// }); |
265 | /// } |
266 | /// ``` |
267 | /// |
268 | /// If the returned value *does not* need to be dropped, there is no memory |
269 | /// cost. Because of this, the following example benchmarks are equivalent: |
270 | /// |
271 | /// ``` |
272 | /// #[divan::bench] |
273 | /// fn with_return() -> i32 { |
274 | /// let n: i32 = // ... |
275 | /// # 0; |
276 | /// n |
277 | /// } |
278 | /// |
279 | /// #[divan::bench] |
280 | /// fn without_return() { |
281 | /// let n: i32 = // ... |
282 | /// # 0; |
283 | /// divan::black_box(n); |
284 | /// } |
285 | /// ``` |
286 | /// |
287 | /// # Options |
288 | /// |
289 | /// - [`name`] |
290 | /// - [`crate`] |
291 | /// - [`args`] |
292 | /// - [`consts`] |
293 | /// - [`types`] |
294 | /// - [`sample_count`] |
295 | /// - [`sample_size`] |
296 | /// - [`threads`] |
297 | /// - [`counters`] |
298 | /// - [`bytes_count`] |
299 | /// - [`chars_count`] |
300 | /// - [`items_count`] |
301 | /// - [`min_time`] |
302 | /// - [`max_time`] |
303 | /// - [`skip_ext_time`] |
304 | /// - [`ignore`] |
305 | /// |
306 | /// ## `name` |
307 | /// [`name`]: #name |
308 | /// |
309 | /// By default, the benchmark uses the function's name. It can be overridden via |
310 | /// the [`name`] option: |
311 | /// |
312 | /// ``` |
313 | /// #[divan::bench(name = "my_add")] |
314 | /// fn add() -> i32 { |
315 | /// // Will appear as "crate_name::my_add". |
316 | /// # 0 |
317 | /// } |
318 | /// ``` |
319 | /// |
320 | /// ## `crate` |
321 | /// [`crate`]: #crate |
322 | /// |
323 | /// The path to the specific `divan` crate instance used by this macro's |
324 | /// generated code can be specified via the [`crate`] option. This is applicable |
325 | /// when using `divan` via a macro from your own crate. |
326 | /// |
327 | /// ``` |
328 | /// extern crate divan as sofa; |
329 | /// |
330 | /// #[::sofa::bench(crate = ::sofa)] |
331 | /// fn add() -> i32 { |
332 | /// // ... |
333 | /// # 0 |
334 | /// } |
335 | /// ``` |
336 | /// |
337 | /// ## `args` |
338 | /// [`args`]: #args |
339 | /// |
340 | /// Function arguments can be provided to benchmark the function over multiple |
341 | /// cases. This is used for comparing across parameters like collection lengths |
342 | /// and [`enum`](https://doc.rust-lang.org/std/keyword.enum.html) variants. If |
343 | /// you are not comparing cases and just need to pass a value into the |
344 | /// benchmark, instead consider passing local values into the [`Bencher::bench`] |
345 | /// closure or use [`Bencher::with_inputs`] for many distinct values. |
346 | /// |
347 | /// The following example benchmarks converting a [`Range`](std::ops::Range) to |
348 | /// [`Vec`] over different lengths: |
349 | /// |
350 | /// ``` |
351 | /// #[divan::bench(args = [1000, LEN, len()])] |
352 | /// fn init_vec(len: usize) -> Vec<usize> { |
353 | /// (0..len).collect() |
354 | /// } |
355 | /// |
356 | /// const LEN: usize = // ... |
357 | /// # 0; |
358 | /// |
359 | /// fn len() -> usize { |
360 | /// // ... |
361 | /// # 0 |
362 | /// } |
363 | /// ``` |
364 | /// |
365 | /// The list of arguments can be shared across multiple benchmarks through an |
366 | /// external [`Iterator`]: |
367 | /// |
368 | /// ``` |
369 | /// const LENS: &[usize] = // ... |
370 | /// # &[]; |
371 | /// |
372 | /// #[divan::bench(args = LENS)] |
373 | /// fn bench_vec1(len: usize) -> Vec<usize> { |
374 | /// // ... |
375 | /// # vec![] |
376 | /// } |
377 | /// |
378 | /// #[divan::bench(args = LENS)] |
379 | /// fn bench_vec2(len: usize) -> Vec<usize> { |
380 | /// // ... |
381 | /// # vec![] |
382 | /// } |
383 | /// ``` |
384 | /// |
385 | /// Unlike the [`consts`] option, any argument type is supported if it |
386 | /// implements [`Any`], [`Copy`], [`Send`], [`Sync`], and [`ToString`] (or |
387 | /// [`Debug`](std::fmt::Debug)): |
388 | /// |
389 | /// ``` |
390 | /// #[derive(Clone, Copy, Debug)] |
391 | /// enum Arg { |
392 | /// A, B |
393 | /// } |
394 | /// |
395 | /// #[divan::bench(args = [Arg::A, Arg::B])] |
396 | /// fn bench_args(arg: Arg) { |
397 | /// // ... |
398 | /// } |
399 | /// ``` |
400 | /// |
401 | /// The argument type does not need to implement [`Copy`] if it is used through |
402 | /// a reference: |
403 | /// |
404 | /// ``` |
405 | /// #[derive(Debug)] |
406 | /// enum Arg { |
407 | /// A, B |
408 | /// } |
409 | /// |
410 | /// #[divan::bench(args = [Arg::A, Arg::B])] |
411 | /// fn bench_args(arg: &Arg) { |
412 | /// // ... |
413 | /// } |
414 | /// ``` |
415 | /// |
416 | /// For convenience, common string types are coerced to [`&str`](primitive@str): |
417 | /// |
418 | /// ``` |
419 | /// fn strings() -> impl Iterator<Item = String> { |
420 | /// // ... |
421 | /// # [].into_iter() |
422 | /// } |
423 | /// |
424 | /// #[divan::bench(args = strings())] |
425 | /// fn bench_strings(s: &str) { |
426 | /// // ... |
427 | /// } |
428 | /// ``` |
429 | /// |
430 | /// Arguments can also be used with [`Bencher`]. This allows for generating |
431 | /// inputs based on [`args`] values or providing throughput information via |
432 | /// [`Counter`s](crate::counter::Counter): |
433 | /// |
434 | /// ``` |
435 | /// # fn new_value<T>(v: T) -> T { v } |
436 | /// # fn do_work<T>(_: T) {} |
437 | /// use divan::Bencher; |
438 | /// |
439 | /// #[divan::bench(args = [1, 2, 3])] |
440 | /// fn bench(bencher: Bencher, len: usize) { |
441 | /// let value = new_value(len); |
442 | /// |
443 | /// bencher |
444 | /// .counter(len) |
445 | /// .bench(|| { |
446 | /// do_work(value); |
447 | /// }); |
448 | /// } |
449 | /// ``` |
450 | /// |
451 | /// ## `consts` |
452 | /// [`consts`]: #consts |
453 | /// |
454 | /// Divan supports benchmarking functions with [`const` |
455 | /// generics](https://doc.rust-lang.org/reference/items/generics.html#const-generics) |
456 | /// via the [`consts`] option. |
457 | /// |
458 | /// The following example benchmarks initialization of [`[i32; N]`](prim@array) |
459 | /// for values of `N` provided by a [literal](https://doc.rust-lang.org/reference/expressions/literal-expr.html), |
460 | /// [`const` item](https://doc.rust-lang.org/reference/items/constant-items.html), |
461 | /// and [`const fn`](https://doc.rust-lang.org/reference/const_eval.html#const-functions): |
462 | /// |
463 | /// ``` |
464 | /// #[divan::bench(consts = [1000, LEN, len()])] |
465 | /// fn init_array<const N: usize>() -> [i32; N] { |
466 | /// let mut result = [0; N]; |
467 | /// |
468 | /// for i in 0..N { |
469 | /// result[i] = divan::black_box(i as i32); |
470 | /// } |
471 | /// |
472 | /// result |
473 | /// } |
474 | /// |
475 | /// const LEN: usize = // ... |
476 | /// # 0; |
477 | /// |
478 | /// const fn len() -> usize { |
479 | /// // ... |
480 | /// # 0 |
481 | /// } |
482 | /// ``` |
483 | /// |
484 | /// The list of constants can be shared across multiple benchmarks through an |
485 | /// external [array](prim@array) or [slice](prim@slice): |
486 | /// |
487 | /// ``` |
488 | /// const SIZES: &[usize] = &[1, 2, 5, 10]; |
489 | /// |
490 | /// #[divan::bench(consts = SIZES)] |
491 | /// fn bench_array1<const N: usize>() -> [i32; N] { |
492 | /// // ... |
493 | /// # [0; N] |
494 | /// } |
495 | /// |
496 | /// #[divan::bench(consts = SIZES)] |
497 | /// fn bench_array2<const N: usize>() -> [i32; N] { |
498 | /// // ... |
499 | /// # [0; N] |
500 | /// } |
501 | /// ``` |
502 | /// |
503 | /// External constants are limited to lengths 1 through 20, because of |
504 | /// implementation details. This limit does not apply if the list is provided |
505 | /// directly like in the first example. |
506 | /// |
507 | /// ```compile_fail |
508 | /// const SIZES: [usize; 21] = [ |
509 | /// // ... |
510 | /// # 0; 21 |
511 | /// ]; |
512 | /// |
513 | /// #[divan::bench(consts = SIZES)] |
514 | /// fn bench_array<const N: usize>() -> [i32; N] { |
515 | /// // ... |
516 | /// # [0; N] |
517 | /// } |
518 | /// ``` |
519 | /// |
520 | /// ## `types` |
521 | /// [`types`]: #types |
522 | /// |
523 | /// Divan supports benchmarking generic functions over a list of types via the |
524 | /// [`types`] option. |
525 | /// |
526 | /// The following example benchmarks the [`From<&str>`](From) implementations |
527 | /// for [`&str`](prim@str) and [`String`]: |
528 | /// |
529 | /// ``` |
530 | /// #[divan::bench(types = [&str, String])] |
531 | /// fn from_str<'a, T>() -> T |
532 | /// where |
533 | /// T: From<&'a str>, |
534 | /// { |
535 | /// divan::black_box("hello world").into() |
536 | /// } |
537 | /// ``` |
538 | /// |
539 | /// The [`types`] and [`args`] options can be combined to benchmark _T_ × _A_ |
540 | /// scenarios. The following example benchmarks the [`FromIterator`] |
541 | /// implementations for [`Vec`], [`BTreeSet`], and [`HashSet`]: |
542 | /// |
543 | /// ``` |
544 | /// use std::collections::{BTreeSet, HashSet}; |
545 | /// |
546 | /// #[divan::bench( |
547 | /// types = [Vec<i32>, BTreeSet<i32>, HashSet<i32>], |
548 | /// args = [0, 2, 4, 16, 256, 4096], |
549 | /// )] |
550 | /// fn from_range<T>(n: i32) -> T |
551 | /// where |
552 | /// T: FromIterator<i32>, |
553 | /// { |
554 | /// (0..n).collect() |
555 | /// } |
556 | /// ``` |
557 | /// |
558 | /// [`BTreeSet`]: std::collections::BTreeSet |
559 | /// [`HashSet`]: std::collections::HashSet |
560 | /// |
561 | /// ## `sample_count` |
562 | /// [`sample_count`]: #sample_count |
563 | /// |
564 | /// The number of statistical sample recordings can be set to a predetermined |
565 | /// [`u32`] value via the [`sample_count`] option. This may be overridden at |
566 | /// runtime using either the `DIVAN_SAMPLE_COUNT` environment variable or |
567 | /// `--sample-count` CLI argument. |
568 | /// |
569 | /// ``` |
570 | /// #[divan::bench(sample_count = 1000)] |
571 | /// fn add() -> i32 { |
572 | /// // ... |
573 | /// # 0 |
574 | /// } |
575 | /// ``` |
576 | /// |
577 | /// If the [`threads`] option is enabled, sample count becomes a multiple of the |
578 | /// number of threads. This is because each thread operates over the same sample |
579 | /// size to ensure there are always N competing threads doing the same amount of |
580 | /// work. |
581 | /// |
582 | /// ## `sample_size` |
583 | /// [`sample_size`]: #sample_size |
584 | /// |
585 | /// The number iterations within each statistics sample can be set to a |
586 | /// predetermined [`u32`] value via the [`sample_size`] option. This may be |
587 | /// overridden at runtime using either the `DIVAN_SAMPLE_SIZE` environment |
588 | /// variable or `--sample-size` CLI argument. |
589 | /// |
590 | /// ``` |
591 | /// #[divan::bench(sample_size = 1000)] |
592 | /// fn add() -> i32 { |
593 | /// // ... |
594 | /// # 0 |
595 | /// } |
596 | /// ``` |
597 | /// |
598 | /// ## `threads` |
599 | /// [`threads`]: #threads |
600 | /// |
601 | /// Benchmarked functions can be run across multiple threads via the [`threads`] |
602 | /// option. This enables you to measure contention on [atomics and |
603 | /// locks][std::sync]. The default thread count is the [available parallelism]. |
604 | /// |
605 | /// ``` |
606 | /// use std::sync::Arc; |
607 | /// |
608 | /// #[divan::bench(threads)] |
609 | /// fn arc_clone(bencher: divan::Bencher) { |
610 | /// let arc = Arc::new(42); |
611 | /// |
612 | /// bencher.bench(|| arc.clone()); |
613 | /// } |
614 | /// ``` |
615 | /// |
616 | /// The [`threads`] option can be set to any of: |
617 | /// - [`bool`] for [available parallelism] (true) or no parallelism. |
618 | /// - [`usize`] for a specific number of threads. 0 means use [available |
619 | /// parallelism] and 1 means no parallelism. |
620 | /// - [`IntoIterator`] over [`usize`] for multiple thread counts, such as: |
621 | /// - [`Range<usize>`](std::ops::Range) |
622 | /// - [`[usize; N]`](prim@array) |
623 | /// - [`&[usize]`](prim@slice) |
624 | /// |
625 | /// ``` |
626 | /// #[divan::bench(threads = false)] |
627 | /// fn single() { |
628 | /// // ... |
629 | /// } |
630 | /// |
631 | /// #[divan::bench(threads = 10)] |
632 | /// fn specific() { |
633 | /// // ... |
634 | /// } |
635 | /// |
636 | /// #[divan::bench(threads = 0..=8)] |
637 | /// fn range() { |
638 | /// // Note: Includes 0 for available parallelism. |
639 | /// } |
640 | /// |
641 | /// #[divan::bench(threads = [0, 1, 4, 8, 16])] |
642 | /// fn selection() { |
643 | /// // ... |
644 | /// } |
645 | /// ``` |
646 | /// |
647 | /// ## `counters` |
648 | /// [`counters`]: #counters |
649 | /// |
650 | /// The [`Counter`s](crate::counter::Counter) of each iteration can be set via |
651 | /// the [`counters`] option. The following example emits info for the number of |
652 | /// bytes and number of ints processed when benchmarking [slice sorting](slice::sort): |
653 | /// |
654 | /// ``` |
655 | /// use divan::{Bencher, counter::{BytesCount, ItemsCount}}; |
656 | /// |
657 | /// const INTS: &[i32] = &[ |
658 | /// // ... |
659 | /// ]; |
660 | /// |
661 | /// #[divan::bench(counters = [ |
662 | /// BytesCount::of_slice(INTS), |
663 | /// ItemsCount::new(INTS.len()), |
664 | /// ])] |
665 | /// fn sort(bencher: Bencher) { |
666 | /// bencher |
667 | /// .with_inputs(|| INTS.to_vec()) |
668 | /// .bench_refs(|ints| ints.sort()); |
669 | /// } |
670 | /// ``` |
671 | /// |
672 | /// For convenience, singular `counter` allows a single |
673 | /// [`Counter`](crate::counter::Counter) to be set. The following example emits |
674 | /// info for the number of bytes processed when benchmarking |
675 | /// [`char`-counting](std::str::Chars::count): |
676 | /// |
677 | /// ``` |
678 | /// use divan::counter::BytesCount; |
679 | /// |
680 | /// const STR: &str = "..."; |
681 | /// |
682 | /// #[divan::bench(counter = BytesCount::of_str(STR))] |
683 | /// fn char_count() -> usize { |
684 | /// divan::black_box(STR).chars().count() |
685 | /// } |
686 | /// ``` |
687 | /// |
688 | /// See: |
689 | /// - [`#[divan::bench_group(counters = ...)]`](macro@bench_group#counters) |
690 | /// - [`Bencher::counter`] |
691 | /// - [`Bencher::input_counter`] |
692 | /// |
693 | /// ### `bytes_count` |
694 | /// [`bytes_count`]: #bytes_count |
695 | /// |
696 | /// Convenience shorthand for |
697 | /// <code>[counter](#counters) = [BytesCount](counter::BytesCount)::from(n)</code>. |
698 | /// |
699 | /// ### `chars_count` |
700 | /// [`chars_count`]: #chars_count |
701 | /// |
702 | /// Convenience shorthand for |
703 | /// <code>[counter](#counters) = [CharsCount](counter::CharsCount)::from(n)</code>. |
704 | /// |
705 | /// ### `items_count` |
706 | /// [`items_count`]: #items_count |
707 | /// |
708 | /// Convenience shorthand for |
709 | /// <code>[counter](#counters) = [ItemsCount](counter::ItemsCount)::from(n)</code>. |
710 | /// |
711 | /// ## `min_time` |
712 | /// [`min_time`]: #min_time |
713 | /// |
714 | /// The minimum time spent benchmarking each function can be set to a |
715 | /// predetermined [`Duration`] via the [`min_time`] option. This may be |
716 | /// overridden at runtime using either the `DIVAN_MIN_TIME` environment variable |
717 | /// or `--min-time` CLI argument. |
718 | /// |
719 | /// Unless [`skip_ext_time`] is set, this includes time external to the |
720 | /// benchmarked function, such as time spent generating inputs and running |
721 | /// [`Drop`]. |
722 | /// |
723 | /// ``` |
724 | /// use std::time::Duration; |
725 | /// |
726 | /// #[divan::bench(min_time = Duration::from_secs(3))] |
727 | /// fn add() -> i32 { |
728 | /// // ... |
729 | /// # 0 |
730 | /// } |
731 | /// ``` |
732 | /// |
733 | /// For convenience, [`min_time`] can also be set with seconds as [`u64`] or |
734 | /// [`f64`]. Invalid values will cause a panic at runtime. |
735 | /// |
736 | /// ``` |
737 | /// #[divan::bench(min_time = 2)] |
738 | /// fn int_secs() -> i32 { |
739 | /// // ... |
740 | /// # 0 |
741 | /// } |
742 | /// |
743 | /// #[divan::bench(min_time = 1.5)] |
744 | /// fn float_secs() -> i32 { |
745 | /// // ... |
746 | /// # 0 |
747 | /// } |
748 | /// ``` |
749 | /// |
750 | /// ## `max_time` |
751 | /// [`max_time`]: #max_time |
752 | /// |
753 | /// The maximum time spent benchmarking each function can be set to a |
754 | /// predetermined [`Duration`] via the [`max_time`] option. This may be |
755 | /// overridden at runtime using either the `DIVAN_MAX_TIME` environment variable |
756 | /// or `--max-time` CLI argument. |
757 | /// |
758 | /// Unless [`skip_ext_time`] is set, this includes time external to the |
759 | /// benchmarked function, such as time spent generating inputs and running |
760 | /// [`Drop`]. |
761 | /// |
762 | /// If `min_time > max_time`, then [`max_time`] has priority and [`min_time`] |
763 | /// will not be reached. |
764 | /// |
765 | /// ``` |
766 | /// use std::time::Duration; |
767 | /// |
768 | /// #[divan::bench(max_time = Duration::from_secs(5))] |
769 | /// fn add() -> i32 { |
770 | /// // ... |
771 | /// # 0 |
772 | /// } |
773 | /// ``` |
774 | /// |
775 | /// For convenience, like [`min_time`], [`max_time`] can also be set with |
776 | /// seconds as [`u64`] or [`f64`]. Invalid values will cause a panic at runtime. |
777 | /// |
778 | /// ``` |
779 | /// #[divan::bench(max_time = 8)] |
780 | /// fn int_secs() -> i32 { |
781 | /// // ... |
782 | /// # 0 |
783 | /// } |
784 | /// |
785 | /// #[divan::bench(max_time = 9.5)] |
786 | /// fn float_secs() -> i32 { |
787 | /// // ... |
788 | /// # 0 |
789 | /// } |
790 | /// ``` |
791 | /// |
792 | /// ## `skip_ext_time` |
793 | /// [`skip_ext_time`]: #skip_ext_time |
794 | /// |
795 | /// By default, [`min_time`] and [`max_time`] include time external to the |
796 | /// benchmarked function, such as time spent generating inputs and running |
797 | /// [`Drop`]. Enabling the [`skip_ext_time`] option will instead make those |
798 | /// options only consider time spent within the benchmarked function. This may |
799 | /// be overridden at runtime using either the `DIVAN_SKIP_EXT_TIME` environment |
800 | /// variable or `--skip-ext-time` CLI argument. |
801 | /// |
802 | /// In the following example, [`max_time`] only considers time spent running |
803 | /// `measured_function`: |
804 | /// |
805 | /// ``` |
806 | /// # fn generate_input() {} |
807 | /// # fn measured_function(_: ()) {} |
808 | /// #[divan::bench(max_time = 5, skip_ext_time)] |
809 | /// fn bench(bencher: divan::Bencher) { |
810 | /// bencher |
811 | /// .with_inputs(|| generate_input()) |
812 | /// .bench_values(|input| measured_function(input)); |
813 | /// } |
814 | /// ``` |
815 | /// |
816 | /// This option can be set to an explicit [`bool`] value to override parent |
817 | /// values: |
818 | /// |
819 | /// ``` |
820 | /// #[divan::bench(max_time = 5, skip_ext_time = false)] |
821 | /// fn bench(bencher: divan::Bencher) { |
822 | /// // ... |
823 | /// } |
824 | /// ``` |
825 | /// |
826 | /// ## `ignore` |
827 | /// [`ignore`]: #ignore |
828 | /// |
829 | /// Like [`#[test]`](https://doc.rust-lang.org/reference/attributes/testing.html#the-test-attribute), |
830 | /// `#[divan::bench]` functions can use [`#[ignore]`](https://doc.rust-lang.org/reference/attributes/testing.html#the-ignore-attribute): |
831 | /// |
832 | /// ``` |
833 | /// #[divan::bench] |
834 | /// #[ignore] |
835 | /// fn todo() { |
836 | /// unimplemented!(); |
837 | /// } |
838 | /// # divan::main(); |
839 | /// ``` |
840 | /// |
841 | /// This option can also instead be set within the `#[divan::bench]` attribute: |
842 | /// |
843 | /// ``` |
844 | /// #[divan::bench(ignore)] |
845 | /// fn todo() { |
846 | /// unimplemented!(); |
847 | /// } |
848 | /// # divan::main(); |
849 | /// ``` |
850 | /// |
851 | /// Like [`skip_ext_time`], this option can be set to an explicit [`bool`] value |
852 | /// to override parent values: |
853 | /// |
854 | /// ``` |
855 | /// #[divan::bench(ignore = false)] |
856 | /// fn bench() { |
857 | /// // ... |
858 | /// } |
859 | /// ``` |
860 | /// |
861 | /// This can be used to ignore benchmarks based on a runtime condition. The |
862 | /// following example benchmark will be ignored if an [environment |
863 | /// variable](std::env::var) is not set to "true": |
864 | /// |
865 | /// ``` |
866 | /// #[divan::bench( |
867 | /// ignore = std::env::var("BENCH_EXPENSIVE").as_deref() != Ok("true") |
868 | /// )] |
869 | /// fn expensive_bench() { |
870 | /// // ... |
871 | /// } |
872 | /// ``` |
873 | /// |
874 | /// [`Any`]: std::any::Any |
875 | /// [`Duration`]: std::time::Duration |
876 | /// [available parallelism]: std::thread::available_parallelism |
877 | pub use divan_macros::bench; |
878 | |
879 | /// Registers a benchmarking group. |
880 | /// |
881 | /// # Examples |
882 | /// |
883 | /// This is used for setting [options] shared across |
884 | /// [`#[divan::bench]`](macro@bench) functions in the same module: |
885 | /// |
886 | /// ``` |
887 | /// #[divan::bench_group( |
888 | /// sample_count = 100, |
889 | /// sample_size = 500, |
890 | /// )] |
891 | /// mod math { |
892 | /// use divan::black_box; |
893 | /// |
894 | /// #[divan::bench] |
895 | /// fn add() -> i32 { |
896 | /// black_box(1) + black_box(42) |
897 | /// } |
898 | /// |
899 | /// #[divan::bench] |
900 | /// fn div() -> i32 { |
901 | /// black_box(1) / black_box(42) |
902 | /// } |
903 | /// } |
904 | /// |
905 | /// fn main() { |
906 | /// // Run `math::add` and `math::div` benchmarks: |
907 | /// divan::main(); |
908 | /// } |
909 | /// ``` |
910 | /// |
911 | /// Benchmarking [options] set on parent groups cascade into child groups and |
912 | /// their benchmarks: |
913 | /// |
914 | /// ``` |
915 | /// #[divan::bench_group( |
916 | /// sample_count = 100, |
917 | /// sample_size = 500, |
918 | /// )] |
919 | /// mod parent { |
920 | /// #[divan::bench_group(sample_size = 1)] |
921 | /// mod child1 { |
922 | /// #[divan::bench] |
923 | /// fn bench() { |
924 | /// // Will be sampled 100 times with 1 iteration per sample. |
925 | /// } |
926 | /// } |
927 | /// |
928 | /// #[divan::bench_group(sample_count = 42)] |
929 | /// mod child2 { |
930 | /// #[divan::bench] |
931 | /// fn bench() { |
932 | /// // Will be sampled 42 times with 500 iterations per sample. |
933 | /// } |
934 | /// } |
935 | /// |
936 | /// mod child3 { |
937 | /// #[divan::bench(sample_count = 1)] |
938 | /// fn bench() { |
939 | /// // Will be sampled 1 time with 500 iterations per sample. |
940 | /// } |
941 | /// } |
942 | /// } |
943 | /// ``` |
944 | /// |
945 | /// Applying this attribute multiple times to the same item will cause a compile |
946 | /// error: |
947 | /// |
948 | /// ```compile_fail |
949 | /// #[divan::bench_group] |
950 | /// #[divan::bench_group] |
951 | /// mod math { |
952 | /// // ... |
953 | /// } |
954 | /// ``` |
955 | /// |
956 | /// # Options |
957 | /// [options]: #options |
958 | /// |
959 | /// - [`name`] |
960 | /// - [`crate`] |
961 | /// - [`sample_count`] |
962 | /// - [`sample_size`] |
963 | /// - [`threads`] |
964 | /// - [`counters`] |
965 | /// - [`bytes_count`] |
966 | /// - [`chars_count`] |
967 | /// - [`items_count`] |
968 | /// - [`min_time`] |
969 | /// - [`max_time`] |
970 | /// - [`skip_ext_time`] |
971 | /// - [`ignore`] |
972 | /// |
973 | /// ## `name` |
974 | /// [`name`]: #name |
975 | /// |
976 | /// By default, the benchmark group uses the module's name. It can be overridden |
977 | /// via the `name` option: |
978 | /// |
979 | /// ``` |
980 | /// #[divan::bench_group(name = "my_math")] |
981 | /// mod math { |
982 | /// #[divan::bench(name = "my_add")] |
983 | /// fn add() -> i32 { |
984 | /// // Will appear as "crate_name::my_math::my_add". |
985 | /// # 0 |
986 | /// } |
987 | /// } |
988 | /// ``` |
989 | /// |
990 | /// ## `crate` |
991 | /// [`crate`]: #crate |
992 | /// |
993 | /// The path to the specific `divan` crate instance used by this macro's |
994 | /// generated code can be specified via the [`crate`] option. This is applicable |
995 | /// when using `divan` via a macro from your own crate. |
996 | /// |
997 | /// ``` |
998 | /// extern crate divan as sofa; |
999 | /// |
1000 | /// #[::sofa::bench_group(crate = ::sofa)] |
1001 | /// mod math { |
1002 | /// #[::sofa::bench(crate = ::sofa)] |
1003 | /// fn add() -> i32 { |
1004 | /// // ... |
1005 | /// # 0 |
1006 | /// } |
1007 | /// } |
1008 | /// ``` |
1009 | /// |
1010 | /// ## `sample_count` |
1011 | /// [`sample_count`]: #sample_count |
1012 | /// |
1013 | /// The number of statistical sample recordings can be set to a predetermined |
1014 | /// [`u32`] value via the [`sample_count`] option. This may be overridden at |
1015 | /// runtime using either the `DIVAN_SAMPLE_COUNT` environment variable or |
1016 | /// `--sample-count` CLI argument. |
1017 | /// |
1018 | /// ``` |
1019 | /// #[divan::bench_group(sample_count = 1000)] |
1020 | /// mod math { |
1021 | /// #[divan::bench] |
1022 | /// fn add() -> i32 { |
1023 | /// // ... |
1024 | /// # 0 |
1025 | /// } |
1026 | /// } |
1027 | /// ``` |
1028 | /// |
1029 | /// If the [`threads`] option is enabled, sample count becomes a multiple of the |
1030 | /// number of threads. This is because each thread operates over the same sample |
1031 | /// size to ensure there are always N competing threads doing the same amount of |
1032 | /// work. |
1033 | /// |
1034 | /// ## `sample_size` |
1035 | /// [`sample_size`]: #sample_size |
1036 | /// |
1037 | /// The number iterations within each statistical sample can be set to a |
1038 | /// predetermined [`u32`] value via the [`sample_size`] option. This may be |
1039 | /// overridden at runtime using either the `DIVAN_SAMPLE_SIZE` environment |
1040 | /// variable or `--sample-size` CLI argument. |
1041 | /// |
1042 | /// ``` |
1043 | /// #[divan::bench_group(sample_size = 1000)] |
1044 | /// mod math { |
1045 | /// #[divan::bench] |
1046 | /// fn add() -> i32 { |
1047 | /// // ... |
1048 | /// # 0 |
1049 | /// } |
1050 | /// } |
1051 | /// ``` |
1052 | /// |
1053 | /// ## `threads` |
1054 | /// [`threads`]: #threads |
1055 | /// |
1056 | /// See [`#[divan::bench(threads = ...)]`](macro@bench#threads). |
1057 | /// |
1058 | /// ## `counters` |
1059 | /// [`counters`]: #counters |
1060 | /// |
1061 | /// The [`Counter`s](crate::counter::Counter) of each iteration of benchmarked |
1062 | /// functions in a group can be set via the [`counters`] option. The following |
1063 | /// example emits info for the number of bytes and number of ints processed when |
1064 | /// benchmarking [slice sorting](slice::sort): |
1065 | /// |
1066 | /// ``` |
1067 | /// use divan::{Bencher, counter::{BytesCount, ItemsCount}}; |
1068 | /// |
1069 | /// const INTS: &[i32] = &[ |
1070 | /// // ... |
1071 | /// ]; |
1072 | /// |
1073 | /// #[divan::bench_group(counters = [ |
1074 | /// BytesCount::of_slice(INTS), |
1075 | /// ItemsCount::new(INTS.len()), |
1076 | /// ])] |
1077 | /// mod sort { |
1078 | /// use super::*; |
1079 | /// |
1080 | /// #[divan::bench] |
1081 | /// fn default(bencher: Bencher) { |
1082 | /// bencher |
1083 | /// .with_inputs(|| INTS.to_vec()) |
1084 | /// .bench_refs(|ints| ints.sort()); |
1085 | /// } |
1086 | /// |
1087 | /// #[divan::bench] |
1088 | /// fn unstable(bencher: Bencher) { |
1089 | /// bencher |
1090 | /// .with_inputs(|| INTS.to_vec()) |
1091 | /// .bench_refs(|ints| ints.sort_unstable()); |
1092 | /// } |
1093 | /// } |
1094 | /// # fn main() {} |
1095 | /// ``` |
1096 | /// |
1097 | /// For convenience, singular `counter` allows a single |
1098 | /// [`Counter`](crate::counter::Counter) to be set. The following example emits |
1099 | /// info for the number of bytes processed when benchmarking |
1100 | /// [`char`-counting](std::str::Chars::count) and |
1101 | /// [`char`-collecting](std::str::Chars::collect): |
1102 | /// |
1103 | /// ``` |
1104 | /// use divan::counter::BytesCount; |
1105 | /// |
1106 | /// const STR: &str = "..."; |
1107 | /// |
1108 | /// #[divan::bench_group(counter = BytesCount::of_str(STR))] |
1109 | /// mod chars { |
1110 | /// use super::STR; |
1111 | /// |
1112 | /// #[divan::bench] |
1113 | /// fn count() -> usize { |
1114 | /// divan::black_box(STR).chars().count() |
1115 | /// } |
1116 | /// |
1117 | /// #[divan::bench] |
1118 | /// fn collect() -> String { |
1119 | /// divan::black_box(STR).chars().collect() |
1120 | /// } |
1121 | /// } |
1122 | /// # fn main() {} |
1123 | /// ``` |
1124 | /// |
1125 | /// See: |
1126 | /// - [`#[divan::bench(counters = ...)]`](macro@bench#counters) |
1127 | /// - [`Bencher::counter`] |
1128 | /// - [`Bencher::input_counter`] |
1129 | /// |
1130 | /// ### `bytes_count` |
1131 | /// [`bytes_count`]: #bytes_count |
1132 | /// |
1133 | /// Convenience shorthand for |
1134 | /// <code>[counter](#counters) = [BytesCount](counter::BytesCount)::from(n)</code>. |
1135 | /// |
1136 | /// ### `chars_count` |
1137 | /// [`chars_count`]: #chars_count |
1138 | /// |
1139 | /// Convenience shorthand for |
1140 | /// <code>[counter](#counters) = [CharsCount](counter::CharsCount)::from(n)</code>. |
1141 | /// |
1142 | /// ### `cycles_count` |
1143 | /// [`cycles_count`]: #cycles_count |
1144 | /// |
1145 | /// Convenience shorthand for |
1146 | /// <code>[counter](#counters) = [CyclesCount](counter::CyclesCount)::from(n)</code>. |
1147 | /// |
1148 | /// ### `items_count` |
1149 | /// [`items_count`]: #items_count |
1150 | /// |
1151 | /// Convenience shorthand for |
1152 | /// <code>[counter](#counters) = [ItemsCount](counter::ItemsCount)::from(n)</code>. |
1153 | /// |
1154 | /// ## `min_time` |
1155 | /// [`min_time`]: #min_time |
1156 | /// |
1157 | /// The minimum time spent benchmarking each function can be set to a |
1158 | /// predetermined [`Duration`] via the [`min_time`] option. This may be |
1159 | /// overridden at runtime using either the `DIVAN_MIN_TIME` environment variable |
1160 | /// or `--min-time` CLI argument. |
1161 | /// |
1162 | /// Unless [`skip_ext_time`] is set, this includes time external to benchmarked |
1163 | /// functions, such as time spent generating inputs and running [`Drop`]. |
1164 | /// |
1165 | /// ``` |
1166 | /// use std::time::Duration; |
1167 | /// |
1168 | /// #[divan::bench_group(min_time = Duration::from_secs(3))] |
1169 | /// mod math { |
1170 | /// #[divan::bench] |
1171 | /// fn add() -> i32 { |
1172 | /// // ... |
1173 | /// # 0 |
1174 | /// } |
1175 | /// } |
1176 | /// ``` |
1177 | /// |
1178 | /// For convenience, [`min_time`] can also be set with seconds as [`u64`] or |
1179 | /// [`f64`]. Invalid values will cause a panic at runtime. |
1180 | /// |
1181 | /// ``` |
1182 | /// #[divan::bench_group(min_time = 2)] |
1183 | /// mod int_secs { |
1184 | /// // ... |
1185 | /// } |
1186 | /// |
1187 | /// #[divan::bench_group(min_time = 1.5)] |
1188 | /// mod float_secs { |
1189 | /// // ... |
1190 | /// } |
1191 | /// ``` |
1192 | /// |
1193 | /// ## `max_time` |
1194 | /// [`max_time`]: #max_time |
1195 | /// |
1196 | /// The maximum time spent benchmarking each function can be set to a |
1197 | /// predetermined [`Duration`] via the [`max_time`] option. This may be |
1198 | /// overridden at runtime using either the `DIVAN_MAX_TIME` environment variable |
1199 | /// or `--max-time` CLI argument. |
1200 | /// |
1201 | /// Unless [`skip_ext_time`] is set, this includes time external to benchmarked |
1202 | /// functions, such as time spent generating inputs and running [`Drop`]. |
1203 | /// |
1204 | /// If `min_time > max_time`, then [`max_time`] has priority and [`min_time`] |
1205 | /// will not be reached. |
1206 | /// |
1207 | /// ``` |
1208 | /// use std::time::Duration; |
1209 | /// |
1210 | /// #[divan::bench_group(max_time = Duration::from_secs(5))] |
1211 | /// mod math { |
1212 | /// #[divan::bench] |
1213 | /// fn add() -> i32 { |
1214 | /// // ... |
1215 | /// # 0 |
1216 | /// } |
1217 | /// } |
1218 | /// ``` |
1219 | /// |
1220 | /// For convenience, like [`min_time`], [`max_time`] can also be set with |
1221 | /// seconds as [`u64`] or [`f64`]. Invalid values will cause a panic at runtime. |
1222 | /// |
1223 | /// ``` |
1224 | /// #[divan::bench_group(max_time = 8)] |
1225 | /// mod int_secs { |
1226 | /// // ... |
1227 | /// } |
1228 | /// |
1229 | /// #[divan::bench_group(max_time = 9.5)] |
1230 | /// mod float_secs { |
1231 | /// // ... |
1232 | /// } |
1233 | /// ``` |
1234 | /// |
1235 | /// ## `skip_ext_time` |
1236 | /// [`skip_ext_time`]: #skip_ext_time |
1237 | /// |
1238 | /// By default, [`min_time`] and [`max_time`] include time external to |
1239 | /// benchmarked functions, such as time spent generating inputs and running |
1240 | /// [`Drop`]. Enabling the [`skip_ext_time`] option will instead make those |
1241 | /// options only consider time spent within benchmarked functions. This may be |
1242 | /// overridden at runtime using either the `DIVAN_SKIP_EXT_TIME` environment |
1243 | /// variable or `--skip-ext-time` CLI argument. |
1244 | /// |
1245 | /// In the following example, [`max_time`] only considers time spent running |
1246 | /// `measured_function`: |
1247 | /// |
1248 | /// ``` |
1249 | /// #[divan::bench_group(skip_ext_time)] |
1250 | /// mod group { |
1251 | /// # fn generate_input() {} |
1252 | /// # fn measured_function(_: ()) {} |
1253 | /// #[divan::bench(max_time = 5)] |
1254 | /// fn bench(bencher: divan::Bencher) { |
1255 | /// bencher |
1256 | /// .with_inputs(|| generate_input()) |
1257 | /// .bench_values(|input| measured_function(input)); |
1258 | /// } |
1259 | /// } |
1260 | /// ``` |
1261 | /// |
1262 | /// This option can be set to an explicit [`bool`] value to override parent |
1263 | /// values: |
1264 | /// |
1265 | /// ``` |
1266 | /// #[divan::bench_group(skip_ext_time = false)] |
1267 | /// mod group { |
1268 | /// // ... |
1269 | /// } |
1270 | /// ``` |
1271 | /// |
1272 | /// ## `ignore` |
1273 | /// [`ignore`]: #ignore |
1274 | /// |
1275 | /// Like [`#[test]`](https://doc.rust-lang.org/reference/attributes/testing.html#the-test-attribute) |
1276 | /// and [`#[divan::bench]`](macro@bench), `#[divan::bench_group]` functions can |
1277 | /// use [`#[ignore]`](https://doc.rust-lang.org/reference/attributes/testing.html#the-ignore-attribute): |
1278 | /// |
1279 | /// ``` |
1280 | /// #[divan::bench_group] |
1281 | /// #[ignore] |
1282 | /// mod math { |
1283 | /// #[divan::bench] |
1284 | /// fn todo() { |
1285 | /// unimplemented!(); |
1286 | /// } |
1287 | /// } |
1288 | /// # divan::main(); |
1289 | /// ``` |
1290 | /// |
1291 | /// This option can also instead be set within the `#[divan::bench_group]` |
1292 | /// attribute: |
1293 | /// |
1294 | /// ``` |
1295 | /// #[divan::bench_group(ignore)] |
1296 | /// mod math { |
1297 | /// #[divan::bench] |
1298 | /// fn todo() { |
1299 | /// unimplemented!(); |
1300 | /// } |
1301 | /// } |
1302 | /// # divan::main(); |
1303 | /// ``` |
1304 | /// |
1305 | /// Like [`skip_ext_time`], this option can be set to an explicit [`bool`] value |
1306 | /// to override parent values: |
1307 | /// |
1308 | /// ``` |
1309 | /// #[divan::bench_group(ignore = false)] |
1310 | /// mod group { |
1311 | /// // ... |
1312 | /// } |
1313 | /// ``` |
1314 | /// |
1315 | /// This can be used to ignore benchmarks based on a runtime condition. The |
1316 | /// following example benchmark group will be ignored if an [environment |
1317 | /// variable](std::env::var) is not set to "true": |
1318 | /// |
1319 | /// ``` |
1320 | /// #[divan::bench_group( |
1321 | /// ignore = std::env::var("BENCH_EXPENSIVE").as_deref() != Ok("true") |
1322 | /// )] |
1323 | /// mod expensive_benches { |
1324 | /// // ... |
1325 | /// } |
1326 | /// ``` |
1327 | /// |
1328 | /// [`Duration`]: std::time::Duration |
1329 | pub use divan_macros::bench_group; |
1330 | |