1 use criterion::{
2     black_box, criterion_group,
3     measurement::{Measurement, ValueFormatter},
4     Criterion, Throughput,
5 };
6 use std::time::{Duration, Instant};
7 
8 struct HalfSecFormatter;
9 impl ValueFormatter for HalfSecFormatter {
format_value(&self, value: f64) -> String10     fn format_value(&self, value: f64) -> String {
11         // The value will be in nanoseconds so we have to convert to half-seconds.
12         format!("{} s/2", value * 2f64 * 10f64.powi(-9))
13     }
14 
format_throughput(&self, throughput: &Throughput, value: f64) -> String15     fn format_throughput(&self, throughput: &Throughput, value: f64) -> String {
16         match *throughput {
17             Throughput::Bytes(bytes) => {
18                 format!("{} b/s/2", (bytes as f64) / (value * 2f64 * 10f64.powi(-9)))
19             }
20             Throughput::Elements(elems) => format!(
21                 "{} elem/s/2",
22                 (elems as f64) / (value * 2f64 * 10f64.powi(-9))
23             ),
24         }
25     }
26 
scale_values(&self, _typical: f64, values: &mut [f64]) -> &'static str27     fn scale_values(&self, _typical: f64, values: &mut [f64]) -> &'static str {
28         for val in values {
29             *val *= 2f64 * 10f64.powi(-9);
30         }
31 
32         "s/2"
33     }
34 
scale_throughputs( &self, _typical: f64, throughput: &Throughput, values: &mut [f64], ) -> &'static str35     fn scale_throughputs(
36         &self,
37         _typical: f64,
38         throughput: &Throughput,
39         values: &mut [f64],
40     ) -> &'static str {
41         match *throughput {
42             Throughput::Bytes(bytes) => {
43                 for val in values {
44                     *val = (bytes as f64) / (*val * 2f64 * 10f64.powi(-9))
45                 }
46 
47                 "b/s/2"
48             }
49             Throughput::Elements(elems) => {
50                 for val in values {
51                     *val = (elems as f64) / (*val * 2f64 * 10f64.powi(-9))
52                 }
53 
54                 "elem/s/2"
55             }
56         }
57     }
58 
scale_for_machines(&self, values: &mut [f64]) -> &'static str59     fn scale_for_machines(&self, values: &mut [f64]) -> &'static str {
60         for val in values {
61             *val *= 2f64 * 10f64.powi(-9);
62         }
63 
64         "s/2"
65     }
66 }
67 
68 const NANOS_PER_SEC: u64 = 1_000_000_000;
69 
70 /// Silly "measurement" that is really just wall-clock time reported in half-seconds.
71 struct HalfSeconds;
72 impl Measurement for HalfSeconds {
73     type Intermediate = Instant;
74     type Value = Duration;
75 
start(&self) -> Self::Intermediate76     fn start(&self) -> Self::Intermediate {
77         Instant::now()
78     }
end(&self, i: Self::Intermediate) -> Self::Value79     fn end(&self, i: Self::Intermediate) -> Self::Value {
80         i.elapsed()
81     }
add(&self, v1: &Self::Value, v2: &Self::Value) -> Self::Value82     fn add(&self, v1: &Self::Value, v2: &Self::Value) -> Self::Value {
83         *v1 + *v2
84     }
zero(&self) -> Self::Value85     fn zero(&self) -> Self::Value {
86         Duration::from_secs(0)
87     }
to_f64(&self, val: &Self::Value) -> f6488     fn to_f64(&self, val: &Self::Value) -> f64 {
89         let nanos = val.as_secs() * NANOS_PER_SEC + u64::from(val.subsec_nanos());
90         nanos as f64
91     }
formatter(&self) -> &dyn ValueFormatter92     fn formatter(&self) -> &dyn ValueFormatter {
93         &HalfSecFormatter
94     }
95 }
96 
fibonacci_slow(n: u64) -> u6497 fn fibonacci_slow(n: u64) -> u64 {
98     match n {
99         0 | 1 => 1,
100         n => fibonacci_slow(n - 1) + fibonacci_slow(n - 2),
101     }
102 }
103 
fibonacci_cycles(criterion: &mut Criterion<HalfSeconds>)104 fn fibonacci_cycles(criterion: &mut Criterion<HalfSeconds>) {
105     criterion.bench_function("fibonacci_custom_measurement", |bencher| {
106         bencher.iter(|| fibonacci_slow(black_box(10)))
107     });
108 }
109 
alternate_measurement() -> Criterion<HalfSeconds>110 fn alternate_measurement() -> Criterion<HalfSeconds> {
111     Criterion::default().with_measurement(HalfSeconds)
112 }
113 
114 criterion_group! {
115     name = benches;
116     config = alternate_measurement();
117     targets = fibonacci_cycles
118 }
119