Coverage Report

Created: 2026-02-14 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}