iai_callgrind_runner/runner/cachegrind/
regression.rs

1//! Module containing the cachegrind specific regression check configuration
2use indexmap::{IndexMap, IndexSet};
3
4use crate::api::{self, CachegrindMetric};
5use crate::runner::metrics::{Metric, MetricKind, MetricsSummary};
6use crate::runner::summary::ToolRegression;
7use crate::runner::tool::regression::RegressionConfig;
8
9/// The callgrind regression check configuration
10#[derive(Debug, Clone, PartialEq)]
11pub struct CachegrindRegressionConfig {
12    /// True if benchmarks should fail on first encountered failed regression check
13    pub fail_fast: bool,
14    /// The hard limits
15    pub hard_limits: Vec<(CachegrindMetric, Metric)>,
16    /// The soft limits
17    pub soft_limits: Vec<(CachegrindMetric, f64)>,
18}
19
20impl Default for CachegrindRegressionConfig {
21    fn default() -> Self {
22        Self {
23            soft_limits: vec![(CachegrindMetric::Ir, 10f64)],
24            hard_limits: Vec::default(),
25            fail_fast: false,
26        }
27    }
28}
29
30impl RegressionConfig<CachegrindMetric> for CachegrindRegressionConfig {
31    fn check(&self, metrics_summary: &MetricsSummary<CachegrindMetric>) -> Vec<ToolRegression> {
32        self.check_regressions(metrics_summary)
33            .into_iter()
34            .map(|regressions| ToolRegression::with(MetricKind::Cachegrind, regressions))
35            .collect()
36    }
37
38    fn get_soft_limits(&self) -> &[(CachegrindMetric, f64)] {
39        &self.soft_limits
40    }
41
42    fn get_hard_limits(&self) -> &[(CachegrindMetric, Metric)] {
43        &self.hard_limits
44    }
45}
46
47impl TryFrom<api::CachegrindRegressionConfig> for CachegrindRegressionConfig {
48    type Error = String;
49
50    fn try_from(value: api::CachegrindRegressionConfig) -> Result<Self, Self::Error> {
51        let api::CachegrindRegressionConfig {
52            soft_limits,
53            hard_limits,
54            fail_fast,
55        } = value;
56
57        let (soft_limits, hard_limits) = if soft_limits.is_empty() && hard_limits.is_empty() {
58            (
59                IndexMap::from([(CachegrindMetric::Ir, 10f64)]),
60                IndexMap::new(),
61            )
62        } else {
63            let hard_limits = hard_limits
64                .into_iter()
65                .flat_map(|(cachegrind_metrics, metric)| {
66                    IndexSet::from(cachegrind_metrics)
67                        .into_iter()
68                        .map(move |metric_kind| {
69                            Metric::from(metric)
70                                .try_convert(metric_kind)
71                                .ok_or_else(|| {
72                                    format!(
73                                        "Invalid hard limit for \
74                                         '{metric_kind:?}/{cachegrind_metrics:?}': Expected a \
75                                         'Int' but found '{metric:?}'"
76                                    )
77                                })
78                        })
79                })
80                .collect::<Result<IndexMap<CachegrindMetric, Metric>, String>>()?;
81
82            let soft_limits = soft_limits
83                .into_iter()
84                .flat_map(|(m, l)| IndexSet::from(m).into_iter().map(move |e| (e, l)))
85                .collect::<IndexMap<_, _>>();
86
87            (soft_limits, hard_limits)
88        };
89
90        Ok(Self {
91            soft_limits: soft_limits.into_iter().collect(),
92            hard_limits: hard_limits.into_iter().collect(),
93            fail_fast: fail_fast.unwrap_or(false),
94        })
95    }
96}