witchcraft_metrics/
histogram.rs

1// Copyright 2019 Palantir Technologies, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14use crate::{ExponentiallyDecayingReservoir, Reservoir, Snapshot};
15use std::sync::atomic::{AtomicU64, Ordering};
16
17/// A metric tracking a statistical distribution of values.
18///
19/// The histogram's default reservoir implementation (used by its [`Default`] implementation) is the
20/// [`ExponentiallyDecayingReservoir`].
21pub struct Histogram {
22    count: AtomicU64,
23    reservoir: Box<dyn Reservoir>,
24}
25
26impl Default for Histogram {
27    #[inline]
28    fn default() -> Histogram {
29        Histogram::new(ExponentiallyDecayingReservoir::new())
30    }
31}
32
33impl Histogram {
34    /// Creates a new histogram using the provided reservoir.
35    pub fn new<R>(reservoir: R) -> Histogram
36    where
37        R: Reservoir,
38    {
39        Histogram {
40            count: AtomicU64::new(0),
41            reservoir: Box::new(reservoir),
42        }
43    }
44
45    /// Adds a value to the histogram.
46    #[inline]
47    pub fn update(&self, value: i64) {
48        self.count.fetch_add(1, Ordering::Relaxed);
49        self.reservoir.update(value);
50    }
51
52    /// Returns the number of values added to the histogram.
53    #[inline]
54    pub fn count(&self) -> u64 {
55        self.count.load(Ordering::Relaxed)
56    }
57
58    /// Returns a snapshot of the statistical distribution of values.
59    #[inline]
60    pub fn snapshot(&self) -> Box<dyn Snapshot> {
61        self.reservoir.snapshot()
62    }
63}
64
65#[cfg(test)]
66mod test {
67    use crate::{Histogram, Reservoir, Snapshot};
68    use std::sync::atomic::{AtomicI64, Ordering};
69
70    struct TestReservoir(AtomicI64);
71
72    impl Reservoir for TestReservoir {
73        fn update(&self, value: i64) {
74            self.0.store(value, Ordering::SeqCst);
75        }
76
77        fn snapshot(&self) -> Box<dyn Snapshot> {
78            Box::new(TestSnapshot(self.0.load(Ordering::SeqCst)))
79        }
80    }
81
82    struct TestSnapshot(i64);
83
84    impl Snapshot for TestSnapshot {
85        fn value(&self, _: f64) -> f64 {
86            unimplemented!()
87        }
88
89        fn max(&self) -> i64 {
90            unimplemented!()
91        }
92
93        fn min(&self) -> i64 {
94            self.0
95        }
96
97        fn mean(&self) -> f64 {
98            unimplemented!()
99        }
100
101        fn stddev(&self) -> f64 {
102            unimplemented!()
103        }
104    }
105
106    #[test]
107    fn basic() {
108        let histogram = Histogram::new(TestReservoir(AtomicI64::new(0)));
109        assert_eq!(histogram.count(), 0);
110
111        histogram.update(15);
112        assert_eq!(histogram.count(), 1);
113        assert_eq!(histogram.snapshot().min(), 15);
114
115        histogram.update(10);
116        assert_eq!(histogram.count(), 2);
117        assert_eq!(histogram.snapshot().min(), 10);
118    }
119}