1 // Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10 
11 //! Simplified stable-compatible benchmark runner.
12 //!
13 //! Almost all user code will only be interested in `Bencher` and the
14 //! macros that are used to describe benchmarker functions and
15 //! the benchmark runner.
16 //!
17 //! NOTE: There's no proper `black_box` yet in this stable port of the
18 //! benchmark runner, only a workaround implementation. It may not work
19 //! exactly like the upstream `test::black_box`.
20 //!
21 //! One way to use this crate is to use it as dev-dependency and setup
22 //! cargo to compile a file in `benches/` that runs without the testing harness.
23 //!
24 //! In Cargo.toml:
25 //!
26 //! ```ignore
27 //! [[bench]]
28 //! name = "example"
29 //! harness = false
30 //! ```
31 //!
32 //! In benches/example.rs:
33 //!
34 //! ```
35 //! #[macro_use]
36 //! extern crate bencher;
37 //!
38 //! use bencher::Bencher;
39 //!
40 //! fn a(bench: &mut Bencher) {
41 //!     bench.iter(|| {
42 //!         (0..1000).fold(0, |x, y| x + y)
43 //!     })
44 //! }
45 //!
46 //! fn b(bench: &mut Bencher) {
47 //!     const N: usize = 1024;
48 //!     bench.iter(|| {
49 //!         vec![0u8; N]
50 //!     });
51 //!
52 //!     bench.bytes = N as u64;
53 //! }
54 //!
55 //! benchmark_group!(benches, a, b);
56 //! benchmark_main!(benches);
57 //!
58 //! # #[cfg(never)]
59 //! # fn main() { }
60 //! ```
61 //!
62 //! Use `cargo bench` as usual. A command line argument can be used to filter
63 //! which benchmarks to run.
64 
65 pub use self::TestFn::*;
66 use self::TestResult::*;
67 use self::TestEvent::*;
68 use self::NamePadding::*;
69 use self::OutputLocation::*;
70 
71 use std::borrow::Cow;
72 use std::cmp;
73 use std::fmt;
74 use std::fs::File;
75 use std::io::prelude::*;
76 use std::io;
77 use std::iter::repeat;
78 use std::mem::forget;
79 use std::path::PathBuf;
80 use std::ptr;
81 use std::time::{Instant, Duration};
82 
83 pub mod stats;
84 mod macros;
85 
86 // The name of a test. By convention this follows the rules for rust
87 // paths; i.e. it should be a series of identifiers separated by double
88 // colons. This way if some test runner wants to arrange the tests
89 // hierarchically it may.
90 
91 pub type TestName = Cow<'static, str>;
92 
93 #[derive(Clone, Copy, PartialEq, Eq)]
94 enum NamePadding {
95     PadOnRight,
96 }
97 
98 impl TestDesc {
padded_name(&self, column_count: usize, align: NamePadding) -> String99     fn padded_name(&self, column_count: usize, align: NamePadding) -> String {
100         let mut name = self.name.to_string();
101         let fill = column_count.saturating_sub(name.len());
102         let pad = repeat(" ").take(fill).collect::<String>();
103         match align {
104             PadOnRight => {
105                 name.push_str(&pad);
106                 name
107             }
108         }
109     }
110 }
111 
112 /// Represents a benchmark function.
113 pub trait TDynBenchFn: Send {
run(&self, harness: &mut Bencher)114     fn run(&self, harness: &mut Bencher);
115 }
116 
117 // A function that runs a test. If the function returns successfully,
118 // the test succeeds; if the function panics then the test fails. We
119 // may need to come up with a more clever definition of test in order
120 // to support isolation of tests into threads.
121 pub enum TestFn {
122     StaticBenchFn(fn(&mut Bencher)),
123     DynBenchFn(Box<TDynBenchFn + 'static>),
124 }
125 
126 impl TestFn {
padding(&self) -> NamePadding127     fn padding(&self) -> NamePadding {
128         match *self {
129             StaticBenchFn(..) |
130             DynBenchFn(..) => PadOnRight,
131         }
132     }
133 }
134 
135 impl fmt::Debug for TestFn {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result136     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
137         f.write_str(match *self {
138             StaticBenchFn(..) => "StaticBenchFn(..)",
139             DynBenchFn(..) => "DynBenchFn(..)",
140         })
141     }
142 }
143 
144 /// Manager of the benchmarking runs.
145 ///
146 /// This is fed into functions marked with `#[bench]` to allow for
147 /// set-up & tear-down before running a piece of code repeatedly via a
148 /// call to `iter`.
149 #[derive(Copy, Clone)]
150 pub struct Bencher {
151     iterations: u64,
152     dur: Duration,
153     pub bytes: u64,
154 }
155 
156 // The definition of a single test. A test runner will run a list of
157 // these.
158 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
159 pub struct TestDesc {
160     pub name: TestName,
161     pub ignore: bool,
162 }
163 
164 #[derive(Clone)]
165 pub struct TestPaths {
166     pub file: PathBuf,         // e.g., compile-test/foo/bar/baz.rs
167     pub base: PathBuf,         // e.g., compile-test, auxiliary
168     pub relative_dir: PathBuf, // e.g., foo/bar
169 }
170 
171 #[derive(Debug)]
172 pub struct TestDescAndFn {
173     pub desc: TestDesc,
174     pub testfn: TestFn,
175 }
176 
177 #[derive(Default)]
178 pub struct TestOpts {
179     pub filter: Option<String>,
180     pub run_ignored: bool,
181     pub logfile: Option<PathBuf>,
182     pub quiet: bool,
183     pub test_threads: Option<usize>,
184 }
185 
186 #[derive(Clone, PartialEq)]
187 pub struct BenchSamples {
188     ns_iter_summ: stats::Summary,
189     mb_s: usize,
190 }
191 
192 #[derive(Clone, PartialEq)]
193 enum TestResult {
194     TrIgnored,
195     TrBench(BenchSamples),
196 }
197 
198 unsafe impl Send for TestResult {}
199 
200 enum OutputLocation<T> {
201     Raw(T),
202 }
203 
204 struct ConsoleTestState<T> {
205     log_out: Option<File>,
206     out: OutputLocation<T>,
207     quiet: bool,
208     total: usize,
209     passed: usize,
210     failed: usize,
211     ignored: usize,
212     measured: usize,
213     failures: Vec<(TestDesc, Vec<u8>)>,
214     max_name_len: usize, // number of columns to fill when aligning names
215 }
216 
217 impl ConsoleTestState<()> {
new(opts: &TestOpts) -> io::Result<ConsoleTestState<io::Stdout>>218     pub fn new(opts: &TestOpts) -> io::Result<ConsoleTestState<io::Stdout>> {
219         let log_out = match opts.logfile {
220             Some(ref path) => Some(try!(File::create(path))),
221             None => None,
222         };
223         let out = Raw(io::stdout());
224 
225         Ok(ConsoleTestState {
226             out: out,
227             log_out: log_out,
228             quiet: opts.quiet,
229             total: 0,
230             passed: 0,
231             failed: 0,
232             ignored: 0,
233             measured: 0,
234             failures: Vec::new(),
235             max_name_len: 0,
236         })
237     }
238 }
239 
240 impl<T: Write> ConsoleTestState<T> {
write_ignored(&mut self) -> io::Result<()>241     pub fn write_ignored(&mut self) -> io::Result<()> {
242         self.write_short_result("ignored", "i")
243     }
244 
write_bench(&mut self) -> io::Result<()>245     pub fn write_bench(&mut self) -> io::Result<()> {
246         self.write_pretty("bench")
247     }
248 
write_short_result(&mut self, verbose: &str, quiet: &str) -> io::Result<()>249     pub fn write_short_result(&mut self, verbose: &str, quiet: &str)
250                               -> io::Result<()> {
251         if self.quiet {
252             self.write_pretty(quiet)
253         } else {
254             try!(self.write_pretty(verbose));
255             self.write_plain("\n")
256         }
257     }
258 
write_pretty(&mut self, word: &str) -> io::Result<()>259     pub fn write_pretty(&mut self, word: &str) -> io::Result<()> {
260         match self.out {
261             Raw(ref mut stdout) => {
262                 try!(stdout.write_all(word.as_bytes()));
263                 stdout.flush()
264             }
265         }
266     }
267 
write_plain(&mut self, s: &str) -> io::Result<()>268     pub fn write_plain(&mut self, s: &str) -> io::Result<()> {
269         match self.out {
270             Raw(ref mut stdout) => {
271                 try!(stdout.write_all(s.as_bytes()));
272                 stdout.flush()
273             }
274         }
275     }
276 
write_run_start(&mut self, len: usize) -> io::Result<()>277     pub fn write_run_start(&mut self, len: usize) -> io::Result<()> {
278         self.total = len;
279         let noun = if len != 1 {
280             "tests"
281         } else {
282             "test"
283         };
284         self.write_plain(&format!("\nrunning {} {}\n", len, noun))
285     }
286 
write_test_start(&mut self, test: &TestDesc, align: NamePadding) -> io::Result<()>287     pub fn write_test_start(&mut self, test: &TestDesc, align: NamePadding) -> io::Result<()> {
288         if self.quiet && align != PadOnRight {
289             Ok(())
290         } else {
291             let name = test.padded_name(self.max_name_len, align);
292             self.write_plain(&format!("test {} ... ", name))
293         }
294     }
295 
write_result(&mut self, result: &TestResult) -> io::Result<()>296     pub fn write_result(&mut self, result: &TestResult) -> io::Result<()> {
297         match *result {
298             TrIgnored => self.write_ignored(),
299             TrBench(ref bs) => {
300                 try!(self.write_bench());
301                 self.write_plain(&format!(": {}\n", fmt_bench_samples(bs)))
302             }
303         }
304     }
305 
write_log(&mut self, test: &TestDesc, result: &TestResult) -> io::Result<()>306     pub fn write_log(&mut self, test: &TestDesc, result: &TestResult) -> io::Result<()> {
307         match self.log_out {
308             None => Ok(()),
309             Some(ref mut o) => {
310                 let s = format!("{} {}\n",
311                                 match *result {
312                                     TrIgnored => "ignored".to_owned(),
313                                     TrBench(ref bs) => fmt_bench_samples(bs),
314                                 },
315                                 test.name);
316                 o.write_all(s.as_bytes())
317             }
318         }
319     }
320 
write_failures(&mut self) -> io::Result<()>321     pub fn write_failures(&mut self) -> io::Result<()> {
322         try!(self.write_plain("\nfailures:\n"));
323         let mut failures = Vec::new();
324         let mut fail_out = String::new();
325         for &(ref f, ref stdout) in &self.failures {
326             failures.push(f.name.to_string());
327             if !stdout.is_empty() {
328                 fail_out.push_str(&format!("---- {} stdout ----\n\t", f.name));
329                 let output = String::from_utf8_lossy(stdout);
330                 fail_out.push_str(&output);
331                 fail_out.push_str("\n");
332             }
333         }
334         if !fail_out.is_empty() {
335             try!(self.write_plain("\n"));
336             try!(self.write_plain(&fail_out));
337         }
338 
339         try!(self.write_plain("\nfailures:\n"));
340         failures.sort();
341         for name in &failures {
342             try!(self.write_plain(&format!("    {}\n", name)));
343         }
344         Ok(())
345     }
346 
write_run_finish(&mut self) -> io::Result<bool>347     pub fn write_run_finish(&mut self) -> io::Result<bool> {
348         assert_eq!(self.passed + self.failed + self.ignored + self.measured, self.total);
349 
350         let success = self.failed == 0;
351         if !success {
352             try!(self.write_failures());
353         }
354 
355         try!(self.write_plain("\ntest result: "));
356         if success {
357             // There's no parallelism at this point so it's safe to use color
358             try!(self.write_pretty("ok"));
359         } else {
360             try!(self.write_pretty("FAILED"));
361         }
362         let s = format!(". {} passed; {} failed; {} ignored; {} measured\n\n",
363                         self.passed,
364                         self.failed,
365                         self.ignored,
366                         self.measured);
367         try!(self.write_plain(&s));
368         Ok(success)
369     }
370 }
371 
372 // Format a number with thousands separators
fmt_thousands_sep(mut n: usize, sep: char) -> String373 fn fmt_thousands_sep(mut n: usize, sep: char) -> String {
374     use std::fmt::Write;
375     let mut output = String::new();
376     let mut trailing = false;
377     for &pow in &[9, 6, 3, 0] {
378         let base = 10_usize.pow(pow);
379         if pow == 0 || trailing || n / base != 0 {
380             if !trailing {
381                 output.write_fmt(format_args!("{}", n / base)).unwrap();
382             } else {
383                 output.write_fmt(format_args!("{:03}", n / base)).unwrap();
384             }
385             if pow != 0 {
386                 output.push(sep);
387             }
388             trailing = true;
389         }
390         n %= base;
391     }
392 
393     output
394 }
395 
fmt_bench_samples(bs: &BenchSamples) -> String396 pub fn fmt_bench_samples(bs: &BenchSamples) -> String {
397     use std::fmt::Write;
398     let mut output = String::new();
399 
400     let median = bs.ns_iter_summ.median as usize;
401     let deviation = (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as usize;
402 
403     output.write_fmt(format_args!("{:>11} ns/iter (+/- {})",
404                                   fmt_thousands_sep(median, ','),
405                                   fmt_thousands_sep(deviation, ',')))
406           .unwrap();
407     if bs.mb_s != 0 {
408         output.write_fmt(format_args!(" = {} MB/s", bs.mb_s)).unwrap();
409     }
410     output
411 }
412 
413 // A simple console test runner
run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Result<bool>414 pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Result<bool> {
415 
416     fn callback<T: Write>(event: &TestEvent, st: &mut ConsoleTestState<T>) -> io::Result<()> {
417         match (*event).clone() {
418             TeFiltered(ref filtered_tests) => st.write_run_start(filtered_tests.len()),
419             TeWait(ref test, padding) => st.write_test_start(test, padding),
420             TeResult(test, result, _) => {
421                 try!(st.write_log(&test, &result));
422                 try!(st.write_result(&result));
423                 match result {
424                     TrIgnored => st.ignored += 1,
425                     TrBench(_) => {
426                         st.measured += 1
427                     }
428                 }
429                 Ok(())
430             }
431         }
432     }
433 
434     let mut st = try!(ConsoleTestState::new(opts));
435     fn len_if_padded(t: &TestDescAndFn) -> usize {
436         match t.testfn.padding() {
437             PadOnRight => t.desc.name.len(),
438         }
439     }
440     if let Some(t) = tests.iter().max_by_key(|t| len_if_padded(*t)) {
441         let n = &t.desc.name;
442         st.max_name_len = n.len();
443     }
444     try!(run_tests(opts, tests, |x| callback(&x, &mut st)));
445     st.write_run_finish()
446 }
447 
448 #[test]
should_sort_failures_before_printing_them()449 fn should_sort_failures_before_printing_them() {
450     let test_a = TestDesc {
451         name: Cow::from("a"),
452         ignore: false,
453     };
454 
455     let test_b = TestDesc {
456         name: Cow::from("b"),
457         ignore: false,
458     };
459 
460     let mut st = ConsoleTestState {
461         log_out: None,
462         out: Raw(Vec::new()),
463         quiet: false,
464         total: 0,
465         passed: 0,
466         failed: 0,
467         ignored: 0,
468         measured: 0,
469         max_name_len: 10,
470         failures: vec![(test_b, Vec::new()), (test_a, Vec::new())],
471     };
472 
473     st.write_failures().unwrap();
474     let s = match st.out {
475         Raw(ref m) => String::from_utf8_lossy(&m[..]),
476     };
477 
478     let apos = s.find("a").unwrap();
479     let bpos = s.find("b").unwrap();
480     assert!(apos < bpos);
481 }
482 
483 #[derive(Clone)]
484 enum TestEvent {
485     TeFiltered(Vec<TestDesc>),
486     TeWait(TestDesc, NamePadding),
487     TeResult(TestDesc, TestResult, Vec<u8>),
488 }
489 
490 type MonitorMsg = (TestDesc, TestResult, Vec<u8>);
491 
492 
run_tests<F>(opts: &TestOpts, tests: Vec<TestDescAndFn>, mut callback: F) -> io::Result<()> where F: FnMut(TestEvent) -> io::Result<()>493 fn run_tests<F>(opts: &TestOpts, tests: Vec<TestDescAndFn>, mut callback: F) -> io::Result<()>
494     where F: FnMut(TestEvent) -> io::Result<()>
495 {
496 
497     let filtered_tests = filter_tests(opts, tests);
498 
499     let filtered_descs = filtered_tests.iter()
500                                        .map(|t| t.desc.clone())
501                                        .collect();
502 
503     try!(callback(TeFiltered(filtered_descs)));
504 
505     let filtered_benchs_and_metrics = filtered_tests;
506 
507     // All benchmarks run at the end, in serial.
508     // (this includes metric fns)
509     for b in filtered_benchs_and_metrics {
510         try!(callback(TeWait(b.desc.clone(), b.testfn.padding())));
511         let (test, result, stdout) = run_test(opts, false, b);
512         try!(callback(TeResult(test, result, stdout)));
513     }
514     Ok(())
515 }
516 
filter_tests(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> Vec<TestDescAndFn>517 fn filter_tests(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> Vec<TestDescAndFn> {
518     let mut filtered = tests;
519 
520     // Remove tests that don't match the test filter
521     filtered = match opts.filter {
522         None => filtered,
523         Some(ref filter) => {
524             filtered.into_iter()
525                     .filter(|test| test.desc.name.contains(&filter[..]))
526                     .collect()
527         }
528     };
529 
530     // Maybe pull out the ignored test and unignore them
531     filtered = if !opts.run_ignored {
532         filtered
533     } else {
534         fn filter(test: TestDescAndFn) -> Option<TestDescAndFn> {
535             if test.desc.ignore {
536                 let TestDescAndFn {desc, testfn} = test;
537                 Some(TestDescAndFn {
538                     desc: TestDesc { ignore: false, ..desc },
539                     testfn: testfn,
540                 })
541             } else {
542                 None
543             }
544         }
545         filtered.into_iter().filter_map(filter).collect()
546     };
547 
548     // Sort the tests alphabetically
549     filtered.sort_by(|t1, t2| t1.desc.name.cmp(&t2.desc.name));
550 
551     filtered
552 }
553 
run_test(_opts: &TestOpts, force_ignore: bool, test: TestDescAndFn) -> MonitorMsg554 fn run_test(_opts: &TestOpts,
555             force_ignore: bool,
556             test: TestDescAndFn) -> MonitorMsg
557 {
558 
559     let TestDescAndFn {desc, testfn} = test;
560 
561     if force_ignore || desc.ignore {
562         return (desc, TrIgnored, Vec::new());
563     }
564 
565     match testfn {
566         DynBenchFn(bencher) => {
567             let bs = ::bench::benchmark(|harness| bencher.run(harness));
568             (desc, TrBench(bs), Vec::new())
569         }
570         StaticBenchFn(benchfn) => {
571             let bs = ::bench::benchmark(|harness| benchfn(harness));
572             (desc, TrBench(bs), Vec::new())
573         }
574     }
575 }
576 
577 
578 // Benchmarking
579 
580 // FIXME: We don't have black_box in stable rust
581 
582 /// NOTE: We don't have a proper black box in stable Rust. This is
583 /// a workaround implementation, that may have a too big performance overhead,
584 /// depending on operation, or it may fail to properly avoid having code
585 /// optimized out. It is good enough that it is used by default.
586 ///
587 /// A function that is opaque to the optimizer, to allow benchmarks to
588 /// pretend to use outputs to assist in avoiding dead-code
589 /// elimination.
black_box<T>(dummy: T) -> T590 pub fn black_box<T>(dummy: T) -> T {
591     unsafe {
592         let ret = ptr::read_volatile(&dummy);
593         forget(dummy);
594         ret
595     }
596 }
597 
598 
599 impl Bencher {
600     /// Callback for benchmark functions to run in their body.
iter<T, F>(&mut self, mut inner: F) where F: FnMut() -> T601     pub fn iter<T, F>(&mut self, mut inner: F)
602         where F: FnMut() -> T
603     {
604         let start = Instant::now();
605         let k = self.iterations;
606         for _ in 0..k {
607             black_box(inner());
608         }
609         self.dur = start.elapsed();
610     }
611 
ns_elapsed(&mut self) -> u64612     pub fn ns_elapsed(&mut self) -> u64 {
613         self.dur.as_secs() * 1_000_000_000 + (self.dur.subsec_nanos() as u64)
614     }
615 
ns_per_iter(&mut self) -> u64616     pub fn ns_per_iter(&mut self) -> u64 {
617         if self.iterations == 0 {
618             0
619         } else {
620             self.ns_elapsed() / cmp::max(self.iterations, 1)
621         }
622     }
623 
bench_n<F>(&mut self, n: u64, f: F) where F: FnOnce(&mut Bencher)624     pub fn bench_n<F>(&mut self, n: u64, f: F)
625         where F: FnOnce(&mut Bencher)
626     {
627         self.iterations = n;
628         f(self);
629     }
630 
631     // This is a more statistics-driven benchmark algorithm
auto_bench<F>(&mut self, mut f: F) -> stats::Summary where F: FnMut(&mut Bencher)632     pub fn auto_bench<F>(&mut self, mut f: F) -> stats::Summary
633         where F: FnMut(&mut Bencher)
634     {
635         // Initial bench run to get ballpark figure.
636         let mut n = 1;
637         self.bench_n(n, |x| f(x));
638 
639         // Try to estimate iter count for 1ms falling back to 1m
640         // iterations if first run took < 1ns.
641         if self.ns_per_iter() == 0 {
642             n = 1_000_000;
643         } else {
644             n = 1_000_000 / cmp::max(self.ns_per_iter(), 1);
645         }
646         // if the first run took more than 1ms we don't want to just
647         // be left doing 0 iterations on every loop. The unfortunate
648         // side effect of not being able to do as many runs is
649         // automatically handled by the statistical analysis below
650         // (i.e. larger error bars).
651         if n == 0 {
652             n = 1;
653         }
654 
655         let mut total_run = Duration::new(0, 0);
656         let samples: &mut [f64] = &mut [0.0_f64; 50];
657         loop {
658             let loop_start = Instant::now();
659 
660             for p in &mut *samples {
661                 self.bench_n(n, |x| f(x));
662                 *p = self.ns_per_iter() as f64;
663             }
664 
665             stats::winsorize(samples, 5.0);
666             let summ = stats::Summary::new(samples);
667 
668             for p in &mut *samples {
669                 self.bench_n(5 * n, |x| f(x));
670                 *p = self.ns_per_iter() as f64;
671             }
672 
673             stats::winsorize(samples, 5.0);
674             let summ5 = stats::Summary::new(samples);
675             let loop_run = loop_start.elapsed();
676 
677             // If we've run for 100ms and seem to have converged to a
678             // stable median.
679             if loop_run > Duration::from_millis(100) && summ.median_abs_dev_pct < 1.0 &&
680                summ.median - summ5.median < summ5.median_abs_dev {
681                 return summ5;
682             }
683 
684             total_run += loop_run;
685             // Longest we ever run for is 3s.
686             if total_run > Duration::from_secs(3) {
687                 return summ5;
688             }
689 
690             // If we overflow here just return the results so far. We check a
691             // multiplier of 10 because we're about to multiply by 2 and the
692             // next iteration of the loop will also multiply by 5 (to calculate
693             // the summ5 result)
694             n = match n.checked_mul(10) {
695                 Some(_) => n * 2,
696                 None => return summ5,
697             };
698         }
699     }
700 }
701 
702 pub mod bench {
703     use std::cmp;
704     use std::time::Duration;
705     use super::{Bencher, BenchSamples};
706 
benchmark<F>(f: F) -> BenchSamples where F: FnMut(&mut Bencher)707     pub fn benchmark<F>(f: F) -> BenchSamples
708         where F: FnMut(&mut Bencher)
709     {
710         let mut bs = Bencher {
711             iterations: 0,
712             dur: Duration::new(0, 0),
713             bytes: 0,
714         };
715 
716         let ns_iter_summ = bs.auto_bench(f);
717 
718         let ns_iter = cmp::max(ns_iter_summ.median as u64, 1);
719         let mb_s = bs.bytes * 1000 / ns_iter;
720 
721         BenchSamples {
722             ns_iter_summ: ns_iter_summ,
723             mb_s: mb_s as usize,
724         }
725     }
726 
run_once<F>(f: F) where F: FnOnce(&mut Bencher)727     pub fn run_once<F>(f: F)
728         where F: FnOnce(&mut Bencher)
729     {
730         let mut bs = Bencher {
731             iterations: 0,
732             dur: Duration::new(0, 0),
733             bytes: 0,
734         };
735         bs.bench_n(1, f);
736     }
737 }
738 
739