use crate::{BenchCases, BenchGroup, BenchRounds, BenchRoutine, BenchSentRoutine, BenchStated};
use alux_ext::ext;
use core::time::Duration;
pub trait BenchAlg {
type Bench;
fn nothing(&self) -> Self::Bench;
fn group(&self, group: &'static str, cases: BenchCases<'static>) -> Self::Bench;
fn then(&self, first: Self::Bench, next: Self::Bench) -> Self::Bench;
}
pub trait MeasureBenchAlg {
type Bench;
fn measure(&mut self, bench: Self::Bench);
}
#[ext(name = BenchExt)]
pub impl<This> This
where
This: BenchAlg,
{
fn one_at_a_time<Stated, Routine>(&self, group: &'static str, cases: Stated) -> This::Bench
where
Stated: IntoIterator<Item = (&'static str, Routine)>,
Routine: FnMut(BenchRounds) -> Duration + 'static,
{
let cases = cases.into_iter().map(|(subject, routine)| (subject, Box::new(routine) as BenchRoutine<'static>));
self.group(group, BenchCases::OneAtATime(cases.collect()))
}
fn together<Stated, Routine>(&self, group: &'static str, cases: Stated) -> This::Bench
where
Stated: IntoIterator<Item = (&'static str, Routine)>,
Routine: FnMut(BenchRounds) -> Duration + Send + 'static,
{
let cases = cases.into_iter().map(|(subject, routine)| (subject, Box::new(routine) as BenchSentRoutine));
self.group(group, BenchCases::Together(cases.collect()))
}
fn benches<Stated>(&self, stated: Stated) -> This::Bench
where
Stated: IntoIterator<Item = This::Bench>,
{
stated.into_iter().fold(self.nothing(), |bench, next| self.then(bench, next))
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct StatingBench;
impl BenchAlg for StatingBench {
type Bench = BenchStated<'static>;
fn nothing(&self) -> Self::Bench {
BenchStated::new()
}
fn group(&self, group: &'static str, cases: BenchCases<'static>) -> Self::Bench {
BenchStated::new().group(BenchGroup::new(group, cases))
}
fn then(&self, first: Self::Bench, next: Self::Bench) -> Self::Bench {
first.then(next)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::BenchRounds;
use core::time::Duration;
fn case(subject: &'static str) -> (&'static str, BenchRoutine<'static>) {
(subject, Box::new(|rounds: BenchRounds| Duration::from_millis(rounds.count())))
}
#[test]
fn states_groups_in_the_order_stated() {
let stating = StatingBench;
let bench = stating.benches([
stating.one_at_a_time("close", [case("hyper"), case("axum")]),
stating.together("end", [("poem", Box::new(|_| Duration::from_secs(1)) as BenchSentRoutine)]),
]);
let groups = bench.groups().map(|group| (group.group(), group.stated().len())).collect::<Vec<_>>();
assert_eq!(groups, [("close", 2), ("end", 1)]);
}
#[test]
fn states_what_measuring_a_group_at_once_would_cost() {
let stating = StatingBench;
let bench = stating.one_at_a_time("close", [case("hyper")]);
let stated = bench.groups().next().expect("the group stated");
assert!(matches!(stated.stated(), BenchCases::OneAtATime(_)));
assert_eq!(stated.stated().subjects().collect::<Vec<_>>(), ["hyper"]);
}
}