diff --git a/src/bench.rs b/src/bench.rs index 89a0631..e0e46b4 100644 --- a/src/bench.rs +++ b/src/bench.rs @@ -213,7 +213,9 @@ impl<'a, I, O> NamedBench<'a, I, O> { num_iter: usize, events: &mut EventManager, ) -> RunResult { - events.emit(BingganEvents::BenchStart(&self.bench_id)); + events.emit(BingganEvents::BenchStart { + bench_id: &self.bench_id, + }); let start = std::time::Instant::now(); let mut res = None; for _ in 0..num_iter { @@ -222,10 +224,10 @@ impl<'a, I, O> NamedBench<'a, I, O> { let elapsed = start.elapsed(); let run_result = RunResult::new(elapsed.as_nanos() as u64 / num_iter as u64, res); - events.emit(BingganEvents::BenchStop( - &self.bench_id, - run_result.duration_ns, - )); + events.emit(BingganEvents::BenchStop { + bench_id: &self.bench_id, + duration: run_result.duration_ns, + }); run_result } } diff --git a/src/bench_runner.rs b/src/bench_runner.rs index a343338..2a4658f 100644 --- a/src/bench_runner.rs +++ b/src/bench_runner.rs @@ -53,7 +53,7 @@ impl BenchRunner { } /// Returns the event manager, which can be used to add listeners to the benchmarks. - /// See [EventManager] for more information. + /// See [crate::plugins::EventManager] for more information. pub fn get_event_manager(&mut self) -> &mut EventManager { &mut self.listeners } @@ -159,8 +159,12 @@ impl BenchRunner { } } + self.listeners.emit(BingganEvents::GroupStart { + runner_name: self.name.as_deref(), + group_name, + output_value_column_title, + }); if let Some(name) = &group_name { - self.listeners.emit(BingganEvents::GroupStart(name)); println!("{}", name.black().on_yellow().invert().bold()); } @@ -191,6 +195,7 @@ impl BenchRunner { } report_group( + self.name.as_deref(), group_name, group, &*self.reporter, @@ -327,7 +332,7 @@ fn round_up(num: u64) -> u64 { divisor *= 10; } - num.div_ceil(divisor) + num.div_ceil(divisor) * divisor } pub fn minmax(mut vals: I) -> Option<(T, T)> diff --git a/src/lib.rs b/src/lib.rs index e5f93df..fd0276a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,7 @@ //! - **[BenchRunner]**: A main runner for. Useful for single benchmarks or to create groups. //! - **[InputGroup]**: Use this when running a group of benchmarks with the same inputs, where ownership of inputs can be transferred. //! -//! Otherwise if you need more flexibility you can use [BenchGroup] via [BenchRunner::new_group_with_name](crate::BenchRunner::new_group_with_name). +//! Otherwise if you need more flexibility you can use [BenchGroup] via [BenchRunner::new_group](crate::BenchRunner::new). //! //! See for examples. `benches/bench_group.rs` and //! `benches/bench_input_group.rs` are different ways to produce the same output. @@ -166,7 +166,8 @@ pub(crate) mod bench; pub(crate) mod bench_id; pub(crate) mod bench_runner; pub(crate) mod output_value; -pub(crate) mod plugins; +/// The module to define custom plugins +pub mod plugins; pub use output_value::OutputValue; /// The module to report benchmark results pub mod report; diff --git a/src/plugins/alloc.rs b/src/plugins/alloc.rs index 9936e99..f710a27 100644 --- a/src/plugins/alloc.rs +++ b/src/plugins/alloc.rs @@ -9,7 +9,7 @@ use crate::{ /// Integration via EventListener /// tracks the max memory consumption per bench -pub struct AllocPerBench { +pub(crate) struct AllocPerBench { alloc_per_bench: PerBenchData>, alloc: &'static dyn PeakMemAllocTrait, } @@ -39,11 +39,11 @@ impl EventListener for AllocPerBench { } fn on_event(&mut self, event: BingganEvents) { match event { - BingganEvents::BenchStart(bench_id) => { + BingganEvents::BenchStart { bench_id } => { self.alloc_per_bench.insert_if_absent(bench_id, Vec::new); self.alloc.reset_peak_memory(); } - BingganEvents::BenchStop(bench_id, _) => { + BingganEvents::BenchStop { bench_id, .. } => { let perf = self.alloc_per_bench.get_mut(bench_id).unwrap(); perf.push(self.alloc.get_peak_memory()); } diff --git a/src/plugins/events.rs b/src/plugins/events.rs index b71ebfe..0679bc7 100644 --- a/src/plugins/events.rs +++ b/src/plugins/events.rs @@ -1,41 +1,106 @@ -use std::any::Any; - -use rustc_hash::FxHashMap; +//! Event manager for Binggan. +//! The event manager is responsible for managing event listeners and emitting events. +//! It is used to notify listeners about events that occur during the benchmark run. +//! +//! # Example +//! ```rust no_run +//! use binggan::plugins::*; +//! +//! struct MyListener; +//! +//! impl EventListener for MyListener { +//! fn name(&self) -> &'static str { +//! "my_listener" +//! } +//! fn on_event(&mut self, event: BingganEvents) { +//! match event { +//! BingganEvents::GroupStart{runner_name, ..} => { +//! println!("Starting: {:?}", runner_name); +//! } +//! _ => {} +//! } +//! } +//! fn as_any(&mut self) -> &mut dyn std::any::Any { +//! self +//! } +//! } +//! +//! let mut event_manager = EventManager::new(); +//! event_manager.add_listener_if_absent(MyListener); +//! event_manager.emit(BingganEvents::GroupStart{runner_name: Some("test"), group_name: None, output_value_column_title: "output"}); +//! ``` +//! +//! See the `BingganEvents` enum for the list of events that can be emitted. +//! Any type that implements the `EventListener` trait can be added to the event manager. +//! use crate::{bench::BenchResult, bench_id::BenchId}; +use rustc_hash::FxHashMap; +use std::any::Any; /// Events that can be emitted by the benchmark runner. #[derive(Debug, Clone, Copy)] pub enum BingganEvents<'a> { - /// Parameter is the name of the run - StartRun(&'a str), - /// Parameter is the name of the benchmark group - GroupStart(&'a str), + /// Profiling of the group started + GroupStart { + /// The name of the runner + runner_name: Option<&'a str>, + /// The name of the group + group_name: Option<&'a str>, + /// The name of the column of the output value. + output_value_column_title: &'static str, + }, + /// Profiling of the group finished. GroupStop { - name: Option<&'a str>, + /// The name of the runner + runner_name: Option<&'a str>, + /// The name of the group + group_name: Option<&'a str>, + /// The results of the group + /// This will include the results of all the benchmarks in the group. + /// It also contains delta information of the last run if available results: &'a [BenchResult], + /// The name of the column of the output value. output_value_column_title: &'static str, }, - /// The benchmark is started. Note that a benchmark can be run multiple times for higher + /// A benchmark in a group is started. Note that a benchmark can be run multiple times for higher /// accuracy. BenchStart and BenchStop are not called for each iteration. /// - BenchStart(&'a BenchId), - BenchStop(&'a BenchId, u64), + /// A group is iterated multiple times. This will be called for every iteration in the group. + BenchStart { + /// The bench id + bench_id: &'a BenchId, + }, + /// A benchmark in a group is stopped. + BenchStop { + /// The bench id + bench_id: &'a BenchId, + /// The duration of the benchmark + duration: u64, + }, } +/// The trait for listening to events emitted by the benchmark runner. pub trait EventListener: Any { + /// The name of the event listener. fn name(&self) -> &'static str; + /// Handle an event. + /// See the [BingganEvents] enum for the list of events that can be emitted. fn on_event(&mut self, event: BingganEvents); + /// Downcast the listener to `Any`. fn as_any(&mut self) -> &mut dyn Any; } /// The event manager is responsible for managing event listeners and emitting events. /// It is used to notify listeners about events that occur during the benchmark run. /// +/// See the `BingganEvents` enum for the list of events that can be emitted. +/// Any type that implements the `EventListener` trait can be added to the event manager. pub struct EventManager { listeners: Vec<(String, Box)>, } impl EventManager { + /// Create a new instance of `EventManager`. pub fn new() -> Self { Self { listeners: Vec::new(), @@ -59,6 +124,7 @@ impl EventManager { .map(|(_, l)| l) } + /// Downcast a listener to a specific type. pub fn downcast_listener(&mut self, name: &str) -> Option<&mut T> { self.get_listener(name)?.as_any().downcast_mut::() } @@ -68,6 +134,7 @@ impl EventManager { self.listeners.retain(|(n, _)| n != name); } + /// Emit an event to all listeners. pub fn emit(&mut self, event: BingganEvents) { for (_, listener) in self.listeners.iter_mut() { listener.on_event(event); @@ -91,17 +158,21 @@ impl Default for PerBenchData { } } impl PerBenchData { + /// Create a new instance of `PerBenchData`. pub fn new() -> Self { Self { per_bench_data: FxHashMap::default(), } } + /// Get a mutable reference to the data for a specific bench id. pub fn get_mut(&mut self, bench_id: &BenchId) -> Option<&mut T> { self.per_bench_data.get_mut(bench_id) } + /// Get a reference to the data for a specific bench id. pub fn get(&self, bench_id: &BenchId) -> Option<&T> { self.per_bench_data.get(bench_id) } + /// Insert data for a specific bench id if it is not already present. pub fn insert_if_absent T>(&mut self, bench_id: &BenchId, data: F) { if !self.per_bench_data.contains_key(bench_id) { self.per_bench_data.insert(bench_id.clone(), data()); diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs index b4ae7b0..2ae0c64 100644 --- a/src/plugins/mod.rs +++ b/src/plugins/mod.rs @@ -1,4 +1,4 @@ -pub mod alloc; -pub(crate) mod events; -pub mod profiler; +pub(crate) mod alloc; +pub mod events; +pub(crate) mod profiler; pub use events::*; diff --git a/src/plugins/profiler/perf_profiler.rs b/src/plugins/profiler/perf_profiler.rs index 3fb0560..72a8fa0 100644 --- a/src/plugins/profiler/perf_profiler.rs +++ b/src/plugins/profiler/perf_profiler.rs @@ -122,7 +122,7 @@ impl EventListener for PerfCounterPerBench { } fn on_event(&mut self, event: BingganEvents) { match event { - BingganEvents::BenchStart(bench_id) => { + BingganEvents::BenchStart { bench_id } => { self.perf_per_bench .insert_if_absent(bench_id, || PerfCounters::new().ok()); let perf = self.perf_per_bench.get_mut(bench_id).unwrap(); @@ -130,7 +130,7 @@ impl EventListener for PerfCounterPerBench { perf.enable(); } } - BingganEvents::BenchStop(bench_id, _) => { + BingganEvents::BenchStop { bench_id, .. } => { let perf = self.perf_per_bench.get_mut(bench_id).unwrap(); if let Some(perf) = perf { perf.disable(); diff --git a/src/report/mod.rs b/src/report/mod.rs index 52fddd8..a563cb2 100644 --- a/src/report/mod.rs +++ b/src/report/mod.rs @@ -40,6 +40,7 @@ pub trait ReporterClone { } pub(crate) fn report_group<'a>( + runner_name: Option<&str>, group_name: Option<&str>, benches: &mut [Box + 'a>], reporter: &dyn Reporter, @@ -57,7 +58,8 @@ pub(crate) fn report_group<'a>( results.push(result); } events.emit(BingganEvents::GroupStop { - name: group_name, + runner_name, + group_name, results: &results, output_value_column_title, });