iai_callgrind_runner/runner/
metrics.rs1use std::borrow::Cow;
2use std::fmt::Display;
3use std::hash::Hash;
4
5use anyhow::{Context, Result};
6use indexmap::map::Iter;
7use indexmap::{IndexMap, IndexSet};
8#[cfg(feature = "schema")]
9use schemars::JsonSchema;
10use serde::{Deserialize, Serialize};
11
12pub trait Summarize: Hash + Eq + Clone {
13 fn summarize(_: &mut Cow<Metrics<Self>>) {}
14}
15
16#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
20#[cfg_attr(feature = "schema", derive(JsonSchema))]
21pub struct Metrics<K: Hash + Eq>(pub IndexMap<K, u64>);
22
23impl<K: Hash + Eq + Display + Clone> Metrics<K> {
24 pub fn empty() -> Self {
26 Metrics(IndexMap::new())
27 }
28
29 pub fn with_metric_kinds<T>(kinds: T) -> Self
31 where
32 T: IntoIterator<Item = (K, u64)>,
33 {
34 Self(kinds.into_iter().collect())
35 }
36
37 pub fn add_iter_str<I, T>(&mut self, iter: T) -> Result<()>
50 where
51 I: AsRef<str>,
52 T: IntoIterator<Item = I>,
53 {
54 for (this, other) in self.0.values_mut().zip(iter.into_iter()) {
55 *this += other
56 .as_ref()
57 .parse::<u64>()
58 .context("A metric must be an integer type")?;
59 }
60
61 Ok(())
62 }
63
64 pub fn add(&mut self, other: &Self) {
68 for (this, other) in self.0.values_mut().zip(other.0.values()) {
69 *this += other;
70 }
71 }
72
73 pub fn metric_by_index(&self, index: usize) -> Option<u64> {
77 self.0.get_index(index).map(|(_, c)| *c)
78 }
79
80 pub fn metric_by_kind(&self, kind: &K) -> Option<u64> {
84 self.0.get_key_value(kind).map(|(_, c)| *c)
85 }
86
87 pub fn try_metric_by_kind(&self, kind: &K) -> Result<u64> {
93 self.metric_by_kind(kind)
94 .with_context(|| format!("Missing event type '{kind}"))
95 }
96
97 pub fn metric_kinds(&self) -> Vec<K> {
98 self.0.iter().map(|(k, _)| k.clone()).collect()
99 }
100
101 pub fn metric_kinds_union<'a>(&'a self, other: &'a Self) -> IndexSet<&'a K> {
106 let set = self.0.keys().collect::<IndexSet<_>>();
107 let other_set = other.0.keys().collect::<IndexSet<_>>();
108 set.union(&other_set).copied().collect()
109 }
110
111 pub fn iter(&self) -> Iter<'_, K, u64> {
113 self.0.iter()
114 }
115
116 pub fn is_empty(&self) -> bool {
118 self.0.is_empty()
119 }
120
121 pub fn insert(&mut self, key: K, value: u64) -> Option<u64> {
130 self.0.insert(key, value)
131 }
132
133 pub fn insert_all(&mut self, entries: &[(K, u64)]) {
137 for (key, value) in entries {
138 self.insert(key.clone(), *value);
139 }
140 }
141}
142
143impl<'a, K: Hash + Eq + Display + Clone> IntoIterator for &'a Metrics<K> {
144 type Item = (&'a K, &'a u64);
145
146 type IntoIter = Iter<'a, K, u64>;
147
148 fn into_iter(self) -> Self::IntoIter {
149 self.iter()
150 }
151}
152
153impl<I, K: Hash + Eq + From<I>> FromIterator<I> for Metrics<K> {
154 fn from_iter<T>(iter: T) -> Self
155 where
156 T: IntoIterator<Item = I>,
157 {
158 Self(
159 iter.into_iter()
160 .map(|s| (K::from(s), 0))
161 .collect::<IndexMap<_, _>>(),
162 )
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use std::iter;
169
170 use rstest::rstest;
171
172 use super::*;
173 use crate::api::EventKind::{self, *};
174
175 fn expected_metrics<T>(events: T) -> Metrics<EventKind>
176 where
177 T: IntoIterator<Item = (EventKind, u64)>,
178 {
179 Metrics(IndexMap::from_iter(events))
180 }
181
182 #[rstest]
183 #[case::single_zero(&[Ir], &["0"], expected_metrics([(Ir, 0)]))]
184 #[case::single_one(&[Ir], &["1"], expected_metrics([(Ir, 1)]))]
185 #[case::single_u64_max(&[Ir], &[u64::MAX.to_string()], expected_metrics([(Ir, u64::MAX)]))]
186 #[case::more_values_than_kinds(&[Ir], &["1", "2"], expected_metrics([(Ir, 1)]))]
187 #[case::more_kinds_than_values(&[Ir, I1mr], &["1"], expected_metrics([(Ir, 1), (I1mr, 0)]))]
188 fn test_metrics_add_iter_str<I>(
189 #[case] event_kinds: &[EventKind],
190 #[case] to_add: &[I],
191 #[case] expected_metrics: Metrics<EventKind>,
192 ) where
193 I: AsRef<str>,
194 {
195 let mut metrics =
196 Metrics::with_metric_kinds(event_kinds.iter().copied().zip(iter::repeat(0)));
197 metrics.add_iter_str(to_add).unwrap();
198
199 assert_eq!(metrics, expected_metrics);
200 }
201
202 #[rstest]
203 #[case::float(&[Ir], &["0.0"])]
204 #[case::word(&[Ir], &["abc"])]
205 #[case::empty(&[Ir], &[""])]
206 #[case::one_more_than_max_u64(&[Ir], &["18446744073709551616"])]
207 fn test_metrics_add_iter_str_when_error<I>(
208 #[case] event_kinds: &[EventKind],
209 #[case] to_add: &[I],
210 ) where
211 I: AsRef<str>,
212 {
213 let mut metrics =
214 Metrics::with_metric_kinds(event_kinds.iter().copied().zip(iter::repeat(0)));
215 assert!(metrics.add_iter_str(to_add).is_err());
216 }
217}