1 //! A statistics-driven micro-benchmarking library written in Rust.
2 //!
3 //! This crate is a microbenchmarking library which aims to provide strong
4 //! statistical confidence in detecting and estimating the size of performance
5 //! improvements and regressions, while also being easy to use.
6 //!
7 //! See
8 //! [the user guide](https://bheisler.github.io/criterion.rs/book/index.html)
9 //! for examples as well as details on the measurement and analysis process,
10 //! and the output.
11 //!
12 //! ## Features:
13 //! * Collects detailed statistics, providing strong confidence that changes
14 //!   to performance are real, not measurement noise
15 //! * Produces detailed charts, providing thorough understanding of your code's
16 //!   performance behavior.
17 
18 #![deny(missing_docs)]
19 #![deny(bare_trait_objects)]
20 #![deny(warnings)]
21 #![cfg_attr(feature = "real_blackbox", feature(test))]
22 #![cfg_attr(
23     feature = "cargo-clippy",
24     allow(
25         clippy::just_underscores_and_digits, // Used in the stats code
26         clippy::transmute_ptr_to_ptr, // Used in the stats code
27         clippy::option_as_ref_deref, // Remove when MSRV bumped above 1.40
28     )
29 )]
30 
31 #[cfg(test)]
32 #[macro_use]
33 extern crate approx;
34 
35 #[cfg(test)]
36 #[macro_use]
37 extern crate quickcheck;
38 
39 use clap::value_t;
40 use regex::Regex;
41 
42 #[macro_use]
43 extern crate lazy_static;
44 
45 #[cfg(feature = "real_blackbox")]
46 extern crate test;
47 
48 #[macro_use]
49 extern crate serde_derive;
50 
51 // Needs to be declared before other modules
52 // in order to be usable there.
53 #[macro_use]
54 mod macros_private;
55 #[macro_use]
56 mod analysis;
57 mod benchmark;
58 #[macro_use]
59 mod benchmark_group;
60 mod csv_report;
61 mod error;
62 mod estimate;
63 mod format;
64 mod fs;
65 mod html;
66 mod kde;
67 mod macros;
68 pub mod measurement;
69 mod plot;
70 pub mod profiler;
71 mod report;
72 mod routine;
73 mod stats;
74 
75 use std::cell::RefCell;
76 use std::collections::{BTreeMap, HashSet};
77 use std::default::Default;
78 use std::fmt;
79 use std::iter::IntoIterator;
80 use std::marker::PhantomData;
81 use std::path::{Path, PathBuf};
82 use std::time::Duration;
83 use std::time::Instant;
84 
85 use criterion_plot::{Version, VersionError};
86 
87 use crate::benchmark::BenchmarkConfig;
88 use crate::benchmark::NamedRoutine;
89 use crate::csv_report::FileCsvReport;
90 use crate::estimate::{Distributions, Estimates, Statistic};
91 use crate::html::Html;
92 use crate::measurement::{Measurement, WallTime};
93 use crate::plot::{Gnuplot, Plotter, PlottersBackend};
94 use crate::profiler::{ExternalProfiler, Profiler};
95 use crate::report::{BencherReport, CliReport, Report, ReportContext, Reports};
96 use crate::routine::Function;
97 
98 pub use crate::benchmark::{Benchmark, BenchmarkDefinition, ParameterizedBenchmark};
99 pub use crate::benchmark_group::{BenchmarkGroup, BenchmarkId};
100 
101 lazy_static! {
102     static ref DEBUG_ENABLED: bool = std::env::var_os("CRITERION_DEBUG").is_some();
103     static ref GNUPLOT_VERSION: Result<Version, VersionError> = criterion_plot::version();
104     static ref DEFAULT_PLOTTING_BACKEND: PlottingBackend = {
105         match &*GNUPLOT_VERSION {
106             Ok(_) => PlottingBackend::Gnuplot,
107             Err(e) => {
108                 match e {
109                     VersionError::Exec(_) => println!("Gnuplot not found, using plotters backend"),
110                     e => println!(
111                         "Gnuplot not found or not usable, using plotters backend\n{}",
112                         e
113                     ),
114                 };
115                 PlottingBackend::Plotters
116             }
117         }
118     };
119 }
120 
debug_enabled() -> bool121 fn debug_enabled() -> bool {
122     *DEBUG_ENABLED
123 }
124 
125 /// A function that is opaque to the optimizer, used to prevent the compiler from
126 /// optimizing away computations in a benchmark.
127 ///
128 /// This variant is backed by the (unstable) test::black_box function.
129 #[cfg(feature = "real_blackbox")]
black_box<T>(dummy: T) -> T130 pub fn black_box<T>(dummy: T) -> T {
131     test::black_box(dummy)
132 }
133 
134 /// A function that is opaque to the optimizer, used to prevent the compiler from
135 /// optimizing away computations in a benchmark.
136 ///
137 /// This variant is stable-compatible, but it may cause some performance overhead
138 /// or fail to prevent code from being eliminated.
139 #[cfg(not(feature = "real_blackbox"))]
black_box<T>(dummy: T) -> T140 pub fn black_box<T>(dummy: T) -> T {
141     unsafe {
142         let ret = std::ptr::read_volatile(&dummy);
143         std::mem::forget(dummy);
144         ret
145     }
146 }
147 
148 /// Representing a function to benchmark together with a name of that function.
149 /// Used together with `bench_functions` to represent one out of multiple functions
150 /// under benchmark.
151 #[doc(hidden)]
152 pub struct Fun<I: fmt::Debug, M: Measurement + 'static = WallTime> {
153     f: NamedRoutine<I, M>,
154     _phantom: PhantomData<M>,
155 }
156 
157 impl<I, M: Measurement> Fun<I, M>
158 where
159     I: fmt::Debug + 'static,
160 {
161     /// Create a new `Fun` given a name and a closure
new<F>(name: &str, f: F) -> Fun<I, M> where F: FnMut(&mut Bencher<'_, M>, &I) + 'static,162     pub fn new<F>(name: &str, f: F) -> Fun<I, M>
163     where
164         F: FnMut(&mut Bencher<'_, M>, &I) + 'static,
165     {
166         let routine = NamedRoutine {
167             id: name.to_owned(),
168             f: Box::new(RefCell::new(Function::new(f))),
169         };
170 
171         Fun {
172             f: routine,
173             _phantom: PhantomData,
174         }
175     }
176 }
177 
178 /// Argument to [`Bencher::iter_batched`](struct.Bencher.html#method.iter_batched) and
179 /// [`Bencher::iter_batched_ref`](struct.Bencher.html#method.iter_batched_ref) which controls the
180 /// batch size.
181 ///
182 /// Generally speaking, almost all benchmarks should use `SmallInput`. If the input or the result
183 /// of the benchmark routine is large enough that `SmallInput` causes out-of-memory errors,
184 /// `LargeInput` can be used to reduce memory usage at the cost of increasing the measurement
185 /// overhead. If the input or the result is extremely large (or if it holds some
186 /// limited external resource like a file handle), `PerIteration` will set the number of iterations
187 /// per batch to exactly one. `PerIteration` can increase the measurement overhead substantially
188 /// and should be avoided wherever possible.
189 ///
190 /// Each value lists an estimate of the measurement overhead. This is intended as a rough guide
191 /// to assist in choosing an option, it should not be relied upon. In particular, it is not valid
192 /// to subtract the listed overhead from the measurement and assume that the result represents the
193 /// true runtime of a function. The actual measurement overhead for your specific benchmark depends
194 /// on the details of the function you're benchmarking and the hardware and operating
195 /// system running the benchmark.
196 ///
197 /// With that said, if the runtime of your function is small relative to the measurement overhead
198 /// it will be difficult to take accurate measurements. In this situation, the best option is to use
199 /// [`Bencher::iter`](struct.Bencher.html#method.iter) which has next-to-zero measurement overhead.
200 #[derive(Debug, Eq, PartialEq, Copy, Hash, Clone)]
201 pub enum BatchSize {
202     /// `SmallInput` indicates that the input to the benchmark routine (the value returned from
203     /// the setup routine) is small enough that millions of values can be safely held in memory.
204     /// Always prefer `SmallInput` unless the benchmark is using too much memory.
205     ///
206     /// In testing, the maximum measurement overhead from benchmarking with `SmallInput` is on the
207     /// order of 500 picoseconds. This is presented as a rough guide; your results may vary.
208     SmallInput,
209 
210     /// `LargeInput` indicates that the input to the benchmark routine or the value returned from
211     /// that routine is large. This will reduce the memory usage but increase the measurement
212     /// overhead.
213     ///
214     /// In testing, the maximum measurement overhead from benchmarking with `LargeInput` is on the
215     /// order of 750 picoseconds. This is presented as a rough guide; your results may vary.
216     LargeInput,
217 
218     /// `PerIteration` indicates that the input to the benchmark routine or the value returned from
219     /// that routine is extremely large or holds some limited resource, such that holding many values
220     /// in memory at once is infeasible. This provides the worst measurement overhead, but the
221     /// lowest memory usage.
222     ///
223     /// In testing, the maximum measurement overhead from benchmarking with `PerIteration` is on the
224     /// order of 350 nanoseconds or 350,000 picoseconds. This is presented as a rough guide; your
225     /// results may vary.
226     PerIteration,
227 
228     /// `NumBatches` will attempt to divide the iterations up into a given number of batches.
229     /// A larger number of batches (and thus smaller batches) will reduce memory usage but increase
230     /// measurement overhead. This allows the user to choose their own tradeoff between memory usage
231     /// and measurement overhead, but care must be taken in tuning the number of batches. Most
232     /// benchmarks should use `SmallInput` or `LargeInput` instead.
233     NumBatches(u64),
234 
235     /// `NumIterations` fixes the batch size to a constant number, specified by the user. This
236     /// allows the user to choose their own tradeoff between overhead and memory usage, but care must
237     /// be taken in tuning the batch size. In general, the measurement overhead of NumIterations
238     /// will be larger than that of `NumBatches`. Most benchmarks should use `SmallInput` or
239     /// `LargeInput` instead.
240     NumIterations(u64),
241 
242     #[doc(hidden)]
243     __NonExhaustive,
244 }
245 impl BatchSize {
246     /// Convert to a number of iterations per batch.
247     ///
248     /// We try to do a constant number of batches regardless of the number of iterations in this
249     /// sample. If the measurement overhead is roughly constant regardless of the number of
250     /// iterations the analysis of the results later will have an easier time separating the
251     /// measurement overhead from the benchmark time.
iters_per_batch(self, iters: u64) -> u64252     fn iters_per_batch(self, iters: u64) -> u64 {
253         match self {
254             BatchSize::SmallInput => (iters + 10 - 1) / 10,
255             BatchSize::LargeInput => (iters + 1000 - 1) / 1000,
256             BatchSize::PerIteration => 1,
257             BatchSize::NumBatches(batches) => (iters + batches - 1) / batches,
258             BatchSize::NumIterations(size) => size,
259             BatchSize::__NonExhaustive => panic!("__NonExhaustive is not a valid BatchSize."),
260         }
261     }
262 }
263 
264 /// Timer struct used to iterate a benchmarked function and measure the runtime.
265 ///
266 /// This struct provides different timing loops as methods. Each timing loop provides a different
267 /// way to time a routine and each has advantages and disadvantages.
268 ///
269 /// * If you want to do the iteration and measurement yourself (eg. passing the iteration count
270 ///   to a separate process), use `iter_custom`.
271 /// * If your routine requires no per-iteration setup and returns a value with an expensive `drop`
272 ///   method, use `iter_with_large_drop`.
273 /// * If your routine requires some per-iteration setup that shouldn't be timed, use `iter_batched`
274 ///   or `iter_batched_ref`. See [`BatchSize`](enum.BatchSize.html) for a discussion of batch sizes.
275 ///   If the setup value implements `Drop` and you don't want to include the `drop` time in the
276 ///   measurement, use `iter_batched_ref`, otherwise use `iter_batched`. These methods are also
277 ///   suitable for benchmarking routines which return a value with an expensive `drop` method,
278 ///   but are more complex than `iter_with_large_drop`.
279 /// * Otherwise, use `iter`.
280 pub struct Bencher<'a, M: Measurement = WallTime> {
281     iterated: bool,         // have we iterated this benchmark?
282     iters: u64,             // Number of times to iterate this benchmark
283     value: M::Value,        // The measured value
284     measurement: &'a M,     // Reference to the measurement object
285     elapsed_time: Duration, // How much time did it take to perform the iteration? Used for the warmup period.
286 }
287 impl<'a, M: Measurement> Bencher<'a, M> {
288     /// Times a `routine` by executing it many times and timing the total elapsed time.
289     ///
290     /// Prefer this timing loop when `routine` returns a value that doesn't have a destructor.
291     ///
292     /// # Timing model
293     ///
294     /// Note that the `Bencher` also times the time required to destroy the output of `routine()`.
295     /// Therefore prefer this timing loop when the runtime of `mem::drop(O)` is negligible compared
296     /// to the runtime of the `routine`.
297     ///
298     /// ```text
299     /// elapsed = Instant::now + iters * (routine + mem::drop(O) + Range::next)
300     /// ```
301     ///
302     /// # Example
303     ///
304     /// ```rust
305     /// #[macro_use] extern crate criterion;
306     ///
307     /// use criterion::*;
308     ///
309     /// // The function to benchmark
310     /// fn foo() {
311     ///     // ...
312     /// }
313     ///
314     /// fn bench(c: &mut Criterion) {
315     ///     c.bench_function("iter", move |b| {
316     ///         b.iter(|| foo())
317     ///     });
318     /// }
319     ///
320     /// criterion_group!(benches, bench);
321     /// criterion_main!(benches);
322     /// ```
323     ///
324     #[inline(never)]
iter<O, R>(&mut self, mut routine: R) where R: FnMut() -> O,325     pub fn iter<O, R>(&mut self, mut routine: R)
326     where
327         R: FnMut() -> O,
328     {
329         self.iterated = true;
330         let time_start = Instant::now();
331         let start = self.measurement.start();
332         for _ in 0..self.iters {
333             black_box(routine());
334         }
335         self.value = self.measurement.end(start);
336         self.elapsed_time = time_start.elapsed();
337     }
338 
339     /// Times a `routine` by executing it many times and relying on `routine` to measure its own execution time.
340     ///
341     /// Prefer this timing loop in cases where `routine` has to do its own measurements to
342     /// get accurate timing information (for example in multi-threaded scenarios where you spawn
343     /// and coordinate with multiple threads).
344     ///
345     /// # Timing model
346     /// Custom, the timing model is whatever is returned as the Duration from `routine`.
347     ///
348     /// # Example
349     /// ```rust
350     /// #[macro_use] extern crate criterion;
351     /// use criterion::*;
352     /// use criterion::black_box;
353     /// use std::time::Instant;
354     ///
355     /// fn foo() {
356     ///     // ...
357     /// }
358     ///
359     /// fn bench(c: &mut Criterion) {
360     ///     c.bench_function("iter", move |b| {
361     ///         b.iter_custom(|iters| {
362     ///             let start = Instant::now();
363     ///             for _i in 0..iters {
364     ///                 black_box(foo());
365     ///             }
366     ///             start.elapsed()
367     ///         })
368     ///     });
369     /// }
370     ///
371     /// criterion_group!(benches, bench);
372     /// criterion_main!(benches);
373     /// ```
374     ///
375     #[inline(never)]
iter_custom<R>(&mut self, mut routine: R) where R: FnMut(u64) -> M::Value,376     pub fn iter_custom<R>(&mut self, mut routine: R)
377     where
378         R: FnMut(u64) -> M::Value,
379     {
380         self.iterated = true;
381         let time_start = Instant::now();
382         self.value = routine(self.iters);
383         self.elapsed_time = time_start.elapsed();
384     }
385 
386     #[doc(hidden)]
iter_with_setup<I, O, S, R>(&mut self, setup: S, routine: R) where S: FnMut() -> I, R: FnMut(I) -> O,387     pub fn iter_with_setup<I, O, S, R>(&mut self, setup: S, routine: R)
388     where
389         S: FnMut() -> I,
390         R: FnMut(I) -> O,
391     {
392         self.iter_batched(setup, routine, BatchSize::PerIteration);
393     }
394 
395     /// Times a `routine` by collecting its output on each iteration. This avoids timing the
396     /// destructor of the value returned by `routine`.
397     ///
398     /// WARNING: This requires `O(iters * mem::size_of::<O>())` of memory, and `iters` is not under the
399     /// control of the caller. If this causes out-of-memory errors, use `iter_batched` instead.
400     ///
401     /// # Timing model
402     ///
403     /// ``` text
404     /// elapsed = Instant::now + iters * (routine) + Iterator::collect::<Vec<_>>
405     /// ```
406     ///
407     /// # Example
408     ///
409     /// ```rust
410     /// #[macro_use] extern crate criterion;
411     ///
412     /// use criterion::*;
413     ///
414     /// fn create_vector() -> Vec<u64> {
415     ///     # vec![]
416     ///     // ...
417     /// }
418     ///
419     /// fn bench(c: &mut Criterion) {
420     ///     c.bench_function("with_drop", move |b| {
421     ///         // This will avoid timing the Vec::drop.
422     ///         b.iter_with_large_drop(|| create_vector())
423     ///     });
424     /// }
425     ///
426     /// criterion_group!(benches, bench);
427     /// criterion_main!(benches);
428     /// ```
429     ///
iter_with_large_drop<O, R>(&mut self, mut routine: R) where R: FnMut() -> O,430     pub fn iter_with_large_drop<O, R>(&mut self, mut routine: R)
431     where
432         R: FnMut() -> O,
433     {
434         self.iter_batched(|| (), |_| routine(), BatchSize::SmallInput);
435     }
436 
437     #[doc(hidden)]
iter_with_large_setup<I, O, S, R>(&mut self, setup: S, routine: R) where S: FnMut() -> I, R: FnMut(I) -> O,438     pub fn iter_with_large_setup<I, O, S, R>(&mut self, setup: S, routine: R)
439     where
440         S: FnMut() -> I,
441         R: FnMut(I) -> O,
442     {
443         self.iter_batched(setup, routine, BatchSize::NumBatches(1));
444     }
445 
446     /// Times a `routine` that requires some input by generating a batch of input, then timing the
447     /// iteration of the benchmark over the input. See [`BatchSize`](enum.BatchSize.html) for
448     /// details on choosing the batch size. Use this when the routine must consume its input.
449     ///
450     /// For example, use this loop to benchmark sorting algorithms, because they require unsorted
451     /// data on each iteration.
452     ///
453     /// # Timing model
454     ///
455     /// ```text
456     /// elapsed = (Instant::now * num_batches) + (iters * (routine + O::drop)) + Vec::extend
457     /// ```
458     ///
459     /// # Example
460     ///
461     /// ```rust
462     /// #[macro_use] extern crate criterion;
463     ///
464     /// use criterion::*;
465     ///
466     /// fn create_scrambled_data() -> Vec<u64> {
467     ///     # vec![]
468     ///     // ...
469     /// }
470     ///
471     /// // The sorting algorithm to test
472     /// fn sort(data: &mut [u64]) {
473     ///     // ...
474     /// }
475     ///
476     /// fn bench(c: &mut Criterion) {
477     ///     let data = create_scrambled_data();
478     ///
479     ///     c.bench_function("with_setup", move |b| {
480     ///         // This will avoid timing the to_vec call.
481     ///         b.iter_batched(|| data.clone(), |mut data| sort(&mut data), BatchSize::SmallInput)
482     ///     });
483     /// }
484     ///
485     /// criterion_group!(benches, bench);
486     /// criterion_main!(benches);
487     /// ```
488     ///
489     #[inline(never)]
iter_batched<I, O, S, R>(&mut self, mut setup: S, mut routine: R, size: BatchSize) where S: FnMut() -> I, R: FnMut(I) -> O,490     pub fn iter_batched<I, O, S, R>(&mut self, mut setup: S, mut routine: R, size: BatchSize)
491     where
492         S: FnMut() -> I,
493         R: FnMut(I) -> O,
494     {
495         self.iterated = true;
496         let batch_size = size.iters_per_batch(self.iters);
497         assert!(batch_size != 0, "Batch size must not be zero.");
498         let time_start = Instant::now();
499         self.value = self.measurement.zero();
500 
501         if batch_size == 1 {
502             for _ in 0..self.iters {
503                 let input = black_box(setup());
504 
505                 let start = self.measurement.start();
506                 let output = routine(input);
507                 let end = self.measurement.end(start);
508                 self.value = self.measurement.add(&self.value, &end);
509 
510                 drop(black_box(output));
511             }
512         } else {
513             let mut iteration_counter = 0;
514 
515             while iteration_counter < self.iters {
516                 let batch_size = ::std::cmp::min(batch_size, self.iters - iteration_counter);
517 
518                 let inputs = black_box((0..batch_size).map(|_| setup()).collect::<Vec<_>>());
519                 let mut outputs = Vec::with_capacity(batch_size as usize);
520 
521                 let start = self.measurement.start();
522                 outputs.extend(inputs.into_iter().map(&mut routine));
523                 let end = self.measurement.end(start);
524                 self.value = self.measurement.add(&self.value, &end);
525 
526                 black_box(outputs);
527 
528                 iteration_counter += batch_size;
529             }
530         }
531 
532         self.elapsed_time = time_start.elapsed();
533     }
534 
535     /// Times a `routine` that requires some input by generating a batch of input, then timing the
536     /// iteration of the benchmark over the input. See [`BatchSize`](enum.BatchSize.html) for
537     /// details on choosing the batch size. Use this when the routine should accept the input by
538     /// mutable reference.
539     ///
540     /// For example, use this loop to benchmark sorting algorithms, because they require unsorted
541     /// data on each iteration.
542     ///
543     /// # Timing model
544     ///
545     /// ```text
546     /// elapsed = (Instant::now * num_batches) + (iters * routine) + Vec::extend
547     /// ```
548     ///
549     /// # Example
550     ///
551     /// ```rust
552     /// #[macro_use] extern crate criterion;
553     ///
554     /// use criterion::*;
555     ///
556     /// fn create_scrambled_data() -> Vec<u64> {
557     ///     # vec![]
558     ///     // ...
559     /// }
560     ///
561     /// // The sorting algorithm to test
562     /// fn sort(data: &mut [u64]) {
563     ///     // ...
564     /// }
565     ///
566     /// fn bench(c: &mut Criterion) {
567     ///     let data = create_scrambled_data();
568     ///
569     ///     c.bench_function("with_setup", move |b| {
570     ///         // This will avoid timing the to_vec call.
571     ///         b.iter_batched(|| data.clone(), |mut data| sort(&mut data), BatchSize::SmallInput)
572     ///     });
573     /// }
574     ///
575     /// criterion_group!(benches, bench);
576     /// criterion_main!(benches);
577     /// ```
578     ///
579     #[inline(never)]
iter_batched_ref<I, O, S, R>(&mut self, mut setup: S, mut routine: R, size: BatchSize) where S: FnMut() -> I, R: FnMut(&mut I) -> O,580     pub fn iter_batched_ref<I, O, S, R>(&mut self, mut setup: S, mut routine: R, size: BatchSize)
581     where
582         S: FnMut() -> I,
583         R: FnMut(&mut I) -> O,
584     {
585         self.iterated = true;
586         let batch_size = size.iters_per_batch(self.iters);
587         assert!(batch_size != 0, "Batch size must not be zero.");
588         let time_start = Instant::now();
589         self.value = self.measurement.zero();
590 
591         if batch_size == 1 {
592             for _ in 0..self.iters {
593                 let mut input = black_box(setup());
594 
595                 let start = self.measurement.start();
596                 let output = routine(&mut input);
597                 let end = self.measurement.end(start);
598                 self.value = self.measurement.add(&self.value, &end);
599 
600                 drop(black_box(output));
601                 drop(black_box(input));
602             }
603         } else {
604             let mut iteration_counter = 0;
605 
606             while iteration_counter < self.iters {
607                 let batch_size = ::std::cmp::min(batch_size, self.iters - iteration_counter);
608 
609                 let mut inputs = black_box((0..batch_size).map(|_| setup()).collect::<Vec<_>>());
610                 let mut outputs = Vec::with_capacity(batch_size as usize);
611 
612                 let start = self.measurement.start();
613                 outputs.extend(inputs.iter_mut().map(&mut routine));
614                 let end = self.measurement.end(start);
615                 self.value = self.measurement.add(&self.value, &end);
616 
617                 black_box(outputs);
618 
619                 iteration_counter += batch_size;
620             }
621         }
622         self.elapsed_time = time_start.elapsed();
623     }
624 
625     // Benchmarks must actually call one of the iter methods. This causes benchmarks to fail loudly
626     // if they don't.
assert_iterated(&mut self)627     fn assert_iterated(&mut self) {
628         if !self.iterated {
629             panic!("Benchmark function must call Bencher::iter or related method.");
630         }
631         self.iterated = false;
632     }
633 }
634 
635 /// Baseline describes how the baseline_directory is handled.
636 #[derive(Debug, Clone, Copy)]
637 pub enum Baseline {
638     /// Compare ensures a previous saved version of the baseline
639     /// exists and runs comparison against that.
640     Compare,
641     /// Save writes the benchmark results to the baseline directory,
642     /// overwriting any results that were previously there.
643     Save,
644 }
645 
646 /// Enum used to select the plotting backend.
647 #[derive(Debug, Clone, Copy)]
648 pub enum PlottingBackend {
649     /// Plotting backend which uses the external `gnuplot` command to render plots. This is the
650     /// default if the `gnuplot` command is installed.
651     Gnuplot,
652     /// Plotting backend which uses the rust 'Plotters' library. This is the default if `gnuplot`
653     /// is not installed.
654     Plotters,
655 }
656 
657 /// The benchmark manager
658 ///
659 /// `Criterion` lets you configure and execute benchmarks
660 ///
661 /// Each benchmark consists of four phases:
662 ///
663 /// - **Warm-up**: The routine is repeatedly executed, to let the CPU/OS/JIT/interpreter adapt to
664 /// the new load
665 /// - **Measurement**: The routine is repeatedly executed, and timing information is collected into
666 /// a sample
667 /// - **Analysis**: The sample is analyzed and distiled into meaningful statistics that get
668 /// reported to stdout, stored in files, and plotted
669 /// - **Comparison**: The current sample is compared with the sample obtained in the previous
670 /// benchmark.
671 pub struct Criterion<M: Measurement = WallTime> {
672     config: BenchmarkConfig,
673     plotting_backend: PlottingBackend,
674     plotting_enabled: bool,
675     filter: Option<Regex>,
676     report: Box<dyn Report>,
677     output_directory: PathBuf,
678     baseline_directory: String,
679     baseline: Baseline,
680     profile_time: Option<Duration>,
681     load_baseline: Option<String>,
682     test_mode: bool,
683     list_mode: bool,
684     all_directories: HashSet<String>,
685     all_titles: HashSet<String>,
686     measurement: M,
687     profiler: Box<RefCell<dyn Profiler>>,
688 }
689 
690 impl Default for Criterion {
691     /// Creates a benchmark manager with the following default settings:
692     ///
693     /// - Sample size: 100 measurements
694     /// - Warm-up time: 3 s
695     /// - Measurement time: 5 s
696     /// - Bootstrap size: 100 000 resamples
697     /// - Noise threshold: 0.01 (1%)
698     /// - Confidence level: 0.95
699     /// - Significance level: 0.05
700     /// - Plotting: enabled, using gnuplot if available or plotters if gnuplot is not available
701     /// - No filter
default() -> Criterion702     fn default() -> Criterion {
703         let mut reports: Vec<Box<dyn Report>> = vec![];
704         reports.push(Box::new(CliReport::new(false, false, false)));
705         reports.push(Box::new(FileCsvReport));
706 
707         let output_directory = match std::env::var_os("CARGO_TARGET_DIR") {
708             Some(value) => {
709                 let mut target_dir = PathBuf::from(value);
710                 target_dir.push("criterion");
711                 target_dir
712             }
713             None => "target/criterion".into(),
714         };
715 
716         Criterion {
717             config: BenchmarkConfig {
718                 confidence_level: 0.95,
719                 measurement_time: Duration::new(5, 0),
720                 noise_threshold: 0.01,
721                 nresamples: 100_000,
722                 sample_size: 100,
723                 significance_level: 0.05,
724                 warm_up_time: Duration::new(3, 0),
725             },
726             plotting_backend: *DEFAULT_PLOTTING_BACKEND,
727             plotting_enabled: true,
728             filter: None,
729             report: Box::new(Reports::new(reports)),
730             baseline_directory: "base".to_owned(),
731             baseline: Baseline::Save,
732             profile_time: None,
733             load_baseline: None,
734             test_mode: false,
735             list_mode: false,
736             output_directory,
737             all_directories: HashSet::new(),
738             all_titles: HashSet::new(),
739             measurement: WallTime,
740             profiler: Box::new(RefCell::new(ExternalProfiler)),
741         }
742     }
743 }
744 
745 impl<M: Measurement> Criterion<M> {
746     /// Changes the measurement for the benchmarks run with this runner. See the
747     /// Measurement trait for more details
with_measurement<M2: Measurement>(self, m: M2) -> Criterion<M2>748     pub fn with_measurement<M2: Measurement>(self, m: M2) -> Criterion<M2> {
749         Criterion {
750             config: self.config,
751             plotting_backend: self.plotting_backend,
752             plotting_enabled: self.plotting_enabled,
753             filter: self.filter,
754             report: self.report,
755             baseline_directory: self.baseline_directory,
756             baseline: self.baseline,
757             profile_time: self.profile_time,
758             load_baseline: self.load_baseline,
759             test_mode: self.test_mode,
760             list_mode: self.list_mode,
761             output_directory: self.output_directory,
762             all_directories: self.all_directories,
763             all_titles: self.all_titles,
764             measurement: m,
765             profiler: self.profiler,
766         }
767     }
768 
769     /// Changes the internal profiler for benchmarks run with this runner. See
770     /// the Profiler trait for more details.
with_profiler<P: Profiler + 'static>(self, p: P) -> Criterion<M>771     pub fn with_profiler<P: Profiler + 'static>(self, p: P) -> Criterion<M> {
772         Criterion {
773             profiler: Box::new(RefCell::new(p)),
774             ..self
775         }
776     }
777 
778     /// Set the plotting backend. By default, Criterion will use gnuplot if available, or plotters
779     /// if not.
780     ///
781     /// Panics if `backend` is `PlottingBackend::Gnuplot` and gnuplot is not available.
plotting_backend(self, backend: PlottingBackend) -> Criterion<M>782     pub fn plotting_backend(self, backend: PlottingBackend) -> Criterion<M> {
783         if let PlottingBackend::Gnuplot = backend {
784             if GNUPLOT_VERSION.is_err() {
785                 panic!("Gnuplot plotting backend was requested, but gnuplot is not available. To continue, either install Gnuplot or allow Criterion.rs to fall back to using plotters.");
786             }
787         }
788 
789         Criterion {
790             plotting_backend: backend,
791             ..self
792         }
793     }
794 
795     /// Changes the default size of the sample for benchmarks run with this runner.
796     ///
797     /// A bigger sample should yield more accurate results if paired with a sufficiently large
798     /// measurement time.
799     ///
800     /// Sample size must be at least 10.
801     ///
802     /// # Panics
803     ///
804     /// Panics if n < 10
sample_size(mut self, n: usize) -> Criterion<M>805     pub fn sample_size(mut self, n: usize) -> Criterion<M> {
806         assert!(n >= 10);
807 
808         self.config.sample_size = n;
809         self
810     }
811 
812     /// Changes the default warm up time for benchmarks run with this runner.
813     ///
814     /// # Panics
815     ///
816     /// Panics if the input duration is zero
warm_up_time(mut self, dur: Duration) -> Criterion<M>817     pub fn warm_up_time(mut self, dur: Duration) -> Criterion<M> {
818         assert!(dur.to_nanos() > 0);
819 
820         self.config.warm_up_time = dur;
821         self
822     }
823 
824     /// Changes the default measurement time for benchmarks run with this runner.
825     ///
826     /// With a longer time, the measurement will become more resilient to transitory peak loads
827     /// caused by external programs
828     ///
829     /// **Note**: If the measurement time is too "low", Criterion will automatically increase it
830     ///
831     /// # Panics
832     ///
833     /// Panics if the input duration in zero
measurement_time(mut self, dur: Duration) -> Criterion<M>834     pub fn measurement_time(mut self, dur: Duration) -> Criterion<M> {
835         assert!(dur.to_nanos() > 0);
836 
837         self.config.measurement_time = dur;
838         self
839     }
840 
841     /// Changes the default number of resamples for benchmarks run with this runner.
842     ///
843     /// Number of resamples to use for the
844     /// [bootstrap](http://en.wikipedia.org/wiki/Bootstrapping_(statistics)#Case_resampling)
845     ///
846     /// A larger number of resamples reduces the random sampling errors, which are inherent to the
847     /// bootstrap method, but also increases the analysis time
848     ///
849     /// # Panics
850     ///
851     /// Panics if the number of resamples is set to zero
nresamples(mut self, n: usize) -> Criterion<M>852     pub fn nresamples(mut self, n: usize) -> Criterion<M> {
853         assert!(n > 0);
854         if n <= 1000 {
855             println!("\nWarning: It is not recommended to reduce nresamples below 1000.");
856         }
857 
858         self.config.nresamples = n;
859         self
860     }
861 
862     /// Changes the default noise threshold for benchmarks run with this runner. The noise threshold
863     /// is used to filter out small changes in performance, even if they are statistically
864     /// significant. Sometimes benchmarking the same code twice will result in small but
865     /// statistically significant differences solely because of noise. This provides a way to filter
866     /// out some of these false positives at the cost of making it harder to detect small changes
867     /// to the true performance of the benchmark.
868     ///
869     /// The default is 0.01, meaning that changes smaller than 1% will be ignored.
870     ///
871     /// # Panics
872     ///
873     /// Panics if the threshold is set to a negative value
noise_threshold(mut self, threshold: f64) -> Criterion<M>874     pub fn noise_threshold(mut self, threshold: f64) -> Criterion<M> {
875         assert!(threshold >= 0.0);
876 
877         self.config.noise_threshold = threshold;
878         self
879     }
880 
881     /// Changes the default confidence level for benchmarks run with this runner. The confidence
882     /// level is the desired probability that the true runtime lies within the estimated
883     /// [confidence interval](https://en.wikipedia.org/wiki/Confidence_interval). The default is
884     /// 0.95, meaning that the confidence interval should capture the true value 95% of the time.
885     ///
886     /// # Panics
887     ///
888     /// Panics if the confidence level is set to a value outside the `(0, 1)` range
confidence_level(mut self, cl: f64) -> Criterion<M>889     pub fn confidence_level(mut self, cl: f64) -> Criterion<M> {
890         assert!(cl > 0.0 && cl < 1.0);
891         if cl < 0.5 {
892             println!("\nWarning: It is not recommended to reduce confidence level below 0.5.");
893         }
894 
895         self.config.confidence_level = cl;
896         self
897     }
898 
899     /// Changes the default [significance level](https://en.wikipedia.org/wiki/Statistical_significance)
900     /// for benchmarks run with this runner. This is used to perform a
901     /// [hypothesis test](https://en.wikipedia.org/wiki/Statistical_hypothesis_testing) to see if
902     /// the measurements from this run are different from the measured performance of the last run.
903     /// The significance level is the desired probability that two measurements of identical code
904     /// will be considered 'different' due to noise in the measurements. The default value is 0.05,
905     /// meaning that approximately 5% of identical benchmarks will register as different due to
906     /// noise.
907     ///
908     /// This presents a trade-off. By setting the significance level closer to 0.0, you can increase
909     /// the statistical robustness against noise, but it also weaken's Criterion.rs' ability to
910     /// detect small but real changes in the performance. By setting the significance level
911     /// closer to 1.0, Criterion.rs will be more able to detect small true changes, but will also
912     /// report more spurious differences.
913     ///
914     /// See also the noise threshold setting.
915     ///
916     /// # Panics
917     ///
918     /// Panics if the significance level is set to a value outside the `(0, 1)` range
significance_level(mut self, sl: f64) -> Criterion<M>919     pub fn significance_level(mut self, sl: f64) -> Criterion<M> {
920         assert!(sl > 0.0 && sl < 1.0);
921 
922         self.config.significance_level = sl;
923         self
924     }
925 
create_plotter(&self) -> Box<dyn Plotter>926     fn create_plotter(&self) -> Box<dyn Plotter> {
927         match self.plotting_backend {
928             PlottingBackend::Gnuplot => Box::new(Gnuplot::default()),
929             PlottingBackend::Plotters => Box::new(PlottersBackend::default()),
930         }
931     }
932 
933     /// Enables plotting
with_plots(mut self) -> Criterion<M>934     pub fn with_plots(mut self) -> Criterion<M> {
935         self.plotting_enabled = true;
936         let mut reports: Vec<Box<dyn Report>> = vec![];
937         reports.push(Box::new(CliReport::new(false, false, false)));
938         reports.push(Box::new(FileCsvReport));
939         reports.push(Box::new(Html::new(self.create_plotter())));
940         self.report = Box::new(Reports::new(reports));
941 
942         self
943     }
944 
945     /// Disables plotting
without_plots(mut self) -> Criterion<M>946     pub fn without_plots(mut self) -> Criterion<M> {
947         self.plotting_enabled = false;
948         let mut reports: Vec<Box<dyn Report>> = vec![];
949         reports.push(Box::new(CliReport::new(false, false, false)));
950         reports.push(Box::new(FileCsvReport));
951         self.report = Box::new(Reports::new(reports));
952         self
953     }
954 
955     /// Return true if generation of the plots is possible.
can_plot(&self) -> bool956     pub fn can_plot(&self) -> bool {
957         // Trivially true now that we have plotters.
958         // TODO: Deprecate and remove this.
959         true
960     }
961 
962     /// Names an explicit baseline and enables overwriting the previous results.
save_baseline(mut self, baseline: String) -> Criterion<M>963     pub fn save_baseline(mut self, baseline: String) -> Criterion<M> {
964         self.baseline_directory = baseline;
965         self.baseline = Baseline::Save;
966         self
967     }
968 
969     /// Names an explicit baseline and disables overwriting the previous results.
retain_baseline(mut self, baseline: String) -> Criterion<M>970     pub fn retain_baseline(mut self, baseline: String) -> Criterion<M> {
971         self.baseline_directory = baseline;
972         self.baseline = Baseline::Compare;
973         self
974     }
975 
976     /// Filters the benchmarks. Only benchmarks with names that contain the
977     /// given string will be executed.
with_filter<S: Into<String>>(mut self, filter: S) -> Criterion<M>978     pub fn with_filter<S: Into<String>>(mut self, filter: S) -> Criterion<M> {
979         let filter_text = filter.into();
980         let filter = Regex::new(&filter_text).unwrap_or_else(|err| {
981             panic!(
982                 "Unable to parse '{}' as a regular expression: {}",
983                 filter_text, err
984             )
985         });
986         self.filter = Some(filter);
987 
988         self
989     }
990 
991     /// Set the output directory (currently for testing only)
992     #[doc(hidden)]
output_directory(mut self, path: &Path) -> Criterion<M>993     pub fn output_directory(mut self, path: &Path) -> Criterion<M> {
994         self.output_directory = path.to_owned();
995 
996         self
997     }
998 
999     /// Set the profile time (currently for testing only)
1000     #[doc(hidden)]
profile_time(mut self, profile_time: Option<Duration>) -> Criterion<M>1001     pub fn profile_time(mut self, profile_time: Option<Duration>) -> Criterion<M> {
1002         self.profile_time = profile_time;
1003 
1004         self
1005     }
1006 
1007     /// Generate the final summary at the end of a run.
1008     #[doc(hidden)]
final_summary(&self)1009     pub fn final_summary(&self) {
1010         if self.profile_time.is_some() || self.test_mode {
1011             return;
1012         }
1013 
1014         let report_context = ReportContext {
1015             output_directory: self.output_directory.clone(),
1016             plot_config: PlotConfiguration::default(),
1017             test_mode: self.test_mode,
1018         };
1019 
1020         self.report.final_summary(&report_context);
1021     }
1022 
1023     /// Configure this criterion struct based on the command-line arguments to
1024     /// this process.
1025     #[cfg_attr(feature = "cargo-clippy", allow(clippy::cognitive_complexity))]
configure_from_args(mut self) -> Criterion<M>1026     pub fn configure_from_args(mut self) -> Criterion<M> {
1027         use clap::{App, Arg};
1028         let matches = App::new("Criterion Benchmark")
1029             .arg(Arg::with_name("FILTER")
1030                 .help("Skip benchmarks whose names do not contain FILTER.")
1031                 .index(1))
1032             .arg(Arg::with_name("color")
1033                 .short("c")
1034                 .long("color")
1035                 .alias("colour")
1036                 .takes_value(true)
1037                 .possible_values(&["auto", "always", "never"])
1038                 .default_value("auto")
1039                 .help("Configure coloring of output. always = always colorize output, never = never colorize output, auto = colorize output if output is a tty and compiled for unix."))
1040             .arg(Arg::with_name("verbose")
1041                 .short("v")
1042                 .long("verbose")
1043                 .help("Print additional statistical information."))
1044             .arg(Arg::with_name("noplot")
1045                 .short("n")
1046                 .long("noplot")
1047                 .help("Disable plot and HTML generation."))
1048             .arg(Arg::with_name("save-baseline")
1049                 .short("s")
1050                 .long("save-baseline")
1051                 .default_value("base")
1052                 .help("Save results under a named baseline."))
1053             .arg(Arg::with_name("baseline")
1054                 .short("b")
1055                 .long("baseline")
1056                 .takes_value(true)
1057                 .conflicts_with("save-baseline")
1058                 .help("Compare to a named baseline."))
1059             .arg(Arg::with_name("list")
1060                 .long("list")
1061                 .help("List all benchmarks"))
1062             .arg(Arg::with_name("profile-time")
1063                 .long("profile-time")
1064                 .takes_value(true)
1065                 .help("Iterate each benchmark for approximately the given number of seconds, doing no analysis and without storing the results. Useful for running the benchmarks in a profiler."))
1066             .arg(Arg::with_name("load-baseline")
1067                  .long("load-baseline")
1068                  .takes_value(true)
1069                  .conflicts_with("profile-time")
1070                  .requires("baseline")
1071                  .help("Load a previous baseline instead of sampling new data."))
1072             .arg(Arg::with_name("sample-size")
1073                 .long("sample-size")
1074                 .takes_value(true)
1075                 .help(&format!("Changes the default size of the sample for this run. [default: {}]", self.config.sample_size)))
1076             .arg(Arg::with_name("warm-up-time")
1077                 .long("warm-up-time")
1078                 .takes_value(true)
1079                 .help(&format!("Changes the default warm up time for this run. [default: {}]", self.config.warm_up_time.as_secs())))
1080             .arg(Arg::with_name("measurement-time")
1081                 .long("measurement-time")
1082                 .takes_value(true)
1083                 .help(&format!("Changes the default measurement time for this run. [default: {}]", self.config.measurement_time.as_secs())))
1084             .arg(Arg::with_name("nresamples")
1085                 .long("nresamples")
1086                 .takes_value(true)
1087                 .help(&format!("Changes the default number of resamples for this run. [default: {}]", self.config.nresamples)))
1088             .arg(Arg::with_name("noise-threshold")
1089                 .long("noise-threshold")
1090                 .takes_value(true)
1091                 .help(&format!("Changes the default noise threshold for this run. [default: {}]", self.config.noise_threshold)))
1092             .arg(Arg::with_name("confidence-level")
1093                 .long("confidence-level")
1094                 .takes_value(true)
1095                 .help(&format!("Changes the default confidence level for this run. [default: {}]", self.config.confidence_level)))
1096             .arg(Arg::with_name("significance-level")
1097                 .long("significance-level")
1098                 .takes_value(true)
1099                 .help(&format!("Changes the default significance level for this run. [default: {}]", self.config.significance_level)))
1100             .arg(Arg::with_name("test")
1101                 .hidden(true)
1102                 .long("test")
1103                 .help("Run the benchmarks once, to verify that they execute successfully, but do not measure or report the results."))
1104             .arg(Arg::with_name("bench")
1105                 .hidden(true)
1106                 .long("bench"))
1107             .arg(Arg::with_name("plotting-backend")
1108                  .long("plotting-backend")
1109                  .takes_value(true)
1110                  .possible_values(&["gnuplot", "plotters"])
1111                  .help("Set the plotting backend. By default, Criterion.rs will use the gnuplot backend if gnuplot is available, or the plotters backend if it isn't."))
1112             .arg(Arg::with_name("output-format")
1113                 .long("output-format")
1114                 .takes_value(true)
1115                 .possible_values(&["criterion", "bencher"])
1116                 .default_value("criterion")
1117                 .help("Change the CLI output format. By default, Criterion.rs will use its own format. If output format is set to 'bencher', Criterion.rs will print output in a format that resembles the 'bencher' crate."))
1118             .arg(Arg::with_name("nocapture")
1119                 .long("nocapture")
1120                 .hidden(true)
1121                 .help("Ignored, but added for compatibility with libtest."))
1122             .arg(Arg::with_name("version")
1123                 .hidden(true)
1124                 .short("V")
1125                 .long("version"))
1126             .after_help("
1127 This executable is a Criterion.rs benchmark.
1128 See https://github.com/bheisler/criterion.rs for more details.
1129 
1130 To enable debug output, define the environment variable CRITERION_DEBUG.
1131 Criterion.rs will output more debug information and will save the gnuplot
1132 scripts alongside the generated plots.
1133 
1134 To test that the benchmarks work, run `cargo test --benches`
1135 ")
1136             .get_matches();
1137 
1138         if let Some(filter) = matches.value_of("FILTER") {
1139             self = self.with_filter(filter);
1140         }
1141 
1142         match matches.value_of("plotting-backend") {
1143             // Use plotting_backend() here to re-use the panic behavior if Gnuplot is not available.
1144             Some("gnuplot") => self = self.plotting_backend(PlottingBackend::Gnuplot),
1145             Some("plotters") => self = self.plotting_backend(PlottingBackend::Plotters),
1146             Some(val) => panic!("Unexpected plotting backend '{}'", val),
1147             None => {}
1148         }
1149 
1150         if matches.is_present("noplot") || matches.is_present("test") {
1151             self = self.without_plots();
1152         } else {
1153             self = self.with_plots();
1154         }
1155 
1156         if let Some(dir) = matches.value_of("save-baseline") {
1157             self.baseline = Baseline::Save;
1158             self.baseline_directory = dir.to_owned()
1159         }
1160         if let Some(dir) = matches.value_of("baseline") {
1161             self.baseline = Baseline::Compare;
1162             self.baseline_directory = dir.to_owned();
1163         }
1164 
1165         let mut reports: Vec<Box<dyn Report>> = vec![];
1166 
1167         let cli_report: Box<dyn Report> = match matches.value_of("output-format") {
1168             Some("bencher") => Box::new(BencherReport),
1169             _ => {
1170                 let verbose = matches.is_present("verbose");
1171                 let stdout_isatty = atty::is(atty::Stream::Stdout);
1172                 let mut enable_text_overwrite = stdout_isatty && !verbose && !debug_enabled();
1173                 let enable_text_coloring;
1174                 match matches.value_of("color") {
1175                     Some("always") => {
1176                         enable_text_coloring = true;
1177                     }
1178                     Some("never") => {
1179                         enable_text_coloring = false;
1180                         enable_text_overwrite = false;
1181                     }
1182                     _ => enable_text_coloring = stdout_isatty,
1183                 };
1184                 Box::new(CliReport::new(
1185                     enable_text_overwrite,
1186                     enable_text_coloring,
1187                     verbose,
1188                 ))
1189             }
1190         };
1191 
1192         reports.push(cli_report);
1193         reports.push(Box::new(FileCsvReport));
1194 
1195         if matches.is_present("profile-time") {
1196             let num_seconds = value_t!(matches.value_of("profile-time"), u64).unwrap_or_else(|e| {
1197                 println!("{}", e);
1198                 std::process::exit(1)
1199             });
1200 
1201             if num_seconds < 1 {
1202                 println!("Profile time must be at least one second.");
1203                 std::process::exit(1);
1204             }
1205 
1206             self.profile_time = Some(Duration::from_secs(num_seconds));
1207         }
1208 
1209         if let Some(dir) = matches.value_of("load-baseline") {
1210             self.load_baseline = Some(dir.to_owned());
1211         }
1212 
1213         let bench = matches.is_present("bench");
1214         let test = matches.is_present("test");
1215         self.test_mode = match (bench, test) {
1216             (true, true) => true,   // cargo bench -- --test should run tests
1217             (true, false) => false, // cargo bench should run benchmarks
1218             (false, _) => true,     // cargo test --benches should run tests
1219         };
1220 
1221         if matches.is_present("sample-size") {
1222             let num_size = value_t!(matches.value_of("sample-size"), usize).unwrap_or_else(|e| {
1223                 println!("{}", e);
1224                 std::process::exit(1)
1225             });
1226 
1227             assert!(num_size >= 10);
1228             self.config.sample_size = num_size;
1229         }
1230         if matches.is_present("warm-up-time") {
1231             let num_seconds = value_t!(matches.value_of("warm-up-time"), u64).unwrap_or_else(|e| {
1232                 println!("{}", e);
1233                 std::process::exit(1)
1234             });
1235 
1236             let dur = std::time::Duration::new(num_seconds, 0);
1237             assert!(dur.to_nanos() > 0);
1238 
1239             self.config.warm_up_time = dur;
1240         }
1241         if matches.is_present("measurement-time") {
1242             let num_seconds =
1243                 value_t!(matches.value_of("measurement-time"), u64).unwrap_or_else(|e| {
1244                     println!("{}", e);
1245                     std::process::exit(1)
1246                 });
1247 
1248             let dur = std::time::Duration::new(num_seconds, 0);
1249             assert!(dur.to_nanos() > 0);
1250 
1251             self.config.measurement_time = dur;
1252         }
1253         if matches.is_present("nresamples") {
1254             let num_resamples =
1255                 value_t!(matches.value_of("nresamples"), usize).unwrap_or_else(|e| {
1256                     println!("{}", e);
1257                     std::process::exit(1)
1258                 });
1259 
1260             assert!(num_resamples > 0);
1261 
1262             self.config.nresamples = num_resamples;
1263         }
1264         if matches.is_present("noise-threshold") {
1265             let num_noise_threshold = value_t!(matches.value_of("noise-threshold"), f64)
1266                 .unwrap_or_else(|e| {
1267                     println!("{}", e);
1268                     std::process::exit(1)
1269                 });
1270 
1271             assert!(num_noise_threshold > 0.0);
1272 
1273             self.config.noise_threshold = num_noise_threshold;
1274         }
1275         if matches.is_present("confidence-level") {
1276             let num_confidence_level = value_t!(matches.value_of("confidence-level"), f64)
1277                 .unwrap_or_else(|e| {
1278                     println!("{}", e);
1279                     std::process::exit(1)
1280                 });
1281 
1282             assert!(num_confidence_level > 0.0 && num_confidence_level < 1.0);
1283 
1284             self.config.confidence_level = num_confidence_level;
1285         }
1286         if matches.is_present("significance-level") {
1287             let num_significance_level = value_t!(matches.value_of("significance-level"), f64)
1288                 .unwrap_or_else(|e| {
1289                     println!("{}", e);
1290                     std::process::exit(1)
1291                 });
1292 
1293             assert!(num_significance_level > 0.0 && num_significance_level < 1.0);
1294 
1295             self.config.significance_level = num_significance_level;
1296         }
1297 
1298         if matches.is_present("list") {
1299             self.test_mode = true;
1300             self.list_mode = true;
1301         }
1302 
1303         if self.profile_time.is_none() && self.plotting_enabled {
1304             reports.push(Box::new(Html::new(self.create_plotter())));
1305         }
1306 
1307         self.report = Box::new(Reports::new(reports));
1308 
1309         self
1310     }
1311 
filter_matches(&self, id: &str) -> bool1312     fn filter_matches(&self, id: &str) -> bool {
1313         match self.filter {
1314             Some(ref regex) => regex.is_match(id),
1315             None => true,
1316         }
1317     }
1318 
1319     /// Return a benchmark group. All benchmarks performed using a benchmark group will be
1320     /// grouped together in the final report.
1321     ///
1322     /// # Examples:
1323     ///
1324     /// ```rust
1325     /// #[macro_use] extern crate criterion;
1326     /// use self::criterion::*;
1327     ///
1328     /// fn bench_simple(c: &mut Criterion) {
1329     ///     let mut group = c.benchmark_group("My Group");
1330     ///
1331     ///     // Now we can perform benchmarks with this group
1332     ///     group.bench_function("Bench 1", |b| b.iter(|| 1 ));
1333     ///     group.bench_function("Bench 2", |b| b.iter(|| 2 ));
1334     ///
1335     ///     group.finish();
1336     /// }
1337     /// criterion_group!(benches, bench_simple);
1338     /// criterion_main!(benches);
1339     /// ```
benchmark_group<S: Into<String>>(&mut self, group_name: S) -> BenchmarkGroup<'_, M>1340     pub fn benchmark_group<S: Into<String>>(&mut self, group_name: S) -> BenchmarkGroup<'_, M> {
1341         BenchmarkGroup::new(self, group_name.into())
1342     }
1343 }
1344 impl<M> Criterion<M>
1345 where
1346     M: Measurement + 'static,
1347 {
1348     /// Benchmarks a function. For comparing multiple functions, see `benchmark_group`.
1349     ///
1350     /// # Example
1351     ///
1352     /// ```rust
1353     /// #[macro_use] extern crate criterion;
1354     /// use self::criterion::*;
1355     ///
1356     /// fn bench(c: &mut Criterion) {
1357     ///     // Setup (construct data, allocate memory, etc)
1358     ///     c.bench_function(
1359     ///         "function_name",
1360     ///         |b| b.iter(|| {
1361     ///             // Code to benchmark goes here
1362     ///         }),
1363     ///     );
1364     /// }
1365     ///
1366     /// criterion_group!(benches, bench);
1367     /// criterion_main!(benches);
1368     /// ```
bench_function<F>(&mut self, id: &str, f: F) -> &mut Criterion<M> where F: FnMut(&mut Bencher<'_, M>),1369     pub fn bench_function<F>(&mut self, id: &str, f: F) -> &mut Criterion<M>
1370     where
1371         F: FnMut(&mut Bencher<'_, M>),
1372     {
1373         self.benchmark_group(id)
1374             .bench_function(BenchmarkId::no_function(), f);
1375         self
1376     }
1377 
1378     /// Benchmarks a function with an input. For comparing multiple functions or multiple inputs,
1379     /// see `benchmark_group`.
1380     ///
1381     /// # Example
1382     ///
1383     /// ```rust
1384     /// #[macro_use] extern crate criterion;
1385     /// use self::criterion::*;
1386     ///
1387     /// fn bench(c: &mut Criterion) {
1388     ///     // Setup (construct data, allocate memory, etc)
1389     ///     let input = 5u64;
1390     ///     c.bench_with_input(
1391     ///         BenchmarkId::new("function_name", input), &input,
1392     ///         |b, i| b.iter(|| {
1393     ///             // Code to benchmark using input `i` goes here
1394     ///         }),
1395     ///     );
1396     /// }
1397     ///
1398     /// criterion_group!(benches, bench);
1399     /// criterion_main!(benches);
1400     /// ```
bench_with_input<F, I>(&mut self, id: BenchmarkId, input: &I, f: F) -> &mut Criterion<M> where F: FnMut(&mut Bencher<'_, M>, &I),1401     pub fn bench_with_input<F, I>(&mut self, id: BenchmarkId, input: &I, f: F) -> &mut Criterion<M>
1402     where
1403         F: FnMut(&mut Bencher<'_, M>, &I),
1404     {
1405         // Guaranteed safe because external callers can't create benchmark IDs without a function
1406         // name or parameter
1407         let group_name = id.function_name.unwrap();
1408         let parameter = id.parameter.unwrap();
1409         self.benchmark_group(group_name).bench_with_input(
1410             BenchmarkId::no_function_with_input(parameter),
1411             input,
1412             f,
1413         );
1414         self
1415     }
1416 
1417     /// Benchmarks a function under various inputs
1418     ///
1419     /// This is a convenience method to execute several related benchmarks. Each benchmark will
1420     /// receive the id: `${id}/${input}`.
1421     ///
1422     /// # Example
1423     ///
1424     /// ```rust
1425     /// # #[macro_use] extern crate criterion;
1426     /// # use self::criterion::*;
1427     ///
1428     /// fn bench(c: &mut Criterion) {
1429     ///     c.bench_function_over_inputs("from_elem",
1430     ///         |b: &mut Bencher, size: &usize| {
1431     ///             b.iter(|| vec![0u8; *size]);
1432     ///         },
1433     ///         vec![1024, 2048, 4096]
1434     ///     );
1435     /// }
1436     ///
1437     /// criterion_group!(benches, bench);
1438     /// criterion_main!(benches);
1439     /// ```
1440     #[doc(hidden)] // Soft-deprecated, use benchmark groups instead
bench_function_over_inputs<I, F>( &mut self, id: &str, f: F, inputs: I, ) -> &mut Criterion<M> where I: IntoIterator, I::Item: fmt::Debug + 'static, F: FnMut(&mut Bencher<'_, M>, &I::Item) + 'static,1441     pub fn bench_function_over_inputs<I, F>(
1442         &mut self,
1443         id: &str,
1444         f: F,
1445         inputs: I,
1446     ) -> &mut Criterion<M>
1447     where
1448         I: IntoIterator,
1449         I::Item: fmt::Debug + 'static,
1450         F: FnMut(&mut Bencher<'_, M>, &I::Item) + 'static,
1451     {
1452         self.bench(id, ParameterizedBenchmark::new(id, f, inputs))
1453     }
1454 
1455     /// Benchmarks multiple functions
1456     ///
1457     /// All functions get the same input and are compared with the other implementations.
1458     /// Works similar to `bench_function`, but with multiple functions.
1459     ///
1460     /// # Example
1461     ///
1462     /// ``` rust
1463     /// # #[macro_use] extern crate criterion;
1464     /// # use self::criterion::*;
1465     /// # fn seq_fib(i: &u32) {}
1466     /// # fn par_fib(i: &u32) {}
1467     ///
1468     /// fn bench_seq_fib(b: &mut Bencher, i: &u32) {
1469     ///     b.iter(|| {
1470     ///         seq_fib(i);
1471     ///     });
1472     /// }
1473     ///
1474     /// fn bench_par_fib(b: &mut Bencher, i: &u32) {
1475     ///     b.iter(|| {
1476     ///         par_fib(i);
1477     ///     });
1478     /// }
1479     ///
1480     /// fn bench(c: &mut Criterion) {
1481     ///     let sequential_fib = Fun::new("Sequential", bench_seq_fib);
1482     ///     let parallel_fib = Fun::new("Parallel", bench_par_fib);
1483     ///     let funs = vec![sequential_fib, parallel_fib];
1484     ///
1485     ///     c.bench_functions("Fibonacci", funs, 14);
1486     /// }
1487     ///
1488     /// criterion_group!(benches, bench);
1489     /// criterion_main!(benches);
1490     /// ```
1491     #[doc(hidden)] // Soft-deprecated, use benchmark groups instead
bench_functions<I>( &mut self, id: &str, funs: Vec<Fun<I, M>>, input: I, ) -> &mut Criterion<M> where I: fmt::Debug + 'static,1492     pub fn bench_functions<I>(
1493         &mut self,
1494         id: &str,
1495         funs: Vec<Fun<I, M>>,
1496         input: I,
1497     ) -> &mut Criterion<M>
1498     where
1499         I: fmt::Debug + 'static,
1500     {
1501         let benchmark = ParameterizedBenchmark::with_functions(
1502             funs.into_iter().map(|fun| fun.f).collect(),
1503             vec![input],
1504         );
1505 
1506         self.bench(id, benchmark)
1507     }
1508 
1509     /// Executes the given benchmark. Use this variant to execute benchmarks
1510     /// with complex configuration. This can be used to compare multiple
1511     /// functions, execute benchmarks with custom configuration settings and
1512     /// more. See the Benchmark and ParameterizedBenchmark structs for more
1513     /// information.
1514     ///
1515     /// ```rust
1516     /// # #[macro_use] extern crate criterion;
1517     /// # use criterion::*;
1518     /// # fn routine_1() {}
1519     /// # fn routine_2() {}
1520     ///
1521     /// fn bench(c: &mut Criterion) {
1522     ///     // Setup (construct data, allocate memory, etc)
1523     ///     c.bench(
1524     ///         "routines",
1525     ///         Benchmark::new("routine_1", |b| b.iter(|| routine_1()))
1526     ///             .with_function("routine_2", |b| b.iter(|| routine_2()))
1527     ///             .sample_size(50)
1528     ///     );
1529     /// }
1530     ///
1531     /// criterion_group!(benches, bench);
1532     /// criterion_main!(benches);
1533     /// ```
1534     #[doc(hidden)] // Soft-deprecated, use benchmark groups instead
bench<B: BenchmarkDefinition<M>>( &mut self, group_id: &str, benchmark: B, ) -> &mut Criterion<M>1535     pub fn bench<B: BenchmarkDefinition<M>>(
1536         &mut self,
1537         group_id: &str,
1538         benchmark: B,
1539     ) -> &mut Criterion<M> {
1540         benchmark.run(group_id, self);
1541         self
1542     }
1543 }
1544 
1545 trait DurationExt {
to_nanos(&self) -> u641546     fn to_nanos(&self) -> u64;
1547 }
1548 
1549 const NANOS_PER_SEC: u64 = 1_000_000_000;
1550 
1551 impl DurationExt for Duration {
to_nanos(&self) -> u641552     fn to_nanos(&self) -> u64 {
1553         self.as_secs() * NANOS_PER_SEC + u64::from(self.subsec_nanos())
1554     }
1555 }
1556 
1557 #[derive(Clone, Copy, PartialEq, Deserialize, Serialize, Debug)]
1558 struct ConfidenceInterval {
1559     confidence_level: f64,
1560     lower_bound: f64,
1561     upper_bound: f64,
1562 }
1563 
1564 #[derive(Clone, Copy, PartialEq, Deserialize, Serialize, Debug)]
1565 struct Estimate {
1566     /// The confidence interval for this estimate
1567     confidence_interval: ConfidenceInterval,
1568     ///
1569     point_estimate: f64,
1570     /// The standard error of this estimate
1571     standard_error: f64,
1572 }
1573 
build_estimates( distributions: &Distributions, points: &BTreeMap<Statistic, f64>, cl: f64, ) -> Estimates1574 fn build_estimates(
1575     distributions: &Distributions,
1576     points: &BTreeMap<Statistic, f64>,
1577     cl: f64,
1578 ) -> Estimates {
1579     distributions
1580         .iter()
1581         .map(|(&statistic, distribution)| {
1582             let point_estimate = points[&statistic];
1583             let (lb, ub) = distribution.confidence_interval(cl);
1584 
1585             (
1586                 statistic,
1587                 Estimate {
1588                     confidence_interval: ConfidenceInterval {
1589                         confidence_level: cl,
1590                         lower_bound: lb,
1591                         upper_bound: ub,
1592                     },
1593                     point_estimate,
1594                     standard_error: distribution.std_dev(None),
1595                 },
1596             )
1597         })
1598         .collect()
1599 }
1600 
1601 /// Enum representing different ways of measuring the throughput of benchmarked code.
1602 /// If the throughput setting is configured for a benchmark then the estimated throughput will
1603 /// be reported as well as the time per iteration.
1604 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1605 pub enum Throughput {
1606     /// Measure throughput in terms of bytes/second. The value should be the number of bytes
1607     /// processed by one iteration of the benchmarked code. Typically, this would be the length of
1608     /// an input string or `&[u8]`.
1609     Bytes(u64),
1610 
1611     /// Measure throughput in terms of elements/second. The value should be the number of elements
1612     /// processed by one iteration of the benchmarked code. Typically, this would be the size of a
1613     /// collection, but could also be the number of lines of input text or the number of values to
1614     /// parse.
1615     Elements(u64),
1616 }
1617 
1618 /// Axis scaling type
1619 #[derive(Debug, Clone, Copy)]
1620 pub enum AxisScale {
1621     /// Axes scale linearly
1622     Linear,
1623 
1624     /// Axes scale logarithmically
1625     Logarithmic,
1626 }
1627 
1628 /// Contains the configuration options for the plots generated by a particular benchmark
1629 /// or benchmark group.
1630 ///
1631 /// ```rust
1632 /// use self::criterion::{Bencher, Criterion, Benchmark, PlotConfiguration, AxisScale};
1633 ///
1634 /// let plot_config = PlotConfiguration::default()
1635 ///     .summary_scale(AxisScale::Logarithmic);
1636 ///
1637 /// // Using Criterion::default() for simplicity; normally you'd use the macros.
1638 /// let mut criterion = Criterion::default();
1639 /// let mut benchmark_group = criterion.benchmark_group("Group name");
1640 /// benchmark_group.plot_config(plot_config);
1641 /// // Use benchmark group
1642 /// ```
1643 #[derive(Debug, Clone)]
1644 pub struct PlotConfiguration {
1645     summary_scale: AxisScale,
1646 }
1647 
1648 impl Default for PlotConfiguration {
default() -> PlotConfiguration1649     fn default() -> PlotConfiguration {
1650         PlotConfiguration {
1651             summary_scale: AxisScale::Linear,
1652         }
1653     }
1654 }
1655 
1656 impl PlotConfiguration {
1657     /// Set the axis scale (linear or logarithmic) for the summary plots. Typically, you would
1658     /// set this to logarithmic if benchmarking over a range of inputs which scale exponentially.
1659     /// Defaults to linear.
summary_scale(mut self, new_scale: AxisScale) -> PlotConfiguration1660     pub fn summary_scale(mut self, new_scale: AxisScale) -> PlotConfiguration {
1661         self.summary_scale = new_scale;
1662         self
1663     }
1664 }
1665 
1666 /// Custom-test-framework runner. Should not be called directly.
1667 #[doc(hidden)]
runner(benches: &[&dyn Fn()])1668 pub fn runner(benches: &[&dyn Fn()]) {
1669     for bench in benches {
1670         bench();
1671     }
1672     Criterion::default().configure_from_args().final_summary();
1673 }
1674