criterion_decimal_throughput/
lib.rs1#![warn(missing_docs)]
75#![warn(rustdoc::missing_crate_level_docs)]
76#![warn(
77 explicit_outlives_requirements,
78 unreachable_pub,
79 semicolon_in_expressions_from_macros,
80 unused_import_braces,
81 unused_lifetimes
82)]
83
84use criterion::{
85 measurement::{Measurement, ValueFormatter, WallTime},
86 Throughput,
87};
88
89pub struct DecimalByteMeasurement(WallTime);
91
92pub type Criterion = criterion::Criterion<DecimalByteMeasurement>;
94
95pub fn decimal_byte_measurement() -> Criterion {
97 criterion::Criterion::default().with_measurement(DecimalByteMeasurement::new())
98}
99
100impl Default for DecimalByteMeasurement {
101 fn default() -> Self {
102 Self::new()
103 }
104}
105
106impl DecimalByteMeasurement {
107 pub fn new() -> Self {
109 DecimalByteMeasurement(WallTime)
110 }
111}
112
113impl Measurement for DecimalByteMeasurement {
114 type Intermediate = <WallTime as Measurement>::Intermediate;
115
116 type Value = <WallTime as Measurement>::Value;
117
118 fn start(&self) -> Self::Intermediate {
119 self.0.start()
120 }
121
122 fn end(&self, i: Self::Intermediate) -> Self::Value {
123 self.0.end(i)
124 }
125
126 fn add(&self, v1: &Self::Value, v2: &Self::Value) -> Self::Value {
127 self.0.add(v1, v2)
128 }
129
130 fn zero(&self) -> Self::Value {
131 self.0.zero()
132 }
133
134 fn to_f64(&self, value: &Self::Value) -> f64 {
135 self.0.to_f64(value)
136 }
137
138 fn formatter(&self) -> &dyn ValueFormatter {
139 self
140 }
141}
142
143#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
144enum Multiple {
145 One,
146 Kilo,
147 Mega,
148 Giga,
149 Tera,
150}
151
152#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
153enum Unit {
154 Byte,
155 Elem,
156}
157
158impl Multiple {
159 fn denominator(&self) -> f64 {
160 match *self {
161 Multiple::One => 1.0,
162 Multiple::Kilo => 1_000.0,
163 Multiple::Mega => 1_000_000.0,
164 Multiple::Giga => 1_000_000_000.0,
165 Multiple::Tera => 1_000_000_000_000.0,
166 }
167 }
168}
169
170impl ValueFormatter for DecimalByteMeasurement {
171 fn scale_values(&self, typical_value: f64, values: &mut [f64]) -> &'static str {
172 self.0.formatter().scale_values(typical_value, values)
173 }
174
175 fn scale_throughputs(
176 &self,
177 typical_value: f64,
178 throughput: &criterion::Throughput,
179 values: &mut [f64],
180 ) -> &'static str {
181 use Multiple::*;
182 use Throughput::*;
183 use Unit::*;
184
185 let (total_units, unit) = match *throughput {
186 Bytes(bytes) => (bytes as f64, Byte),
187 Elements(elements) => (elements as f64, Elem),
188 };
189 let units_per_second = total_units * (1e9 / typical_value);
190 let multiple = if units_per_second >= 1e12 {
191 Tera
192 } else if units_per_second >= 1e9 {
193 Giga
194 } else if units_per_second >= 1e6 {
195 Mega
196 } else if units_per_second >= 1e3 {
197 Kilo
198 } else {
199 One
200 };
201 let denominator = multiple.denominator();
202
203 for val in values {
204 let units_per_second = total_units * (1e9 / *val);
205 *val = units_per_second / denominator;
206 }
207
208 match (unit, multiple) {
209 (Byte, One) => " B/s",
210 (Byte, Kilo) => "KB/s",
211 (Byte, Mega) => "MB/s",
212 (Byte, Giga) => "GB/s",
213 (Byte, Tera) => "TB/s",
214 (Elem, One) => " elem/s",
215 (Elem, Kilo) => "Kelem/s",
216 (Elem, Mega) => "Melem/s",
217 (Elem, Giga) => "Gelem/s",
218 (Elem, Tera) => "Telem/s",
219 }
220 }
221
222 fn scale_for_machines(&self, values: &mut [f64]) -> &'static str {
223 self.0.formatter().scale_for_machines(values)
224 }
225}
226
227#[cfg(test)]
228mod test {
229 use super::*;
230 use proptest::prelude::*;
231 use Target::*;
232
233 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
234 enum Target {
235 One,
236 Kilo,
237 Mega,
238 Giga,
239 Tera,
240 }
241
242 impl Target {
243 fn get_base(self) -> f64 {
244 match self {
245 One => 1.0,
246 Kilo => 1e3,
247 Mega => 1e6,
248 Giga => 1e9,
249 Tera => 1e12,
250 }
251 }
252
253 fn expected_bytes(self) -> &'static str {
254 match self {
255 One => " B/s",
256 Kilo => "KB/s",
257 Mega => "MB/s",
258 Giga => "GB/s",
259 Tera => "TB/s",
260 }
261 }
262
263 fn expected_elems(self) -> &'static str {
264 match self {
265 One => " elem/s",
266 Kilo => "Kelem/s",
267 Mega => "Melem/s",
268 Giga => "Gelem/s",
269 Tera => "Telem/s",
270 }
271 }
272 }
273
274 fn arbitrary_target() -> impl Strategy<Value = Target> {
275 prop_oneof![Just(One), Just(Kilo), Just(Mega), Just(Giga), Just(Tera)]
276 }
277
278 proptest! {
279 #[test]
280 fn scale_throughputs_bytes_gives_correct_unit(target in arbitrary_target(), bytes in any::<u64>()) {
281 let thpt_config = Throughput::Bytes(bytes);
284 let seconds = (bytes as f64) / target.get_base();
285 let typical = (seconds * 1e9) * 0.999999;
286
287 let measurement = DecimalByteMeasurement::default();
288 let result = measurement.scale_throughputs(typical, &thpt_config, &mut []);
289
290 assert_eq!(result, target.expected_bytes());
291 }
292
293 #[test]
294 fn scale_throughputs_elems_gives_correct_unit(target in arbitrary_target(), elems in any::<u64>()) {
295 let thpt_config = Throughput::Elements(elems);
298 let seconds = (elems as f64) / target.get_base();
299 let typical = (seconds * 1e9) * 0.999999;
300
301 let measurement = DecimalByteMeasurement::default();
302 let result = measurement.scale_throughputs(typical, &thpt_config, &mut []);
303
304 assert_eq!(result, target.expected_elems());
305 }
306 }
307
308 #[test]
309 fn scale_throughputs_bytes() {
310 let thpt_config = Throughput::Bytes(1_000_000);
311 let typical = 1_000_000_000.0;
312 let mut values = [
313 100_000_000.0,
314 500_000_000.0,
315 999_999_999.0,
316 1_000_000_000.0,
317 1_000_000_001.0,
318 2_000_000_000.0,
319 10_000_000_000.0,
320 ];
321
322 let measurement = DecimalByteMeasurement::default();
323 let result = measurement.scale_throughputs(typical, &thpt_config, &mut values);
324
325 assert_eq!(result, "MB/s");
326 assert_eq!(values, [10.0, 2.0, 1.000000001, 1.0, 0.999999999, 0.5, 0.1]);
327 }
328
329 #[test]
330 fn scale_throughputs_elems_gives_correct_unit_regression1() {
331 let elems = 13302377187617527;
332 let target = Mega;
333
334 let thpt_config = Throughput::Elements(elems);
335 let seconds = (elems as f64) / target.get_base();
336 let typical = (seconds * 1e9) * 0.999999;
337
338 let measurement = DecimalByteMeasurement::default();
339 let result = measurement.scale_throughputs(typical, &thpt_config, &mut []);
340
341 assert_eq!(result, target.expected_elems());
342 }
343}