Skip to main content

alux_bench/
program.rs

1//! States what a benchmark measures, as a value that names no harness.
2//!
3//! A bench states groups; a group states cases; a case states a subject and the routine measuring
4//! it. Nothing here runs: what a stated bench denotes is read by whoever measures it, in whatever
5//! order and on whatever threads that interpretation wants.
6
7use crate::BenchRounds;
8use core::time::Duration;
9
10/// What one case runs: the rounds of one sample, answering how long they took.
11pub type BenchRoutine<'routine> = Box<dyn FnMut(BenchRounds) -> Duration + 'routine>;
12
13/// What one case runs, when it may be measured beside the cases stated with it.
14pub type BenchSentRoutine = Box<dyn FnMut(BenchRounds) -> Duration + Send>;
15
16/// The cases of one group, and what measuring them at once would cost.
17pub enum BenchCases<'routine> {
18    /// Measured one at a time, which is what a CPU-bound round needs: rounds running beside each
19    /// other contend for cores, and what is measured is the contention.
20    OneAtATime(Vec<(&'static str, BenchRoutine<'routine>)>),
21    /// Measured at the same time, which an IO-bound round allows: time spent blocked costs the
22    /// same blocked beside another round, so the group takes what its longest case takes.
23    Together(Vec<(&'static str, BenchSentRoutine)>),
24}
25
26impl BenchCases<'_> {
27    /// Answers how many cases the group states.
28    pub fn len(&self) -> usize {
29        match self {
30            Self::OneAtATime(cases) => cases.len(),
31            Self::Together(cases) => cases.len(),
32        }
33    }
34
35    /// Answers whether the group states no case.
36    pub fn is_empty(&self) -> bool {
37        self.len() == 0
38    }
39
40    /// Reads the subject of every case, in the order stated.
41    pub fn subjects(&self) -> impl Iterator<Item = &'static str> + '_ {
42        match self {
43            Self::OneAtATime(cases) => Subjects::OneAtATime(cases.iter()),
44            Self::Together(cases) => Subjects::Together(cases.iter()),
45        }
46    }
47}
48
49/// Reads the subjects of either kind of group.
50enum Subjects<'cases, 'routine> {
51    OneAtATime(core::slice::Iter<'cases, (&'static str, BenchRoutine<'routine>)>),
52    Together(core::slice::Iter<'cases, (&'static str, BenchSentRoutine)>),
53}
54
55impl Iterator for Subjects<'_, '_> {
56    type Item = &'static str;
57
58    fn next(&mut self) -> Option<Self::Item> {
59        match self {
60            Self::OneAtATime(cases) => cases.next().map(|(subject, _)| *subject),
61            Self::Together(cases) => cases.next().map(|(subject, _)| *subject),
62        }
63    }
64}
65
66/// One group of a stated bench: what the cases are compared within, and the cases.
67pub struct BenchGroup<'routine> {
68    group: &'static str,
69    cases: BenchCases<'routine>,
70}
71
72impl<'routine> BenchGroup<'routine> {
73    /// States `cases` as one group, named `group`.
74    pub const fn new(group: &'static str, cases: BenchCases<'routine>) -> Self {
75        Self { group, cases }
76    }
77
78    /// Returns what the group is named.
79    pub const fn group(&self) -> &'static str {
80        self.group
81    }
82
83    /// Returns the cases the group states, and how they are measured.
84    pub fn cases(self) -> BenchCases<'routine> {
85        self.cases
86    }
87
88    /// Reads the cases the group states without taking them.
89    pub const fn stated(&self) -> &BenchCases<'routine> {
90        &self.cases
91    }
92}
93
94/// A stated bench: its groups, in the order stated.
95#[derive(Default)]
96pub struct BenchStated<'routine> {
97    groups: Vec<BenchGroup<'routine>>,
98}
99
100impl<'routine> BenchStated<'routine> {
101    /// States a bench of no groups.
102    pub const fn new() -> Self {
103        Self { groups: Vec::new() }
104    }
105
106    /// States one group after the groups already stated.
107    #[must_use]
108    pub fn group(mut self, group: BenchGroup<'routine>) -> Self {
109        self.groups.push(group);
110
111        self
112    }
113
114    /// States the groups of `next` after the groups of this bench.
115    #[must_use]
116    pub fn then(mut self, next: Self) -> Self {
117        self.groups.extend(next.groups);
118
119        self
120    }
121
122    /// Reads the groups the bench states, in the order stated.
123    pub fn groups(&self) -> impl Iterator<Item = &BenchGroup<'routine>> {
124        self.groups.iter()
125    }
126
127    /// Separates the groups from the bench that states them.
128    pub fn into_groups(self) -> impl Iterator<Item = BenchGroup<'routine>> {
129        self.groups.into_iter()
130    }
131}