diff options
author | Aria <me@aria.rip> | 2023-10-31 16:04:51 +0000 |
---|---|---|
committer | Aria <me@aria.rip> | 2023-10-31 16:04:51 +0000 |
commit | 81917b800b206621e39a4d83b347e7b31d717645 (patch) | |
tree | 229540a2f4a6111aff6bbf1ff575e0307c94f1c9 | |
parent | 8a2b8dbc30e32c4112088b67c1d39f55175439a4 (diff) |
feat(benchmarker): cleaner interface for running benchmarks
6 files changed, 127 insertions, 101 deletions
diff --git a/primrose/crates/candelabra-benchmarker/examples/run_vec.rs b/primrose/crates/candelabra-benchmarker/examples/run_vec.rs index 677a538..5dd60da 100644 --- a/primrose/crates/candelabra-benchmarker/examples/run_vec.rs +++ b/primrose/crates/candelabra-benchmarker/examples/run_vec.rs @@ -1,9 +1,13 @@ -use candelabra_benchmarker::{ContainerExt, IndexableExt}; +use candelabra_benchmarker::Benchmarker; const NS: [usize; 3] = [1, 10, 100]; fn main() { env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("debug")).init(); - dbg!(Vec::<usize>::benchmark_container(&NS).merge(Vec::<usize>::benchmark_indexable(&NS))); + dbg!(Benchmarker::<Vec<usize>, usize>::with_ns(&NS) + .container() + .indexable() + .stack() + .finish()); } diff --git a/primrose/crates/candelabra-benchmarker/src/container.rs b/primrose/crates/candelabra-benchmarker/src/container.rs index 1f5eb45..f32594f 100644 --- a/primrose/crates/candelabra-benchmarker/src/container.rs +++ b/primrose/crates/candelabra-benchmarker/src/container.rs @@ -4,12 +4,10 @@ use log::debug; use primrose_library::traits::Container; use rand::{distributions::Standard, prelude::Distribution, random, thread_rng, Rng}; -use crate::{bench::benchmark_op, Results, RunResults, SingleNResults}; +use crate::{bench::benchmark_op, RunResults, SingleNResults}; +/// Benchmark [`primrose_library::traits::Container`] operations pub trait ContainerExt<E> { - /// Benchmark at the given `ns`. - fn benchmark_container(ns: &[usize]) -> Results; - /// Benchmark at a single `n`. fn benchmark_container_at(n: usize) -> SingleNResults; @@ -35,21 +33,6 @@ where E: Clone, Standard: Distribution<E>, { - fn benchmark_container(ns: &[usize]) -> Results { - debug!( - "Benchmarking {} at {} different n values", - type_name::<T>(), - ns.len() - ); - - let mut by_n = HashMap::new(); - for n in ns { - by_n.insert(*n, Self::benchmark_container_at(*n)); - } - - Results { by_n } - } - fn benchmark_container_at(n: usize) -> SingleNResults { let mut by_op = HashMap::new(); diff --git a/primrose/crates/candelabra-benchmarker/src/indexable.rs b/primrose/crates/candelabra-benchmarker/src/indexable.rs index 122ec58..915d093 100644 --- a/primrose/crates/candelabra-benchmarker/src/indexable.rs +++ b/primrose/crates/candelabra-benchmarker/src/indexable.rs @@ -6,10 +6,8 @@ use rand::{distributions::Standard, prelude::Distribution, random}; use crate::{benchmark_op, Results, RunResults, SingleNResults}; +/// Benchmark [`primrose_library::traits::Indexable`] operations pub trait IndexableExt<E> { - /// Benchmark at the given `ns`. - fn benchmark_indexable(ns: &[usize]) -> Results; - /// Benchmark at a single `n`. fn benchmark_indexable_at(n: usize) -> SingleNResults; @@ -28,21 +26,6 @@ where T: Container<E> + Indexable<E> + Default, Standard: Distribution<E>, { - fn benchmark_indexable(ns: &[usize]) -> Results { - debug!( - "Benchmarking {} at {} different n values", - type_name::<T>(), - ns.len() - ); - - let mut by_n = HashMap::new(); - for n in ns { - by_n.insert(*n, Self::benchmark_indexable_at(*n)); - } - - Results { by_n } - } - fn benchmark_indexable_at(n: usize) -> SingleNResults { let mut by_op = HashMap::new(); diff --git a/primrose/crates/candelabra-benchmarker/src/lib.rs b/primrose/crates/candelabra-benchmarker/src/lib.rs index 1e46af4..3361048 100644 --- a/primrose/crates/candelabra-benchmarker/src/lib.rs +++ b/primrose/crates/candelabra-benchmarker/src/lib.rs @@ -1,6 +1,6 @@ -use std::{collections::HashMap, time::Duration}; - mod bench; +use std::{collections::HashMap, marker::PhantomData}; + pub use bench::benchmark_op; mod container; @@ -12,60 +12,66 @@ pub use indexable::IndexableExt; mod stack; pub use stack::StackExt; -/// Results for a whole suite of benchmarks -#[derive(Debug, Clone)] -pub struct Results { - /// Results for each collection size tested with - pub by_n: HashMap<usize, SingleNResults>, -} +mod results; +pub use results::*; -/// Name of an operation -pub type OpName = &'static str; +/// Runs benchmarks at varying `n`s with a builder-style interface. +/// +/// This mostly just makes our code generation easier. +pub struct Benchmarker<T, E>(&'static [usize], Results, PhantomData<(T, E)>); +impl<T, E> Benchmarker<T, E> { + /// Create a benchmarker that will repeat all benchmarks with each of the given n values. + pub fn with_ns(ns: &'static [usize]) -> Self { + Self( + ns, + Results { + by_n: HashMap::new(), + }, + PhantomData::default(), + ) + } -/// Results for operations all ran at the same container size -#[derive(Debug, Clone)] -pub struct SingleNResults { - /// Results for each operation - pub by_op: HashMap<OpName, RunResults>, + /// Finish benchmarking and get the results + pub fn finish(self) -> Results { + self.1 + } } -/// Results for a single benchmark -#[derive(Debug, Clone)] -pub struct RunResults { - /// Number of times the benchmark was run - pub times: usize, - - /// The minimum time taken - pub min: Duration, - - /// The maximum time taken - pub max: Duration, +impl<T, E> Benchmarker<T, E> +where + T: ContainerExt<E>, +{ + /// Run benchmarks for [`primrose_library::traits::Container`] operations. + pub fn container(mut self) -> Self { + for n in self.0 { + self.1.add(*n, T::benchmark_container_at(*n)); + } - /// The average (mean) time taken - pub avg: Duration, + self + } } - -impl Results { - pub fn merge(&mut self, b: Self) -> &mut Self { - for (n, res_b) in b.by_n { - self.by_n - .entry(n) - .and_modify(|res_a| { - res_a.merge(res_b.clone()); - }) - .or_insert(res_b); +impl<T, E> Benchmarker<T, E> +where + T: IndexableExt<E>, +{ + /// Run benchmarks for [`primrose_library::traits::Indexable`] operations. + pub fn indexable(mut self) -> Self { + for n in self.0 { + self.1.add(*n, T::benchmark_indexable_at(*n)); } self } } -impl SingleNResults { - /// Merge results from `b` into these results. - /// If `b` contains benchmarks for operations we have a result for, results from `b` are preferred. - pub fn merge(&mut self, b: Self) -> &mut Self { - for (name, res_b) in b.by_op { - self.by_op.insert(name, res_b); +impl<T, E> Benchmarker<T, E> +where + T: StackExt<E>, +{ + /// Run benchmarks for [`primrose_library::traits::Stack`] operations. + pub fn stack(mut self) -> Self { + for n in self.0 { + self.1.add(*n, T::benchmark_stack_at(*n)); } self diff --git a/primrose/crates/candelabra-benchmarker/src/results.rs b/primrose/crates/candelabra-benchmarker/src/results.rs new file mode 100644 index 0000000..835a6e7 --- /dev/null +++ b/primrose/crates/candelabra-benchmarker/src/results.rs @@ -0,0 +1,66 @@ +use std::{collections::HashMap, time::Duration}; + +/// Results for a whole suite of benchmarks +#[derive(Debug, Clone)] +pub struct Results { + /// Results for each collection size tested with + pub by_n: HashMap<usize, SingleNResults>, +} + +/// Name of an operation +pub type OpName = &'static str; + +/// Results for operations all ran at the same container size +#[derive(Debug, Clone)] +pub struct SingleNResults { + /// Results for each operation + pub by_op: HashMap<OpName, RunResults>, +} + +/// Results for a single benchmark +#[derive(Debug, Clone)] +pub struct RunResults { + /// Number of times the benchmark was run + pub times: usize, + + /// The minimum time taken + pub min: Duration, + + /// The maximum time taken + pub max: Duration, + + /// The average (mean) time taken + pub avg: Duration, +} + +impl Results { + pub fn add(&mut self, n: usize, b: SingleNResults) -> &mut Self { + self.by_n + .entry(n) + .and_modify(|res_a| { + res_a.merge(b.clone()); + }) + .or_insert(b); + + self + } + pub fn merge(&mut self, b: Self) -> &mut Self { + for (n, res_b) in b.by_n { + self.add(n, res_b); + } + + self + } +} + +impl SingleNResults { + /// Merge results from `b` into these results. + /// If `b` contains benchmarks for operations we have a result for, results from `b` are preferred. + pub fn merge(&mut self, b: Self) -> &mut Self { + for (name, res_b) in b.by_op { + self.by_op.insert(name, res_b); + } + + self + } +} diff --git a/primrose/crates/candelabra-benchmarker/src/stack.rs b/primrose/crates/candelabra-benchmarker/src/stack.rs index 4010cae..f6b32f0 100644 --- a/primrose/crates/candelabra-benchmarker/src/stack.rs +++ b/primrose/crates/candelabra-benchmarker/src/stack.rs @@ -6,10 +6,8 @@ use rand::{distributions::Standard, prelude::Distribution, random}; use crate::{benchmark_op, Results, RunResults, SingleNResults}; +/// Benchmark [`primrose_library::traits::Stack`] operations pub trait StackExt<E> { - /// Benchmark at the given `ns`. - fn benchmark_stack(ns: &[usize]) -> Results; - /// Benchmark at a single `n`. fn benchmark_stack_at(n: usize) -> SingleNResults; @@ -19,26 +17,12 @@ pub trait StackExt<E> { /// Benchmark `pop` at a single `n`. fn benchmark_stack_pop(n: usize) -> RunResults; } + impl<T, E> StackExt<E> for T where T: Stack<E> + Default, Standard: Distribution<E>, { - fn benchmark_stack(ns: &[usize]) -> Results { - debug!( - "Benchmarking {} at {} different n values", - type_name::<T>(), - ns.len() - ); - - let mut by_n = HashMap::new(); - for n in ns { - by_n.insert(*n, Self::benchmark_stack_at(*n)); - } - - Results { by_n } - } - fn benchmark_stack_at(n: usize) -> SingleNResults { let mut by_op = HashMap::new(); |