mmtk/scheduler/
work_counter.rs

1//! Counter for work packets
2//!
3//! Provides an abstraction and implementations of counters for collecting
4//! work-packet level statistics
5//!
6//! See [`crate::util::statistics`] for collecting statistics over a GC cycle
7use std::time::Instant;
8
9/// Common struct for different work counters
10///
11/// Stores the total, min and max of counter readings
12#[derive(Copy, Clone, Debug)]
13pub(super) struct WorkCounterBase {
14    pub(super) total: f64,
15    pub(super) min: f64,
16    pub(super) max: f64,
17}
18
19/// Make [`WorkCounter`] trait objects cloneable
20pub(super) trait WorkCounterClone {
21    /// Clone the object
22    fn clone_box(&self) -> Box<dyn WorkCounter>;
23}
24
25impl<T: 'static + WorkCounter + Clone> WorkCounterClone for T {
26    fn clone_box(&self) -> Box<dyn WorkCounter> {
27        Box::new(self.clone())
28    }
29}
30
31/// An abstraction of work counters
32///
33/// Use for trait objects, as we have might have types of work counters for
34/// the same work packet and the types are not statically known.
35/// The overhead should be negligible compared with the cost of executing
36/// a work packet.
37pub(super) trait WorkCounter: WorkCounterClone + std::fmt::Debug + Send {
38    // TODO: consolidate with crate::util::statistics::counter::Counter;
39    /// Start the counter
40    fn start(&mut self);
41    /// Stop the counter
42    fn stop(&mut self);
43    /// Name of counter
44    fn name(&self) -> String;
45    /// Return a reference to [`WorkCounterBase`]
46    fn get_base(&self) -> &WorkCounterBase;
47}
48
49impl Clone for Box<dyn WorkCounter> {
50    fn clone(&self) -> Box<dyn WorkCounter> {
51        self.clone_box()
52    }
53}
54
55impl Default for WorkCounterBase {
56    fn default() -> Self {
57        WorkCounterBase {
58            total: 0.0,
59            min: f64::INFINITY,
60            max: f64::NEG_INFINITY,
61        }
62    }
63}
64
65impl WorkCounterBase {
66    /// Merge two [`WorkCounterBase`], keep the semantics of the fields,
67    /// and return a new object
68    pub(super) fn merge(&self, other: &Self) -> Self {
69        let min = self.min.min(other.min);
70        let max = self.max.max(other.max);
71        let total = self.total + other.total;
72        WorkCounterBase { total, min, max }
73    }
74
75    /// Merge two [`WorkCounterBase`], modify the current object in place,
76    /// and keep the semantics of the fields
77    pub(super) fn merge_inplace(&mut self, other: &Self) {
78        self.min = self.min.min(other.min);
79        self.max = self.max.max(other.max);
80        self.total += other.total;
81    }
82
83    /// Update the object based on a single value
84    pub(super) fn merge_val(&mut self, val: f64) {
85        self.min = self.min.min(val);
86        self.max = self.max.max(val);
87        self.total += val;
88    }
89}
90
91/// Measure the durations of work packets
92///
93/// Timing is based on [`Instant`]
94#[derive(Copy, Clone, Debug)]
95pub(super) struct WorkDuration {
96    base: WorkCounterBase,
97    start_value: Option<Instant>,
98    running: bool,
99}
100
101impl WorkDuration {
102    pub(super) fn new() -> Self {
103        WorkDuration {
104            base: Default::default(),
105            start_value: None,
106            running: false,
107        }
108    }
109}
110
111impl WorkCounter for WorkDuration {
112    fn start(&mut self) {
113        self.start_value = Some(Instant::now());
114        self.running = true;
115    }
116
117    fn stop(&mut self) {
118        let duration = self.start_value.unwrap().elapsed().as_nanos() as f64;
119        self.base.merge_val(duration);
120    }
121
122    fn name(&self) -> String {
123        "time".to_owned()
124    }
125
126    fn get_base(&self) -> &WorkCounterBase {
127        &self.base
128    }
129}
130
131#[cfg(feature = "perf_counter")]
132mod perf_event {
133    //! Measure the perf events of work packets
134    //!
135    //! This is built on top of libpfm4.
136    //! The events to measure are parsed from MMTk option `perf_events`
137    use super::*;
138    use libc::{c_int, pid_t};
139    use pfm::PerfEvent;
140    use std::fmt;
141
142    /// Work counter for perf events
143    #[derive(Clone)]
144    pub struct WorkPerfEvent {
145        base: WorkCounterBase,
146        running: bool,
147        event_name: String,
148        pe: PerfEvent,
149    }
150
151    impl WorkPerfEvent {
152        /// Create a work counter
153        ///
154        /// See `perf_event_open` for more details on `pid` and `cpu`
155        /// Examples:
156        /// 0, -1 measures the calling thread on all CPUs
157        /// -1, 0 measures all threads on CPU 0
158        /// -1, -1 is invalid
159        pub fn new(name: &str, pid: pid_t, cpu: c_int, exclude_kernel: bool) -> WorkPerfEvent {
160            let mut pe = PerfEvent::new(name, false)
161                .unwrap_or_else(|_| panic!("Failed to create perf event {}", name));
162            pe.set_exclude_kernel(exclude_kernel as u64);
163            pe.open(pid, cpu)
164                .unwrap_or_else(|_| panic!("Failed to open perf event {}", name));
165            WorkPerfEvent {
166                base: Default::default(),
167                running: false,
168                event_name: name.to_string(),
169                pe,
170            }
171        }
172    }
173
174    impl fmt::Debug for WorkPerfEvent {
175        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176            f.debug_struct("WorkPerfEvent")
177                .field("base", &self.base)
178                .field("running", &self.running)
179                .field("event_name", &self.event_name)
180                .finish()
181        }
182    }
183
184    impl WorkCounter for WorkPerfEvent {
185        fn start(&mut self) {
186            self.running = true;
187            self.pe.reset().expect("Failed to reset perf event");
188            self.pe.enable().expect("Failed to enable perf event");
189        }
190        fn stop(&mut self) {
191            self.running = true;
192            let perf_event_value = self.pe.read().unwrap();
193            self.base.merge_val(perf_event_value.value as f64);
194            // assert not multiplexing
195            assert_eq!(perf_event_value.time_enabled, perf_event_value.time_running);
196            self.pe.disable().expect("Failed to disable perf event");
197        }
198        fn name(&self) -> String {
199            self.event_name.to_owned()
200        }
201        fn get_base(&self) -> &WorkCounterBase {
202            &self.base
203        }
204    }
205}
206
207#[cfg(feature = "perf_counter")]
208pub(super) use perf_event::WorkPerfEvent;