1 | //! Count values processed in each iteration to measure throughput. |
2 | //! |
3 | //! # Examples |
4 | //! |
5 | //! The following example measures throughput of converting |
6 | //! [`&[i32]`](prim@slice) into [`Vec<i32>`](Vec) by providing [`BytesCount`] |
7 | //! via [`Bencher::counter`](crate::Bencher::counter): |
8 | //! |
9 | //! ``` |
10 | //! use divan::counter::BytesCount; |
11 | //! |
12 | //! #[divan::bench] |
13 | //! fn slice_into_vec(bencher: divan::Bencher) { |
14 | //! let ints: &[i32] = &[ |
15 | //! // ... |
16 | //! ]; |
17 | //! |
18 | //! let bytes = BytesCount::of_slice(ints); |
19 | //! |
20 | //! bencher |
21 | //! .counter(bytes) |
22 | //! .bench(|| -> Vec<i32> { |
23 | //! divan::black_box(ints).into() |
24 | //! }); |
25 | //! } |
26 | //! ``` |
27 | |
28 | use std::any::Any; |
29 | |
30 | mod any_counter; |
31 | mod collection; |
32 | mod into_counter; |
33 | mod sealed; |
34 | mod uint; |
35 | |
36 | pub(crate) use self::{ |
37 | any_counter::{AnyCounter, KnownCounterKind}, |
38 | collection::{CounterCollection, CounterSet}, |
39 | sealed::Sealed, |
40 | uint::{AsCountUInt, CountUInt, MaxCountUInt}, |
41 | }; |
42 | pub use into_counter::IntoCounter; |
43 | |
44 | /// Counts the number of values processed in each iteration of a benchmarked |
45 | /// function. |
46 | /// |
47 | /// This is used via: |
48 | /// - [`#[divan::bench(counters = ...)]`](macro@crate::bench#counters) |
49 | /// - [`#[divan::bench_group(counters = ...)]`](macro@crate::bench_group#counters) |
50 | /// - [`Bencher::counter`](crate::Bencher::counter) |
51 | /// - [`Bencher::input_counter`](crate::Bencher::input_counter) |
52 | #[doc (alias = "throughput" )] |
53 | pub trait Counter: Sized + Any + Sealed {} |
54 | |
55 | /// Process N bytes. |
56 | #[derive (Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] |
57 | pub struct BytesCount { |
58 | count: MaxCountUInt, |
59 | } |
60 | |
61 | /// Process N [`char`s](char). |
62 | /// |
63 | /// This is beneficial when comparing benchmarks between ASCII and Unicode |
64 | /// implementations, since the number of code points is a common baseline |
65 | /// reference. |
66 | #[derive (Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] |
67 | pub struct CharsCount { |
68 | count: MaxCountUInt, |
69 | } |
70 | |
71 | /// Process N cycles, displayed as Hertz. |
72 | /// |
73 | /// This value is user-provided and does not necessarily correspond to the CPU's |
74 | /// cycle frequency, so it may represent cycles of anything appropriate for the |
75 | /// benchmarking context. |
76 | #[derive (Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] |
77 | pub struct CyclesCount { |
78 | count: MaxCountUInt, |
79 | } |
80 | |
81 | /// Process N items. |
82 | #[derive (Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] |
83 | pub struct ItemsCount { |
84 | count: MaxCountUInt, |
85 | } |
86 | |
87 | impl Sealed for BytesCount {} |
88 | impl Sealed for CharsCount {} |
89 | impl Sealed for CyclesCount {} |
90 | impl Sealed for ItemsCount {} |
91 | |
92 | impl Counter for BytesCount {} |
93 | impl Counter for CharsCount {} |
94 | impl Counter for CyclesCount {} |
95 | impl Counter for ItemsCount {} |
96 | |
97 | impl<C: AsCountUInt> From<C> for BytesCount { |
98 | #[inline ] |
99 | fn from(count: C) -> Self { |
100 | Self::new(count.as_max_uint()) |
101 | } |
102 | } |
103 | |
104 | impl<C: AsCountUInt> From<C> for CharsCount { |
105 | #[inline ] |
106 | fn from(count: C) -> Self { |
107 | Self::new(count.as_max_uint()) |
108 | } |
109 | } |
110 | |
111 | impl<C: AsCountUInt> From<C> for CyclesCount { |
112 | #[inline ] |
113 | fn from(count: C) -> Self { |
114 | Self::new(count.as_max_uint()) |
115 | } |
116 | } |
117 | |
118 | impl<C: AsCountUInt> From<C> for ItemsCount { |
119 | #[inline ] |
120 | fn from(count: C) -> Self { |
121 | Self::new(count.as_max_uint()) |
122 | } |
123 | } |
124 | |
125 | impl BytesCount { |
126 | /// Count N bytes. |
127 | #[inline ] |
128 | pub fn new<N: CountUInt>(count: N) -> Self { |
129 | Self { count: count.into_max_uint() } |
130 | } |
131 | |
132 | /// Counts the size of a type with [`size_of`]. |
133 | #[inline ] |
134 | #[doc (alias = "size_of" )] |
135 | pub const fn of<T>() -> Self { |
136 | Self { count: size_of::<T>() as MaxCountUInt } |
137 | } |
138 | |
139 | /// Counts the size of multiple instances of a type with [`size_of`]. |
140 | #[inline ] |
141 | #[doc (alias = "size_of" )] |
142 | pub const fn of_many<T>(n: usize) -> Self { |
143 | match (size_of::<T>() as MaxCountUInt).checked_mul(n as MaxCountUInt) { |
144 | Some(count) => Self { count }, |
145 | None => panic!("overflow" ), |
146 | } |
147 | } |
148 | |
149 | /// Counts the size of a value with [`size_of_val`]. |
150 | #[inline ] |
151 | #[doc (alias = "size_of_val" )] |
152 | pub fn of_val<T: ?Sized>(val: &T) -> Self { |
153 | // TODO: Make const, https://github.com/rust-lang/rust/issues/46571 |
154 | Self { count: size_of_val(val) as MaxCountUInt } |
155 | } |
156 | |
157 | /// Counts the bytes of [`Iterator::Item`s](Iterator::Item). |
158 | #[inline ] |
159 | pub fn of_iter<T, I>(iter: I) -> Self |
160 | where |
161 | I: IntoIterator<Item = T>, |
162 | { |
163 | Self::of_many::<T>(iter.into_iter().count()) |
164 | } |
165 | |
166 | /// Counts the bytes of a [`&str`]. |
167 | /// |
168 | /// This is like [`BytesCount::of_val`] with the convenience of behaving as |
169 | /// expected for [`&String`](String) and other types that convert to |
170 | /// [`&str`]. |
171 | /// |
172 | /// [`&str`]: prim@str |
173 | #[inline ] |
174 | pub fn of_str<S: ?Sized + AsRef<str>>(s: &S) -> Self { |
175 | Self::of_val(s.as_ref()) |
176 | } |
177 | |
178 | /// Counts the bytes of a [slice](prim@slice). |
179 | /// |
180 | /// This is like [`BytesCount::of_val`] with the convenience of behaving as |
181 | /// expected for [`&Vec<T>`](Vec) and other types that convert to |
182 | /// [`&[T]`](prim@slice). |
183 | #[inline ] |
184 | pub fn of_slice<T, S: ?Sized + AsRef<[T]>>(s: &S) -> Self { |
185 | Self::of_val(s.as_ref()) |
186 | } |
187 | } |
188 | |
189 | macro_rules! type_bytes { |
190 | ($ty:ident) => { |
191 | /// Counts the bytes of multiple |
192 | #[doc = concat!("[`" , stringify!($ty), "`s](" , stringify!($ty), ")." )] |
193 | #[inline] |
194 | pub const fn $ty(n: usize) -> Self { |
195 | Self::of_many::<$ty>(n) |
196 | } |
197 | }; |
198 | } |
199 | |
200 | /// Count bytes of multiple values. |
201 | impl BytesCount { |
202 | type_bytes!(f32); |
203 | type_bytes!(f64); |
204 | |
205 | type_bytes!(i8); |
206 | type_bytes!(u8); |
207 | type_bytes!(i16); |
208 | type_bytes!(u16); |
209 | type_bytes!(i32); |
210 | type_bytes!(u32); |
211 | type_bytes!(i64); |
212 | type_bytes!(u64); |
213 | type_bytes!(i128); |
214 | type_bytes!(u128); |
215 | type_bytes!(isize); |
216 | type_bytes!(usize); |
217 | } |
218 | |
219 | impl CharsCount { |
220 | /// Count N [`char`s](char). |
221 | #[inline ] |
222 | pub fn new<N: CountUInt>(count: N) -> Self { |
223 | Self { count: count.into_max_uint() } |
224 | } |
225 | |
226 | /// Counts the [`char`s](prim@char) of a [`&str`](prim@str). |
227 | #[inline ] |
228 | pub fn of_str<S: ?Sized + AsRef<str>>(s: &S) -> Self { |
229 | Self::new(s.as_ref().chars().count()) |
230 | } |
231 | } |
232 | |
233 | impl CyclesCount { |
234 | /// Count N cycles. |
235 | #[inline ] |
236 | pub fn new<N: CountUInt>(count: N) -> Self { |
237 | Self { count: count.into_max_uint() } |
238 | } |
239 | } |
240 | |
241 | impl ItemsCount { |
242 | /// Count N items. |
243 | #[inline ] |
244 | pub fn new<N: CountUInt>(count: N) -> Self { |
245 | Self { count: count.into_max_uint() } |
246 | } |
247 | |
248 | /// Counts [`Iterator::Item`s](Iterator::Item). |
249 | #[inline ] |
250 | pub fn of_iter<T, I>(iter: I) -> Self |
251 | where |
252 | I: IntoIterator<Item = T>, |
253 | { |
254 | Self::new(iter.into_iter().count()) |
255 | } |
256 | } |
257 | |
258 | /// The numerical base for [`BytesCount`] in benchmark outputs. |
259 | /// |
260 | /// See [`Divan::bytes_format`](crate::Divan::bytes_format) for more info. |
261 | #[derive (Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)] |
262 | #[non_exhaustive ] |
263 | pub enum BytesFormat { |
264 | /// Powers of 1000, starting with KB (kilobyte). This is the default. |
265 | #[default] |
266 | Decimal, |
267 | |
268 | /// Powers of 1024, starting with KiB (kibibyte). |
269 | Binary, |
270 | } |
271 | |
272 | /// Private `BytesFormat` that prevents leaking trait implementations we don't |
273 | /// want to publicly commit to. |
274 | #[derive (Clone, Copy)] |
275 | pub(crate) struct PrivBytesFormat(pub BytesFormat); |
276 | |
277 | impl clap::ValueEnum for PrivBytesFormat { |
278 | fn value_variants<'a>() -> &'a [Self] { |
279 | &[Self(BytesFormat::Decimal), Self(BytesFormat::Binary)] |
280 | } |
281 | |
282 | fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> { |
283 | let name: &'static str = match self.0 { |
284 | BytesFormat::Decimal => "decimal" , |
285 | BytesFormat::Binary => "binary" , |
286 | }; |
287 | Some(clap::builder::PossibleValue::new(name)) |
288 | } |
289 | } |
290 | |
291 | #[cfg (test)] |
292 | mod tests { |
293 | use super::*; |
294 | |
295 | mod bytes_count { |
296 | use super::*; |
297 | |
298 | #[test ] |
299 | fn of_iter() { |
300 | assert_eq!(BytesCount::of_iter::<i32, _>([1, 2, 3]), BytesCount::of_slice(&[1, 2, 3])); |
301 | } |
302 | } |
303 | } |
304 | |