Deprecated: The each() function is deprecated. This message will be suppressed on further calls in /home/zhenxiangba/zhenxiangba.com/public_html/phproxy-improved-master/index.php on line 456
measurement.rs - source
[go: Go Back, main page]

criterion/
measurement.rs

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