/rust/registry/src/index.crates.io-1949cf8c6b5b557f/criterion-0.5.1/src/measurement.rs
Line | Count | Source |
1 | | //! This module defines a set of traits that can be used to plug different measurements (eg. |
2 | | //! Unix's Processor Time, CPU or GPU performance counters, etc.) into Criterion.rs. It also |
3 | | //! includes the [WallTime](struct.WallTime.html) struct which defines the default wall-clock time |
4 | | //! measurement. |
5 | | |
6 | | use crate::format::short; |
7 | | use crate::Throughput; |
8 | | use std::time::{Duration, Instant}; |
9 | | |
10 | | /// Trait providing functions to format measured values to string so that they can be displayed on |
11 | | /// the command line or in the reports. The functions of this trait take measured values in f64 |
12 | | /// form; implementors can assume that the values are of the same scale as those produced by the |
13 | | /// associated [MeasuredValue](trait.MeasuredValue.html) (eg. if your measurement produces values in |
14 | | /// nanoseconds, the values passed to the formatter will be in nanoseconds). |
15 | | /// |
16 | | /// Implementors are encouraged to format the values in a way that is intuitive for humans and |
17 | | /// uses the SI prefix system. For example, the format used by [WallTime](struct.WallTime.html) |
18 | | /// can display the value in units ranging from picoseconds to seconds depending on the magnitude |
19 | | /// of the elapsed time in nanoseconds. |
20 | | pub trait ValueFormatter { |
21 | | /// Format the value (with appropriate unit) and return it as a string. |
22 | 0 | fn format_value(&self, value: f64) -> String { |
23 | 0 | let mut values = [value]; |
24 | 0 | let unit = self.scale_values(value, &mut values); |
25 | 0 | format!("{:>6} {}", short(values[0]), unit) |
26 | 0 | } |
27 | | |
28 | | /// Format the value as a throughput measurement. The value represents the measurement value; |
29 | | /// the implementor will have to calculate bytes per second, iterations per cycle, etc. |
30 | 0 | fn format_throughput(&self, throughput: &Throughput, value: f64) -> String { |
31 | 0 | let mut values = [value]; |
32 | 0 | let unit = self.scale_throughputs(value, throughput, &mut values); |
33 | 0 | format!("{:>6} {}", short(values[0]), unit) |
34 | 0 | } |
35 | | |
36 | | /// Scale the given values to some appropriate unit and return the unit string. |
37 | | /// |
38 | | /// The given typical value should be used to choose the unit. This function may be called |
39 | | /// multiple times with different datasets; the typical value will remain the same to ensure |
40 | | /// that the units remain consistent within a graph. The typical value will not be NaN. |
41 | | /// Values will not contain NaN as input, and the transformed values must not contain NaN. |
42 | | fn scale_values(&self, typical_value: f64, values: &mut [f64]) -> &'static str; |
43 | | |
44 | | /// Convert the given measured values into throughput numbers based on the given throughput |
45 | | /// value, scale them to some appropriate unit, and return the unit string. |
46 | | /// |
47 | | /// The given typical value should be used to choose the unit. This function may be called |
48 | | /// multiple times with different datasets; the typical value will remain the same to ensure |
49 | | /// that the units remain consistent within a graph. The typical value will not be NaN. |
50 | | /// Values will not contain NaN as input, and the transformed values must not contain NaN. |
51 | | fn scale_throughputs( |
52 | | &self, |
53 | | typical_value: f64, |
54 | | throughput: &Throughput, |
55 | | values: &mut [f64], |
56 | | ) -> &'static str; |
57 | | |
58 | | /// Scale the values and return a unit string designed for machines. |
59 | | /// |
60 | | /// For example, this is used for the CSV file output. Implementations should modify the given |
61 | | /// values slice to apply the desired scaling (if any) and return a string representing the unit |
62 | | /// the modified values are in. |
63 | | fn scale_for_machines(&self, values: &mut [f64]) -> &'static str; |
64 | | } |
65 | | |
66 | | /// Trait for all types which define something Criterion.rs can measure. The only measurement |
67 | | /// currently provided is [WallTime](struct.WallTime.html), but third party crates or benchmarks |
68 | | /// may define more. |
69 | | /// |
70 | | /// This trait defines two core methods, `start` and `end`. `start` is called at the beginning of |
71 | | /// a measurement to produce some intermediate value (for example, the wall-clock time at the start |
72 | | /// of that set of iterations) and `end` is called at the end of the measurement with the value |
73 | | /// returned by `start`. |
74 | | /// |
75 | | pub trait Measurement { |
76 | | /// This type represents an intermediate value for the measurements. It will be produced by the |
77 | | /// start function and passed to the end function. An example might be the wall-clock time as |
78 | | /// of the `start` call. |
79 | | type Intermediate; |
80 | | |
81 | | /// This type is the measured value. An example might be the elapsed wall-clock time between the |
82 | | /// `start` and `end` calls. |
83 | | type Value; |
84 | | |
85 | | /// Criterion.rs will call this before iterating the benchmark. |
86 | | fn start(&self) -> Self::Intermediate; |
87 | | |
88 | | /// Criterion.rs will call this after iterating the benchmark to get the measured value. |
89 | | fn end(&self, i: Self::Intermediate) -> Self::Value; |
90 | | |
91 | | /// Combine two values. Criterion.rs sometimes needs to perform measurements in multiple batches |
92 | | /// of iterations, so the value from one batch must be added to the sum of the previous batches. |
93 | | fn add(&self, v1: &Self::Value, v2: &Self::Value) -> Self::Value; |
94 | | |
95 | | /// Return a "zero" value for the Value type which can be added to another value. |
96 | | fn zero(&self) -> Self::Value; |
97 | | |
98 | | /// Converts the measured value to f64 so that it can be used in statistical analysis. |
99 | | fn to_f64(&self, value: &Self::Value) -> f64; |
100 | | |
101 | | /// Return a trait-object reference to the value formatter for this measurement. |
102 | | fn formatter(&self) -> &dyn ValueFormatter; |
103 | | } |
104 | | |
105 | | pub(crate) struct DurationFormatter; |
106 | | impl DurationFormatter { |
107 | 0 | fn bytes_per_second(&self, bytes: f64, typical: f64, values: &mut [f64]) -> &'static str { |
108 | 0 | let bytes_per_second = bytes * (1e9 / typical); |
109 | 0 | let (denominator, unit) = if bytes_per_second < 1024.0 { |
110 | 0 | (1.0, " B/s") |
111 | 0 | } else if bytes_per_second < 1024.0 * 1024.0 { |
112 | 0 | (1024.0, "KiB/s") |
113 | 0 | } else if bytes_per_second < 1024.0 * 1024.0 * 1024.0 { |
114 | 0 | (1024.0 * 1024.0, "MiB/s") |
115 | | } else { |
116 | 0 | (1024.0 * 1024.0 * 1024.0, "GiB/s") |
117 | | }; |
118 | | |
119 | 0 | for val in values { |
120 | 0 | let bytes_per_second = bytes * (1e9 / *val); |
121 | 0 | *val = bytes_per_second / denominator; |
122 | 0 | } |
123 | | |
124 | 0 | unit |
125 | 0 | } |
126 | | |
127 | 0 | fn bytes_per_second_decimal( |
128 | 0 | &self, |
129 | 0 | bytes: f64, |
130 | 0 | typical: f64, |
131 | 0 | values: &mut [f64], |
132 | 0 | ) -> &'static str { |
133 | 0 | let bytes_per_second = bytes * (1e9 / typical); |
134 | 0 | let (denominator, unit) = if bytes_per_second < 1000.0 { |
135 | 0 | (1.0, " B/s") |
136 | 0 | } else if bytes_per_second < 1000.0 * 1000.0 { |
137 | 0 | (1000.0, "KB/s") |
138 | 0 | } else if bytes_per_second < 1000.0 * 1000.0 * 1000.0 { |
139 | 0 | (1000.0 * 1000.0, "MB/s") |
140 | | } else { |
141 | 0 | (1000.0 * 1000.0 * 1000.0, "GB/s") |
142 | | }; |
143 | | |
144 | 0 | for val in values { |
145 | 0 | let bytes_per_second = bytes * (1e9 / *val); |
146 | 0 | *val = bytes_per_second / denominator; |
147 | 0 | } |
148 | | |
149 | 0 | unit |
150 | 0 | } |
151 | | |
152 | 0 | fn elements_per_second(&self, elems: f64, typical: f64, values: &mut [f64]) -> &'static str { |
153 | 0 | let elems_per_second = elems * (1e9 / typical); |
154 | 0 | let (denominator, unit) = if elems_per_second < 1000.0 { |
155 | 0 | (1.0, " elem/s") |
156 | 0 | } else if elems_per_second < 1000.0 * 1000.0 { |
157 | 0 | (1000.0, "Kelem/s") |
158 | 0 | } else if elems_per_second < 1000.0 * 1000.0 * 1000.0 { |
159 | 0 | (1000.0 * 1000.0, "Melem/s") |
160 | | } else { |
161 | 0 | (1000.0 * 1000.0 * 1000.0, "Gelem/s") |
162 | | }; |
163 | | |
164 | 0 | for val in values { |
165 | 0 | let elems_per_second = elems * (1e9 / *val); |
166 | 0 | *val = elems_per_second / denominator; |
167 | 0 | } |
168 | | |
169 | 0 | unit |
170 | 0 | } |
171 | | } |
172 | | impl ValueFormatter for DurationFormatter { |
173 | 0 | fn scale_throughputs( |
174 | 0 | &self, |
175 | 0 | typical: f64, |
176 | 0 | throughput: &Throughput, |
177 | 0 | values: &mut [f64], |
178 | 0 | ) -> &'static str { |
179 | 0 | match *throughput { |
180 | 0 | Throughput::Bytes(bytes) => self.bytes_per_second(bytes as f64, typical, values), |
181 | 0 | Throughput::BytesDecimal(bytes) => { |
182 | 0 | self.bytes_per_second_decimal(bytes as f64, typical, values) |
183 | | } |
184 | 0 | Throughput::Elements(elems) => self.elements_per_second(elems as f64, typical, values), |
185 | | } |
186 | 0 | } |
187 | | |
188 | 0 | fn scale_values(&self, ns: f64, values: &mut [f64]) -> &'static str { |
189 | 0 | let (factor, unit) = if ns < 10f64.powi(0) { |
190 | 0 | (10f64.powi(3), "ps") |
191 | 0 | } else if ns < 10f64.powi(3) { |
192 | 0 | (10f64.powi(0), "ns") |
193 | 0 | } else if ns < 10f64.powi(6) { |
194 | 0 | (10f64.powi(-3), "µs") |
195 | 0 | } else if ns < 10f64.powi(9) { |
196 | 0 | (10f64.powi(-6), "ms") |
197 | | } else { |
198 | 0 | (10f64.powi(-9), "s") |
199 | | }; |
200 | | |
201 | 0 | for val in values { |
202 | 0 | *val *= factor; |
203 | 0 | } |
204 | | |
205 | 0 | unit |
206 | 0 | } |
207 | | |
208 | 0 | fn scale_for_machines(&self, _values: &mut [f64]) -> &'static str { |
209 | | // no scaling is needed |
210 | 0 | "ns" |
211 | 0 | } |
212 | | } |
213 | | |
214 | | /// `WallTime` is the default measurement in Criterion.rs. It measures the elapsed time from the |
215 | | /// beginning of a series of iterations to the end. |
216 | | pub struct WallTime; |
217 | | impl Measurement for WallTime { |
218 | | type Intermediate = Instant; |
219 | | type Value = Duration; |
220 | | |
221 | 0 | fn start(&self) -> Self::Intermediate { |
222 | 0 | Instant::now() |
223 | 0 | } |
224 | 0 | fn end(&self, i: Self::Intermediate) -> Self::Value { |
225 | 0 | i.elapsed() |
226 | 0 | } |
227 | 0 | fn add(&self, v1: &Self::Value, v2: &Self::Value) -> Self::Value { |
228 | 0 | *v1 + *v2 |
229 | 0 | } |
230 | 0 | fn zero(&self) -> Self::Value { |
231 | 0 | Duration::from_secs(0) |
232 | 0 | } |
233 | 0 | fn to_f64(&self, val: &Self::Value) -> f64 { |
234 | 0 | val.as_nanos() as f64 |
235 | 0 | } |
236 | 0 | fn formatter(&self) -> &dyn ValueFormatter { |
237 | 0 | &DurationFormatter |
238 | 0 | } |
239 | | } |