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 { |
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 | |
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 | |
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 | |
97 | 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 | |
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 | |
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 | |