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}