Skip to main content

binggan/plugins/
events.rs

1//! [PluginManager] for Binggan.
2//!
3//! The plugin manager is responsible for managing plugins and emitting events to them
4//! that occur during the benchmark run.
5//!
6//! See the [PluginEvents] enum for the list of events that can be emitted.
7//! Any type that implements the [EventListener] trait can be added to [PluginManager].
8//!
9
10use crate::{bench::BenchResult, bench_id::BenchId};
11use std::any::Any;
12
13/// Events that can be emitted by the benchmark runner.
14#[derive(Debug, Clone, Copy)]
15pub enum PluginEvents<'a> {
16    /// The number of iterations for the benches in a group has been set.
17    /// The previous event was `GroupStart`.
18    GroupBenchNumIters {
19        /// The number of iterations for each bench in the group. The whole group has the same
20        /// number of iterations to be a fair comparison between the benches in the group.
21        num_iter: usize,
22    },
23    /// The number of iterations for the bench group.
24    /// The previous event was `GroupStart`.
25    GroupNumIters {
26        /// Unlike GroupBenchNumIters, this is the number of iterations for the group.
27        /// So each bench is run `num_iter` * `num_group_iter` times.
28        num_iter: usize,
29    },
30    /// Profiling of the group started
31    GroupStart {
32        /// The name of the runner
33        runner_name: Option<&'a str>,
34        /// The name of the group
35        group_name: Option<&'a str>,
36        /// The name of the column of the output value.
37        output_value_column_title: &'static str,
38    },
39    /// Profiling of the group finished.
40    GroupStop {
41        /// The name of the runner
42        runner_name: Option<&'a str>,
43        /// The name of the group
44        group_name: Option<&'a str>,
45        /// The results of the group
46        /// This will include the results of all the benchmarks in the group.
47        /// It also contains delta information of the last run if available
48        results: &'a [BenchResult],
49        /// The name of the column of the output value.
50        output_value_column_title: &'static str,
51    },
52    /// A benchmark in a group is started. Note that a benchmark can be run multiple times for higher
53    /// accuracy. BenchStart and BenchStop are not called for each iteration.
54    ///
55    /// A group is iterated multiple times. This will be called for every iteration in the group.
56    BenchStart {
57        /// The bench id
58        bench_id: &'a BenchId,
59    },
60    /// A benchmark in a group is stopped.
61    BenchStop {
62        /// The bench id
63        bench_id: &'a BenchId,
64        /// The duration of the benchmark
65        duration: u64,
66    },
67}
68
69/// The trait for listening to events emitted by the benchmark runner.
70pub trait EventListener: Any {
71    /// The priority of the event listener.
72    /// If the event listener has a higher priority, it will be called first.
73    /// E.g. perf counter plugin should be called after the cache trasher in the [PluginEvents::BenchStart] event,
74    /// so that the cache trashing is not included in the perf counter.
75    ///
76    /// The default priority is `u32::MAX / 2`.
77    ///
78    /// Note: In the [PluginEvents::BenchStop] event, the order is reversed. The listener with the highest priority
79    /// is called last. This is to endure symmetry.
80    fn prio(&self) -> u32 {
81        u32::MAX / 2
82    }
83
84    /// The name of the event listener.
85    fn name(&self) -> &'static str;
86    /// Handle an event.
87    /// See the [PluginEvents] enum for the list of events that can be emitted.
88    fn on_event(&mut self, event: PluginEvents);
89    /// Downcast the listener to `Any`.
90    fn as_any(&mut self) -> &mut dyn Any;
91}
92
93/// [PluginManager] is responsible for managing plugins and emitting events.
94///
95/// See the [PluginEvents] enum for the list of events that can be emitted.
96/// Any type that implements the `EventListener` trait can be added to the plugin manager.
97pub struct PluginManager {
98    listeners: Vec<(String, Box<dyn EventListener>)>,
99}
100impl PluginManager {
101    /// Create a new instance of [PluginManager].
102    pub fn new() -> Self {
103        Self {
104            listeners: Vec::new(),
105        }
106    }
107
108    /// Removes any plugins with the same name and sets the new listener.
109    pub fn replace_plugin<L: EventListener + 'static>(&mut self, listener: L) -> &mut Self {
110        self.remove_plugin_by_name(listener.name());
111        self.add_plugin(listener);
112        self
113    }
114
115    /// Add a new plugin. Note that this will not remove listeners with the
116    /// same name.
117    pub fn add_plugin<L: EventListener + 'static>(&mut self, listener: L) -> &mut Self {
118        self.listeners
119            .push((listener.name().to_owned(), Box::new(listener)));
120        self.listeners.sort_by_key(|(_, l)| l.prio());
121        self
122    }
123
124    /// Add a new plugin to the plugin manager if it is not already present by name.
125    pub fn add_plugin_if_absent<L: EventListener + 'static>(&mut self, listener: L) -> &mut Self {
126        if self.get_plugins(listener.name()).is_some() {
127            return self;
128        }
129        self.add_plugin(listener);
130        self
131    }
132
133    /// Get the first plugin that matches the name.
134    pub fn get_plugins(&mut self, name: &str) -> Option<&mut Box<dyn EventListener>> {
135        self.listeners
136            .iter_mut()
137            .find(|(n, _)| n == name)
138            .map(|(_, l)| l)
139    }
140
141    /// Downcast a plugin to a specific type.
142    pub fn downcast_plugin<T: 'static>(&mut self, name: &str) -> Option<&mut T> {
143        self.get_plugins(name)?.as_any().downcast_mut::<T>()
144    }
145
146    /// Remove a plugin by name.
147    pub fn remove_plugin_by_name(&mut self, name: &str) {
148        self.listeners.retain(|(n, _)| n != name);
149    }
150
151    /// Emit an event to all plugin.
152    pub fn emit(&mut self, event: PluginEvents) {
153        if matches!(event, PluginEvents::BenchStop { .. }) {
154            for (_, listener) in self.listeners.iter_mut().rev() {
155                listener.on_event(event);
156            }
157        } else {
158            for (_, listener) in self.listeners.iter_mut() {
159                listener.on_event(event);
160            }
161        }
162    }
163}
164
165impl Default for PluginManager {
166    fn default() -> Self {
167        Self::new()
168    }
169}