Skip to main content

tracing_calltree/
stats.rs

1use std::time::Duration;
2
3#[derive(Clone, Debug, PartialEq, Eq)]
4#[cfg_attr(feature = "serde", derive(serde::Serialize))]
5#[non_exhaustive]
6pub struct TimingStats {
7    pub samples: usize,
8    pub min: Duration,
9    pub max: Duration,
10    pub mean: Duration,
11    pub p95: Duration,
12}
13
14impl TimingStats {
15    pub(crate) fn from_nanos(values: impl IntoIterator<Item = u64>) -> Self {
16        let mut sorted: Vec<u64> = values.into_iter().collect();
17
18        if sorted.is_empty() {
19            return Self {
20                samples: 0,
21                min: Duration::ZERO,
22                max: Duration::ZERO,
23                mean: Duration::ZERO,
24                p95: Duration::ZERO,
25            };
26        }
27
28        sorted.sort_unstable();
29
30        let samples = sorted.len();
31        let min = sorted[0];
32        let max = sorted[samples - 1];
33        let sum = sorted
34            .iter()
35            .fold(0_u128, |acc, value| acc + u128::from(*value));
36        let mean = (sum / samples as u128).min(u64::MAX as u128) as u64;
37        let rank = ((samples * 95).div_ceil(100)).saturating_sub(1);
38        let p95 = sorted[rank];
39
40        Self {
41            samples,
42            min: Duration::from_nanos(min),
43            max: Duration::from_nanos(max),
44            mean: Duration::from_nanos(mean),
45            p95: Duration::from_nanos(p95),
46        }
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::TimingStats;
53    use std::time::Duration;
54
55    #[test]
56    fn calculates_exact_nearest_rank_statistics() {
57        let stats = TimingStats::from_nanos(1..=100);
58
59        assert_eq!(stats.samples, 100);
60        assert_eq!(stats.min, Duration::from_nanos(1));
61        assert_eq!(stats.max, Duration::from_nanos(100));
62        assert_eq!(stats.mean, Duration::from_nanos(50));
63        assert_eq!(stats.p95, Duration::from_nanos(95));
64    }
65
66    #[test]
67    fn empty_input_returns_zeroed_stats() {
68        let stats = TimingStats::from_nanos(std::iter::empty());
69
70        assert_eq!(stats.samples, 0);
71        assert_eq!(stats.min, Duration::ZERO);
72        assert_eq!(stats.max, Duration::ZERO);
73        assert_eq!(stats.mean, Duration::ZERO);
74        assert_eq!(stats.p95, Duration::ZERO);
75    }
76}