iai_callgrind/
common.rs

1//! Common structs for `bin_bench` and `lib_bench`
2
3use derive_more::AsRef;
4use iai_callgrind_macros::IntoInner;
5
6use super::{internal, Direction, EventKind, FlamegraphKind, ValgrindTool};
7
8/// The `FlamegraphConfig` which allows the customization of the created flamegraphs
9///
10/// Callgrind flamegraphs are very similar to `callgrind_annotate` output. In contrast to
11/// `callgrind_annotate` text based output, the produced flamegraphs are svg files (located in the
12/// `target/iai` directory) which can be viewed in a browser.
13///
14/// # Experimental
15///
16/// Note the following considerations only affect flamegraphs of multi-threaded/multi-process
17/// benchmarks and benchmarks which produce multiple parts with a total over all sub-metrics.
18///
19/// Currently, Iai-Callgrind creates the flamegraphs only for the total over all threads/parts and
20/// subprocesses. This leads to complications since the call graph is not be fully recovered just by
21/// examining each thread/subprocess separately. So, the total metrics in the flamegraphs might not
22/// be the same as the total metrics shown in the terminal output. If in doubt, the terminal output
23/// shows the the correct metrics.
24///
25/// # Examples
26///
27/// ```rust
28/// # use iai_callgrind::{library_benchmark, library_benchmark_group};
29/// use iai_callgrind::{LibraryBenchmarkConfig, FlamegraphConfig, main};
30/// # #[library_benchmark]
31/// # fn some_func() {}
32/// # library_benchmark_group!(name = some_group; benchmarks = some_func);
33/// # fn main() {
34/// main!(
35///     config = LibraryBenchmarkConfig::default()
36///                 .flamegraph(FlamegraphConfig::default());
37///     library_benchmark_groups = some_group
38/// );
39/// # }
40/// ```
41#[derive(Debug, Clone, Default, IntoInner, AsRef)]
42pub struct FlamegraphConfig(internal::InternalFlamegraphConfig);
43
44/// Configure the default output format of the terminal output of Iai-Callgrind
45///
46/// This configuration is only applied to the default output format (`--output-format=default`) and
47/// not to any of the json output formats like (`--output-format=json`).
48///
49/// # Examples
50///
51/// For example configure the truncation length of the description to `200` for all library
52/// benchmarks in the same file with [`OutputFormat::truncate_description`]:
53///
54/// ```rust
55/// use iai_callgrind::{main, LibraryBenchmarkConfig, OutputFormat};
56/// # use iai_callgrind::{library_benchmark, library_benchmark_group};
57/// # #[library_benchmark]
58/// # fn some_func() {}
59/// # library_benchmark_group!(
60/// #    name = some_group;
61/// #    benchmarks = some_func
62/// # );
63/// # fn main() {
64/// main!(
65///     config = LibraryBenchmarkConfig::default()
66///         .output_format(OutputFormat::default()
67///             .truncate_description(Some(200))
68///         );
69///     library_benchmark_groups = some_group
70/// );
71/// # }
72#[derive(Debug, Clone, Default, IntoInner, AsRef)]
73pub struct OutputFormat(internal::InternalOutputFormat);
74
75impl OutputFormat {
76    /// Adjust, enable or disable the truncation of the description in the iai-callgrind output
77    ///
78    /// The default is to truncate the description to the size of 50 ascii characters. A `None`
79    /// value disables the truncation entirely and a `Some` value will truncate the description to
80    /// the given amount of characters excluding the ellipsis.
81    ///
82    /// To clearify which part of the output is meant by `DESCRIPTION`:
83    ///
84    /// ```text
85    /// benchmark_file::group_name::function_name id:DESCRIPTION
86    ///   Instructions:              352135|352135          (No change)
87    ///   L1 Hits:                   470117|470117          (No change)
88    ///   L2 Hits:                      748|748             (No change)
89    ///   RAM Hits:                    4112|4112            (No change)
90    ///   Total read+write:          474977|474977          (No change)
91    ///   Estimated Cycles:          617777|617777          (No change)
92    /// ```
93    ///
94    /// # Examples
95    ///
96    /// For example, specifying this option with a `None` value in the `main!` macro disables the
97    /// truncation of the description for all benchmarks.
98    ///
99    /// ```rust
100    /// use iai_callgrind::{main, LibraryBenchmarkConfig, OutputFormat};
101    /// # use iai_callgrind::{library_benchmark, library_benchmark_group};
102    /// # #[library_benchmark]
103    /// # fn some_func() {}
104    /// # library_benchmark_group!(
105    /// #    name = some_group;
106    /// #    benchmarks = some_func
107    /// # );
108    /// # fn main() {
109    /// main!(
110    ///     config = LibraryBenchmarkConfig::default()
111    ///         .output_format(OutputFormat::default()
112    ///             .truncate_description(None)
113    ///         );
114    ///     library_benchmark_groups = some_group
115    /// );
116    /// # }
117    /// ```
118    pub fn truncate_description(&mut self, value: Option<usize>) -> &mut Self {
119        self.0.truncate_description = Some(value);
120        self
121    }
122
123    /// Show intermediate metrics from parts, subprocesses, threads, ... (Default: false)
124    ///
125    /// In callgrind, threads are treated as separate units (similar to subprocesses) and the
126    /// metrics for them are dumped into an own file. Other valgrind tools usually separate the
127    /// output files only by subprocesses. To also show the metrics of any intermediate fragments
128    /// and not just the total over all of them, set the value of this method to `true`.
129    ///
130    /// Temporarily setting `show_intermediate` to `true` can help to find misconfigurations in
131    /// multi-thread/multi-process benchmarks.
132    ///
133    /// # Examples
134    ///
135    /// As opposed to valgrind/callgrind, `--trace-children=yes`, `--separate-threads=yes` and
136    /// `--fair-sched=try` are the defaults in Iai-Callgrind, so in the following example it's not
137    /// necessary to specify `--separate-threads` to track the metrics of the spawned thread.
138    /// However, it is necessary to specify an additional toggle or else the metrics of the thread
139    /// are all zero. We also set the [`super::EntryPoint`] to `None` to disable the default entry
140    /// point (toggle) which is the benchmark function. So, with this setup we collect only the
141    /// metrics of the method `my_lib::heavy_calculation` in the spawned thread and nothing else.
142    ///
143    /// ```rust
144    /// use iai_callgrind::{
145    ///     main, LibraryBenchmarkConfig, OutputFormat, EntryPoint, library_benchmark,
146    ///     library_benchmark_group
147    /// };
148    /// # mod my_lib { pub fn heavy_calculation() -> u64 { 42 }}
149    ///
150    /// #[library_benchmark(
151    ///     config = LibraryBenchmarkConfig::default()
152    ///         .entry_point(EntryPoint::None)
153    ///         .callgrind_args(["--toggle-collect=my_lib::heavy_calculation"])
154    ///         .output_format(OutputFormat::default().show_intermediate(true))
155    /// )]
156    /// fn bench_thread() -> u64 {
157    ///     let handle = std::thread::spawn(|| my_lib::heavy_calculation());
158    ///     handle.join().unwrap()
159    /// }
160    ///
161    /// library_benchmark_group!(name = some_group; benchmarks = bench_thread);
162    /// # fn main() {
163    /// main!(library_benchmark_groups = some_group);
164    /// # }
165    /// ```
166    ///
167    /// Running the above benchmark the first time will print something like the below (The exact
168    /// metric counts are made up for demonstration purposes):
169    ///
170    /// ```text
171    /// my_benchmark::some_group::bench_thread
172    ///   ## pid: 633247 part: 1 thread: 1   |N/A
173    ///   Command:            target/release/deps/my_benchmark-08fe8356975cd1af
174    ///   Instructions:                     0|N/A             (*********)
175    ///   L1 Hits:                          0|N/A             (*********)
176    ///   L2 Hits:                          0|N/A             (*********)
177    ///   RAM Hits:                         0|N/A             (*********)
178    ///   Total read+write:                 0|N/A             (*********)
179    ///   Estimated Cycles:                 0|N/A             (*********)
180    ///   ## pid: 633247 part: 1 thread: 2   |N/A
181    ///   Command:            target/release/deps/my_benchmark-08fe8356975cd1af
182    ///   Instructions:                  3905|N/A             (*********)
183    ///   L1 Hits:                       4992|N/A             (*********)
184    ///   L2 Hits:                          0|N/A             (*********)
185    ///   RAM Hits:                       464|N/A             (*********)
186    ///   Total read+write:              5456|N/A             (*********)
187    ///   Estimated Cycles:             21232|N/A             (*********)
188    ///   ## Total
189    ///   Instructions:                  3905|N/A             (*********)
190    ///   L1 Hits:                       4992|N/A             (*********)
191    ///   L2 Hits:                          0|N/A             (*********)
192    ///   RAM Hits:                       464|N/A             (*********)
193    ///   Total read+write:              5456|N/A             (*********)
194    ///   Estimated Cycles:             21232|N/A             (*********)
195    /// ```
196    ///
197    /// With `show_intermediate` set to `false` (the default), only the total is shown:
198    ///
199    /// ```text
200    /// my_benchmark::some_group::bench_thread
201    ///   Instructions:                  3905|N/A             (*********)
202    ///   L1 Hits:                       4992|N/A             (*********)
203    ///   L2 Hits:                          0|N/A             (*********)
204    ///   RAM Hits:                       464|N/A             (*********)
205    ///   Total read+write:              5456|N/A             (*********)
206    ///   Estimated Cycles:             21232|N/A             (*********)
207    /// ```
208    pub fn show_intermediate(&mut self, value: bool) -> &mut Self {
209        self.0.show_intermediate = Some(value);
210        self
211    }
212
213    /// Show an ascii grid in the benchmark terminal output
214    ///
215    /// This option adds guiding lines which can help reading the benchmark output when running
216    /// multiple tools with multiple threads/subprocesses.
217    ///
218    /// # Examples
219    ///
220    /// ```rust
221    /// use iai_callgrind::OutputFormat;
222    ///
223    /// let output_format = OutputFormat::default().show_grid(true);
224    /// ```
225    ///
226    /// Below is the output of a Iai-Callgrind run with DHAT as additional tool benchmarking a
227    /// function that executes a subprocess which itself starts multiple threads. For the benchmark
228    /// run below [`OutputFormat::show_intermediate`] was also active to show the threads and
229    /// subprocesses.
230    ///
231    /// ```text
232    /// test_lib_bench_threads::bench_group::bench_thread_in_subprocess three:3
233    /// |======== CALLGRIND ===================================================================
234    /// |-## pid: 3186352 part: 1 thread: 1       |pid: 2721318 part: 1 thread: 1
235    /// | Command:            target/release/deps/test_lib_bench_threads-b0b85adec9a45de1
236    /// | Instructions:                       4697|4697                 (No change)
237    /// | L1 Hits:                            6420|6420                 (No change)
238    /// | L2 Hits:                              17|17                   (No change)
239    /// | RAM Hits:                            202|202                  (No change)
240    /// | Total read+write:                   6639|6639                 (No change)
241    /// | Estimated Cycles:                  13575|13575                (No change)
242    /// |-## pid: 3186468 part: 1 thread: 1       |pid: 2721319 part: 1 thread: 1
243    /// | Command:            target/release/thread 3
244    /// | Instructions:                      35452|35452                (No change)
245    /// | L1 Hits:                           77367|77367                (No change)
246    /// | L2 Hits:                             610|610                  (No change)
247    /// | RAM Hits:                            784|784                  (No change)
248    /// | Total read+write:                  78761|78761                (No change)
249    /// | Estimated Cycles:                 107857|107857               (No change)
250    /// |-## pid: 3186468 part: 1 thread: 2       |pid: 2721319 part: 1 thread: 2
251    /// | Command:            target/release/thread 3
252    /// | Instructions:                    2460507|2460507              (No change)
253    /// | L1 Hits:                         2534939|2534939              (No change)
254    /// | L2 Hits:                              17|17                   (No change)
255    /// | RAM Hits:                            186|186                  (No change)
256    /// | Total read+write:                2535142|2535142              (No change)
257    /// | Estimated Cycles:                2541534|2541534              (No change)
258    /// |-## pid: 3186468 part: 1 thread: 3       |pid: 2721319 part: 1 thread: 3
259    /// | Command:            target/release/thread 3
260    /// | Instructions:                    3650414|3650414              (No change)
261    /// | L1 Hits:                         3724275|3724275              (No change)
262    /// | L2 Hits:                              21|21                   (No change)
263    /// | RAM Hits:                            130|130                  (No change)
264    /// | Total read+write:                3724426|3724426              (No change)
265    /// | Estimated Cycles:                3728930|3728930              (No change)
266    /// |-## pid: 3186468 part: 1 thread: 4       |pid: 2721319 part: 1 thread: 4
267    /// | Command:            target/release/thread 3
268    /// | Instructions:                    4349846|4349846              (No change)
269    /// | L1 Hits:                         4423438|4423438              (No change)
270    /// | L2 Hits:                              24|24                   (No change)
271    /// | RAM Hits:                            125|125                  (No change)
272    /// | Total read+write:                4423587|4423587              (No change)
273    /// | Estimated Cycles:                4427933|4427933              (No change)
274    /// |-## Total
275    /// | Instructions:                   10500916|10500916             (No change)
276    /// | L1 Hits:                        10766439|10766439             (No change)
277    /// | L2 Hits:                             689|689                  (No change)
278    /// | RAM Hits:                           1427|1427                 (No change)
279    /// | Total read+write:               10768555|10768555             (No change)
280    /// | Estimated Cycles:               10819829|10819829             (No change)
281    /// |======== DHAT ========================================================================
282    /// |-## pid: 3186472 ppid: 3185288           |pid: 2721323 ppid: 2720196
283    /// | Command:            target/release/deps/test_lib_bench_threads-b0b85adec9a45de1
284    /// | Total bytes:                        2774|2774                 (No change)
285    /// | Total blocks:                         24|24                   (No change)
286    /// | At t-gmax bytes:                    1736|1736                 (No change)
287    /// | At t-gmax blocks:                      3|3                    (No change)
288    /// | At t-end bytes:                        0|0                    (No change)
289    /// | At t-end blocks:                       0|0                    (No change)
290    /// | Reads bytes:                       21054|21054                (No change)
291    /// | Writes bytes:                      13165|13165                (No change)
292    /// |-## pid: 3186473 ppid: 3186472           |pid: 2721324 ppid: 2721323
293    /// | Command:            target/release/thread 3
294    /// | Total bytes:                      156158|156158               (No change)
295    /// | Total blocks:                         73|73                   (No change)
296    /// | At t-gmax bytes:                   52225|52225                (No change)
297    /// | At t-gmax blocks:                     19|19                   (No change)
298    /// | At t-end bytes:                        0|0                    (No change)
299    /// | At t-end blocks:                       0|0                    (No change)
300    /// | Reads bytes:                      118403|118403               (No change)
301    /// | Writes bytes:                     135926|135926               (No change)
302    /// |-## Total
303    /// | Total bytes:                      158932|158932               (No change)
304    /// | Total blocks:                         97|97                   (No change)
305    /// | At t-gmax bytes:                   53961|53961                (No change)
306    /// | At t-gmax blocks:                     22|22                   (No change)
307    /// | At t-end bytes:                        0|0                    (No change)
308    /// | At t-end blocks:                       0|0                    (No change)
309    /// | Reads bytes:                      139457|139457               (No change)
310    /// | Writes bytes:                     149091|149091               (No change)
311    /// |-Comparison with bench_find_primes_multi_thread three:3
312    /// | Instructions:                   10494117|10500916             (-0.06475%) [-1.00065x]
313    /// | L1 Hits:                        10757259|10766439             (-0.08526%) [-1.00085x]
314    /// | L2 Hits:                             601|689                  (-12.7721%) [-1.14642x]
315    /// | RAM Hits:                           1189|1427                 (-16.6783%) [-1.20017x]
316    /// | Total read+write:               10759049|10768555             (-0.08828%) [-1.00088x]
317    /// | Estimated Cycles:               10801879|10819829             (-0.16590%) [-1.00166x]
318    pub fn show_grid(&mut self, value: bool) -> &mut Self {
319        self.0.show_grid = Some(value);
320        self
321    }
322}
323
324/// Configure performance regression checks and behavior
325///
326/// A performance regression check consists of an [`EventKind`] and a percentage over which a
327/// regression is assumed. If the percentage is negative, then a regression is assumed to be below
328/// this limit. The default [`EventKind`] is [`EventKind::Ir`] with a value of
329/// `+10f64`
330///
331/// If `fail_fast` is set to true, then the whole benchmark run fails on the first encountered
332/// regression. Else, the default behavior is, that the benchmark run fails with a regression error
333/// after all benchmarks have been run.
334///
335/// # Examples
336///
337/// ```rust
338/// # use iai_callgrind::{library_benchmark, library_benchmark_group};
339/// use iai_callgrind::{main, LibraryBenchmarkConfig, RegressionConfig};
340/// # #[library_benchmark]
341/// # fn some_func() {}
342/// # library_benchmark_group!(name = some_group; benchmarks = some_func);
343/// # fn main() {
344/// main!(
345///     config = LibraryBenchmarkConfig::default()
346///                 .regression(RegressionConfig::default());
347///     library_benchmark_groups = some_group
348/// );
349/// # }
350/// ```
351#[derive(Debug, Default, Clone, IntoInner, AsRef)]
352pub struct RegressionConfig(internal::InternalRegressionConfig);
353
354/// Configure to run other valgrind tools like `DHAT` or `Massif` in addition to callgrind
355///
356/// For a list of possible tools see [`ValgrindTool`].
357///
358/// See also the [Valgrind User Manual](https://valgrind.org/docs/manual/manual.html) for details
359/// about possible tools and their command line arguments.
360///
361/// # Examples
362///
363/// ```rust
364/// # use iai_callgrind::{library_benchmark, library_benchmark_group};
365/// use iai_callgrind::{main, LibraryBenchmarkConfig, Tool, ValgrindTool};
366/// # #[library_benchmark]
367/// # fn some_func() {}
368/// # library_benchmark_group!(name = some_group; benchmarks = some_func);
369/// # fn main() {
370/// main!(
371///     config = LibraryBenchmarkConfig::default()
372///                 .tool(Tool::new(ValgrindTool::DHAT));
373///     library_benchmark_groups = some_group
374/// );
375/// # }
376/// ```
377#[derive(Debug, Clone, PartialEq, Eq, IntoInner, AsRef)]
378pub struct Tool(internal::InternalTool);
379
380impl FlamegraphConfig {
381    /// Option to change the [`FlamegraphKind`]
382    ///
383    /// The default is [`FlamegraphKind::All`].
384    ///
385    /// # Examples
386    ///
387    /// For example, to only create a differential flamegraph:
388    ///
389    /// ```
390    /// use iai_callgrind::{FlamegraphConfig, FlamegraphKind};
391    ///
392    /// let config = FlamegraphConfig::default().kind(FlamegraphKind::Differential);
393    /// ```
394    pub fn kind(&mut self, kind: FlamegraphKind) -> &mut Self {
395        self.0.kind = Some(kind);
396        self
397    }
398
399    /// Negate the differential flamegraph [`FlamegraphKind::Differential`]
400    ///
401    /// The default is `false`.
402    ///
403    /// Instead of showing the differential flamegraph from the viewing angle of what has happened
404    /// the negated differential flamegraph shows what will happen. Especially, this allows to see
405    /// vanished event lines (in blue) for example because the underlying code has improved and
406    /// removed an unnecessary function call.
407    ///
408    /// See also [Differential Flame
409    /// Graphs](https://www.brendangregg.com/blog/2014-11-09/differential-flame-graphs.html) from
410    /// Brendan Gregg's Blog.
411    ///
412    /// # Examples
413    ///
414    /// ```
415    /// use iai_callgrind::{FlamegraphConfig, FlamegraphKind};
416    ///
417    /// let config = FlamegraphConfig::default().negate_differential(true);
418    /// ```
419    pub fn negate_differential(&mut self, negate_differential: bool) -> &mut Self {
420        self.0.negate_differential = Some(negate_differential);
421        self
422    }
423
424    /// Normalize the differential flamegraph
425    ///
426    /// This'll make the first profile event count to match the second. This'll help in situations
427    /// when everything looks read (or blue) to get a balanced profile with the full red/blue
428    /// spectrum
429    ///
430    /// # Examples
431    ///
432    /// ```
433    /// use iai_callgrind::{FlamegraphConfig, FlamegraphKind};
434    ///
435    /// let config = FlamegraphConfig::default().normalize_differential(true);
436    /// ```
437    pub fn normalize_differential(&mut self, normalize_differential: bool) -> &mut Self {
438        self.0.normalize_differential = Some(normalize_differential);
439        self
440    }
441
442    /// One or multiple [`EventKind`] for which a flamegraph is going to be created.
443    ///
444    /// The default is [`EventKind::Ir`]
445    ///
446    /// Currently, flamegraph creation is limited to one flamegraph for each [`EventKind`] and
447    /// there's no way to merge all event kinds into a single flamegraph.
448    ///
449    /// Note it is an error to specify a [`EventKind`] which isn't recorded by callgrind. See the
450    /// docs of the variants of [`EventKind`] which callgrind option is needed to create a record
451    /// for it. See also the [Callgrind
452    /// Documentation](https://valgrind.org/docs/manual/cl-manual.html#cl-manual.options). The
453    /// [`EventKind`]s recorded by callgrind which are available as long as the cache simulation is
454    /// turned on with `--cache-sim=yes` (which is the default):
455    ///
456    /// * [`EventKind::Ir`]
457    /// * [`EventKind::Dr`]
458    /// * [`EventKind::Dw`]
459    /// * [`EventKind::I1mr`]
460    /// * [`EventKind::ILmr`]
461    /// * [`EventKind::D1mr`]
462    /// * [`EventKind::DLmr`]
463    /// * [`EventKind::D1mw`]
464    /// * [`EventKind::DLmw`]
465    ///
466    /// If the cache simulation is turned on, the following derived `EventKinds` are also available:
467    ///
468    /// * [`EventKind::L1hits`]
469    /// * [`EventKind::LLhits`]
470    /// * [`EventKind::RamHits`]
471    /// * [`EventKind::TotalRW`]
472    /// * [`EventKind::EstimatedCycles`]
473    ///
474    /// # Examples
475    ///
476    /// ```
477    /// use iai_callgrind::{EventKind, FlamegraphConfig};
478    ///
479    /// let config =
480    ///     FlamegraphConfig::default().event_kinds([EventKind::EstimatedCycles, EventKind::Ir]);
481    /// ```
482    pub fn event_kinds<T>(&mut self, event_kinds: T) -> &mut Self
483    where
484        T: IntoIterator<Item = EventKind>,
485    {
486        self.0.event_kinds = Some(event_kinds.into_iter().collect());
487        self
488    }
489
490    /// Set the [`Direction`] in which the flamegraph should grow.
491    ///
492    /// The default is [`Direction::TopToBottom`].
493    ///
494    /// # Examples
495    ///
496    /// For example to change the default
497    ///
498    /// ```
499    /// use iai_callgrind::{Direction, FlamegraphConfig};
500    ///
501    /// let config = FlamegraphConfig::default().direction(Direction::BottomToTop);
502    /// ```
503    pub fn direction(&mut self, direction: Direction) -> &mut Self {
504        self.0.direction = Some(direction);
505        self
506    }
507
508    /// Overwrite the default title of the final flamegraph
509    ///
510    /// # Examples
511    ///
512    /// ```
513    /// use iai_callgrind::{Direction, FlamegraphConfig};
514    ///
515    /// let config = FlamegraphConfig::default().title("My flamegraph title".to_owned());
516    /// ```
517    pub fn title(&mut self, title: String) -> &mut Self {
518        self.0.title = Some(title);
519        self
520    }
521
522    /// Overwrite the default subtitle of the final flamegraph
523    ///
524    /// # Examples
525    ///
526    /// ```
527    /// use iai_callgrind::FlamegraphConfig;
528    ///
529    /// let config = FlamegraphConfig::default().subtitle("My flamegraph subtitle".to_owned());
530    /// ```
531    pub fn subtitle(&mut self, subtitle: String) -> &mut Self {
532        self.0.subtitle = Some(subtitle);
533        self
534    }
535
536    /// Set the minimum width (in pixels) for which event lines are going to be shown.
537    ///
538    /// The default is `0.1`
539    ///
540    /// To show all events, set the `min_width` to `0f64`.
541    ///
542    /// # Examples
543    ///
544    /// ```
545    /// use iai_callgrind::FlamegraphConfig;
546    ///
547    /// let config = FlamegraphConfig::default().min_width(0f64);
548    /// ```
549    pub fn min_width(&mut self, min_width: f64) -> &mut Self {
550        self.0.min_width = Some(min_width);
551        self
552    }
553}
554
555/// Enable performance regression checks with a [`RegressionConfig`]
556///
557/// A performance regression check consists of an [`EventKind`] and a percentage over which a
558/// regression is assumed. If the percentage is negative, then a regression is assumed to be below
559/// this limit. The default [`EventKind`] is [`EventKind::Ir`] with a value of
560/// `+10f64`
561///
562/// If `fail_fast` is set to true, then the whole benchmark run fails on the first encountered
563/// regression. Else, the default behavior is, that the benchmark run fails with a regression error
564/// after all benchmarks have been run.
565///
566/// # Examples
567///
568/// ```rust
569/// # use iai_callgrind::{library_benchmark, library_benchmark_group, main};
570/// # #[library_benchmark]
571/// # fn some_func() {}
572/// # library_benchmark_group!(name = some_group; benchmarks = some_func);
573/// use iai_callgrind::{LibraryBenchmarkConfig, RegressionConfig};
574///
575/// # fn main() {
576/// main!(
577///     config = LibraryBenchmarkConfig::default()
578///                 .regression(RegressionConfig::default());
579///     library_benchmark_groups = some_group
580/// );
581/// # }
582/// ```
583impl RegressionConfig {
584    /// Configure the limits percentages over/below which a performance regression can be assumed
585    ///
586    /// A performance regression check consists of an [`EventKind`] and a percentage over which a
587    /// regression is assumed. If the percentage is negative, then a regression is assumed to be
588    /// below this limit.
589    ///
590    /// If no `limits` or empty `targets` are specified with this function, the default
591    /// [`EventKind`] is [`EventKind::Ir`] with a value of `+10f64`
592    ///
593    /// # Examples
594    ///
595    /// ```
596    /// use iai_callgrind::{EventKind, RegressionConfig};
597    ///
598    /// let config = RegressionConfig::default().limits([(EventKind::Ir, 5f64)]);
599    /// ```
600    pub fn limits<T>(&mut self, targets: T) -> &mut Self
601    where
602        T: IntoIterator<Item = (EventKind, f64)>,
603    {
604        self.0.limits.extend(targets);
605        self
606    }
607
608    /// If set to true, then the benchmarks fail on the first encountered regression
609    ///
610    /// The default is `false` and the whole benchmark run fails with a regression error after all
611    /// benchmarks have been run.
612    ///
613    /// # Examples
614    ///
615    /// ```
616    /// use iai_callgrind::RegressionConfig;
617    ///
618    /// let config = RegressionConfig::default().fail_fast(true);
619    /// ```
620    pub fn fail_fast(&mut self, value: bool) -> &mut Self {
621        self.0.fail_fast = Some(value);
622        self
623    }
624}
625
626impl Tool {
627    /// Create a new `Tool` configuration
628    ///
629    /// # Examples
630    ///
631    /// ```
632    /// use iai_callgrind::{Tool, ValgrindTool};
633    ///
634    /// let tool = Tool::new(ValgrindTool::DHAT);
635    /// ```
636    pub fn new(tool: ValgrindTool) -> Self {
637        Self(internal::InternalTool {
638            kind: tool,
639            enable: Option::default(),
640            show_log: Option::default(),
641            raw_args: internal::InternalRawArgs::default(),
642        })
643    }
644
645    /// If true, enable running this `Tool` (Default: true)
646    ///
647    /// # Examples
648    ///
649    /// ```
650    /// use iai_callgrind::{Tool, ValgrindTool};
651    ///
652    /// let tool = Tool::new(ValgrindTool::DHAT).enable(true);
653    /// ```
654    pub fn enable(&mut self, value: bool) -> &mut Self {
655        self.0.enable = Some(value);
656        self
657    }
658
659    /// Pass one or more arguments directly to the valgrind `Tool`
660    ///
661    /// # Examples
662    ///
663    /// ```
664    /// use iai_callgrind::{Tool, ValgrindTool};
665    ///
666    /// let tool = Tool::new(ValgrindTool::DHAT).args(["--num-callers=5", "--mode=heap"]);
667    /// ```
668    pub fn args<I, T>(&mut self, args: T) -> &mut Self
669    where
670        I: AsRef<str>,
671        T: IntoIterator<Item = I>,
672    {
673        self.0.raw_args.extend_ignore_flag(args);
674        self
675    }
676}
677
678/// __DEPRECATED__: A function that is opaque to the optimizer
679///
680/// It is used to prevent the compiler from optimizing away computations in a benchmark.
681///
682/// This method is deprecated and is in newer versions of `iai-callgrind` merely a wrapper around
683/// [`std::hint::black_box`]. Please use `std::hint::black_box` directly.
684#[inline]
685pub fn black_box<T>(dummy: T) -> T {
686    std::hint::black_box(dummy)
687}