uwheel_stats/
lib.rs

1//! uwheel-stats is a sub-crate of uwheel which contains profiling utility.
2use core::cell::RefCell;
3use minstant::Instant;
4use sketches_ddsketch::{Config, DDSketch};
5use std::rc::Rc;
6
7pub fn sketch_percentiles(sketch: &DDSketch) -> Percentiles {
8    Percentiles {
9        count: sketch.count(),
10        min: sketch.min().unwrap_or(0.0),
11        p25: sketch.quantile(0.25).unwrap().unwrap_or(0.0),
12        p50: sketch.quantile(0.5).unwrap().unwrap_or(0.0),
13        p75: sketch.quantile(0.75).unwrap().unwrap_or(0.0),
14        p95: sketch.quantile(0.95).unwrap().unwrap_or(0.0),
15        p99: sketch.quantile(0.99).unwrap().unwrap_or(0.0),
16        p99_9: sketch.quantile(0.999).unwrap().unwrap_or(0.0),
17        p99_99: sketch.quantile(0.9999).unwrap().unwrap_or(0.0),
18        p99_999: sketch.quantile(0.99999).unwrap().unwrap_or(0.0),
19        max: sketch.max().unwrap_or(0.0),
20        sum: sketch.sum().unwrap_or(0.0),
21    }
22}
23
24#[derive(Default, Clone, Copy)]
25pub struct Percentiles {
26    pub count: usize,
27    pub min: f64,
28    pub p25: f64,
29    pub p50: f64,
30    pub p75: f64,
31    pub p95: f64,
32    pub p99: f64,
33    pub p99_9: f64,
34    pub p99_99: f64,
35    pub p99_999: f64,
36    pub max: f64,
37    pub sum: f64,
38}
39
40impl std::fmt::Debug for Percentiles {
41    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
42        f.debug_struct("Percentiles")
43            .field("count", &self.count)
44            .field("min", &format_args!("{:.2}ns", self.min))
45            .field("p25", &format_args!("{:.2}ns", self.p25))
46            .field("p50", &format_args!("{:.2}ns", self.p50))
47            .field("p75", &format_args!("{:.2}ns", self.p75))
48            .field("p95", &format_args!("{:.2}ns", self.p95))
49            .field("p99", &format_args!("{:.2}ns", self.p99))
50            .field("p99.9", &format_args!("{:.2}ns", self.p99_9))
51            .field("p99.99", &format_args!("{:.2}ns", self.p99_99))
52            .field("p99.999", &format_args!("{:.2}ns", self.p99_999))
53            .field("max", &format_args!("{:.2}ns", self.max))
54            .field("sum", &self.sum)
55            .finish()
56    }
57}
58
59#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
60#[derive(Clone)]
61pub struct Sketch {
62    inner: Rc<RefCell<DDSketch>>,
63}
64impl Default for Sketch {
65    fn default() -> Self {
66        Self {
67            inner: Rc::new(RefCell::new(DDSketch::new(Config::new(0.01, 2048, 1.0e-9)))),
68        }
69    }
70}
71impl Sketch {
72    #[inline]
73    pub fn add(&self, data: f64) {
74        self.inner.borrow_mut().add(data)
75    }
76    pub fn merge(&self, other: Self) {
77        self.inner
78            .borrow_mut()
79            .merge(&other.inner.borrow())
80            .unwrap()
81    }
82    pub fn percentiles(&self) -> Percentiles {
83        sketch_percentiles(&self.inner.borrow())
84    }
85    pub fn count(&self) -> usize {
86        self.inner.borrow().count()
87    }
88}
89
90// Inspired by https://github.com/spacejam/sled/blob/main/src/metrics.rs
91pub struct Measure {
92    start: Instant,
93    sketch: Sketch,
94}
95
96impl Measure {
97    #[inline]
98    #[allow(unused_variables)]
99    pub fn new(sketch: &Sketch) -> Measure {
100        Measure {
101            sketch: sketch.clone(), // clones Rc
102            start: Instant::now(),
103        }
104    }
105}
106
107impl Drop for Measure {
108    #[inline]
109    fn drop(&mut self) {
110        self.sketch.add(self.start.elapsed().as_nanos() as f64);
111    }
112}
113
114/// Profile the current scope with the given [Sketch]
115///
116/// # Example
117///
118/// ```rust
119/// use uwheel_stats::{profile_scope, Sketch};
120///
121/// let my_sketch = Sketch::default();
122/// {
123///     profile_scope!(&my_sketch);
124///     // do some work
125/// }
126/// ```
127#[macro_export]
128macro_rules! profile_scope {
129    ($id:expr) => {
130        let _measure_scope = $crate::Measure::new($id);
131    };
132}