iai_callgrind_runner/runner/cachegrind/
model.rs

1//! This module includes all the structs to model the cachegrind output
2use std::borrow::Cow;
3
4use anyhow::Result;
5use indexmap::indexmap;
6
7use crate::api::CachegrindMetric;
8use crate::runner::callgrind::{CacheSummary, CyclesEstimator};
9use crate::runner::metrics::{Metric, Summarize};
10
11/// The cachegrind specific `Metrics`
12pub type Metrics = crate::runner::metrics::Metrics<CachegrindMetric>;
13
14impl TryFrom<&Metrics> for CacheSummary {
15    type Error = anyhow::Error;
16
17    fn try_from(value: &Metrics) -> std::result::Result<Self, Self::Error> {
18        use CachegrindMetric::*;
19        let estimator = CyclesEstimator::new(
20            value.try_metric_by_kind(&Ir)?,
21            value.try_metric_by_kind(&Dr)?,
22            value.try_metric_by_kind(&Dw)?,
23            value.try_metric_by_kind(&I1mr)?,
24            value.try_metric_by_kind(&D1mr)?,
25            value.try_metric_by_kind(&D1mw)?,
26            value.try_metric_by_kind(&ILmr)?,
27            value.try_metric_by_kind(&DLmr)?,
28            value.try_metric_by_kind(&DLmw)?,
29        );
30
31        Ok(estimator.calculate())
32    }
33}
34
35impl Summarize for CachegrindMetric {
36    fn summarize(costs: &mut Cow<Metrics>) {
37        if !costs.is_summarized() {
38            let _ = costs.to_mut().make_summary();
39        }
40    }
41}
42
43impl Metrics {
44    /// Calculate and add derived summary events (i.e. estimated cycles) in-place
45    ///
46    /// Additional calls to this function will overwrite the metrics for derived summary events.
47    ///
48    /// # Errors
49    ///
50    /// If the necessary cache simulation events (when running cachegrind with --cache-sim) were not
51    /// present.
52    pub fn make_summary(&mut self) -> Result<()> {
53        let CacheSummary {
54            l1_hits,
55            l3_hits,
56            ram_hits,
57            total_memory_rw,
58            cycles,
59            i1_miss_rate,
60            d1_miss_rate,
61            ll_miss_rate,
62            lli_miss_rate,
63            lld_miss_rate,
64            l1_hit_rate,
65            l3_hit_rate,
66            ram_hit_rate,
67        } = (&*self).try_into()?;
68
69        self.insert(CachegrindMetric::L1hits, l1_hits);
70        self.insert(CachegrindMetric::LLhits, l3_hits);
71        self.insert(CachegrindMetric::RamHits, ram_hits);
72        self.insert(CachegrindMetric::TotalRW, total_memory_rw);
73        self.insert(CachegrindMetric::EstimatedCycles, cycles);
74        self.insert(CachegrindMetric::I1MissRate, i1_miss_rate);
75        self.insert(CachegrindMetric::D1MissRate, d1_miss_rate);
76        self.insert(CachegrindMetric::LLiMissRate, lli_miss_rate);
77        self.insert(CachegrindMetric::LLdMissRate, lld_miss_rate);
78        self.insert(CachegrindMetric::LLMissRate, ll_miss_rate);
79        self.insert(CachegrindMetric::L1HitRate, l1_hit_rate);
80        self.insert(CachegrindMetric::LLHitRate, l3_hit_rate);
81        self.insert(CachegrindMetric::RamHitRate, ram_hit_rate);
82
83        Ok(())
84    }
85
86    /// Return true if costs are already summarized
87    ///
88    /// This method just probes for [`EventKind::EstimatedCycles`] to detect the summarized state.
89    pub fn is_summarized(&self) -> bool {
90        self.metric_by_kind(&CachegrindMetric::EstimatedCycles)
91            .is_some()
92    }
93
94    /// Return true if costs can be summarized
95    ///
96    /// This method probes for [`EventKind::I1mr`] which is present if cachegrind was run with the
97    /// cache simulation (`--cache-sim=yes`) enabled.
98    pub fn can_summarize(&self) -> bool {
99        self.metric_by_kind(&CachegrindMetric::I1mr).is_some()
100    }
101}
102
103impl Default for Metrics {
104    fn default() -> Self {
105        Self(indexmap! {CachegrindMetric::Ir => Metric::Int(0)})
106    }
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112
113    // Not testing here if the numbers make sense. Just if all metrics are present in the correct
114    // order
115    #[test]
116    fn test_metrics_make_summary_when_cache_sim() {
117        use CachegrindMetric::*;
118
119        let mut expected = Metrics::with_metric_kinds([
120            (Ir, 1),
121            (Dr, 2),
122            (Dw, 3),
123            (I1mr, 4),
124            (D1mr, 5),
125            (D1mw, 6),
126            (ILmr, 7),
127            (DLmr, 8),
128            (DLmw, 9),
129            (L1hits, 0),
130            (LLhits, 0),
131            (RamHits, 24),
132            (TotalRW, 6),
133            (EstimatedCycles, 840),
134        ]);
135
136        expected.insert_all(&[
137            (I1MissRate, Metric::Float(400.0f64)),
138            (D1MissRate, Metric::Float(220.000_000_000_000_03_f64)),
139            (LLiMissRate, Metric::Float(700.0f64)),
140            (LLdMissRate, Metric::Float(340.0f64)),
141            (LLMissRate, Metric::Float(400.0f64)),
142            (L1HitRate, Metric::Float(0.0f64)),
143            (LLHitRate, Metric::Float(0.0f64)),
144            (RamHitRate, Metric::Float(400.0f64)),
145        ]);
146
147        let mut metrics = Metrics::with_metric_kinds([
148            (Ir, 1),
149            (Dr, 2),
150            (Dw, 3),
151            (I1mr, 4),
152            (D1mr, 5),
153            (D1mw, 6),
154            (ILmr, 7),
155            (DLmr, 8),
156            (DLmw, 9),
157        ]);
158
159        metrics.make_summary().unwrap();
160
161        assert_eq!(metrics, expected);
162    }
163}