1use criterion::{
2 black_box, criterion_group,
3 measurement::{Measurement, ValueFormatter},
4 Criterion, Throughput,
5};
6use std::time::{Duration, Instant};
7
8struct HalfSecFormatter;
9impl ValueFormatter for HalfSecFormatter {
10 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
15 fn format_throughput(&self, throughput: &Throughput, value: f64) -> String {
16 match *throughput {
17 Throughput::Bytes(bytes) | Throughput::BytesDecimal(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
27 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
35 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) | Throughput::BytesDecimal(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
59 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
68const NANOS_PER_SEC: u64 = 1_000_000_000;
69
70/// Silly "measurement" that is really just wall-clock time reported in half-seconds.
71struct HalfSeconds;
72impl Measurement for HalfSeconds {
73 type Intermediate = Instant;
74 type Value = Duration;
75
76 fn start(&self) -> Self::Intermediate {
77 Instant::now()
78 }
79 fn end(&self, i: Self::Intermediate) -> Self::Value {
80 i.elapsed()
81 }
82 fn add(&self, v1: &Self::Value, v2: &Self::Value) -> Self::Value {
83 *v1 + *v2
84 }
85 fn zero(&self) -> Self::Value {
86 Duration::from_secs(0)
87 }
88 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 }
92 fn formatter(&self) -> &dyn ValueFormatter {
93 &HalfSecFormatter
94 }
95}
96
97fn 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
104fn 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
110fn alternate_measurement() -> Criterion<HalfSeconds> {
111 Criterion::default().with_measurement(HalfSeconds)
112}
113
114criterion_group! {
115 name = benches;
116 config = alternate_measurement();
117 targets = fibonacci_cycles
118}
119