use crate::Counter;
pub struct Histogram {
bounds: Vec<u64>,
buckets: Vec<Counter>,
sum: Counter,
count: Counter,
}
impl Histogram {
pub fn new(bounds: &[u64], shard_count: usize) -> Self {
let buckets = (0..=bounds.len())
.map(|_| Counter::new(shard_count))
.collect();
Self {
bounds: bounds.to_vec(),
buckets,
sum: Counter::new(shard_count),
count: Counter::new(shard_count),
}
}
pub fn with_latency_buckets(shard_count: usize) -> Self {
Self::new(
&[
10, 50, 100, 500, 1_000, 5_000, 10_000, 50_000, 100_000, 500_000, 1_000_000, 5_000_000, 10_000_000, ],
shard_count,
)
}
#[inline]
pub fn record(&self, value: u64) {
let bucket_idx = self
.bounds
.iter()
.position(|&bound| value <= bound)
.unwrap_or(self.bounds.len());
self.buckets[bucket_idx].inc();
self.sum.add(value as isize);
self.count.inc();
}
pub fn buckets_cumulative(&self) -> Vec<(u64, u64)> {
self.buckets_cumulative_iter().collect()
}
pub fn buckets_cumulative_iter(&self) -> impl Iterator<Item = (u64, u64)> + '_ {
let mut cumulative = 0i64;
self.buckets.iter().enumerate().map(move |(i, counter)| {
cumulative += counter.sum() as i64;
let bound = if i < self.bounds.len() {
self.bounds[i]
} else {
u64::MAX
};
(bound, cumulative as u64)
})
}
pub fn sum(&self) -> u64 {
self.sum.sum() as u64
}
pub fn count(&self) -> u64 {
self.count.sum() as u64
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic_recording() {
let h = Histogram::new(&[10, 100, 1000], 4);
h.record(5); h.record(50); h.record(500); h.record(5000);
let buckets = h.buckets_cumulative();
assert_eq!(buckets.len(), 4);
assert_eq!(buckets[0], (10, 1)); assert_eq!(buckets[1], (100, 2)); assert_eq!(buckets[2], (1000, 3)); assert_eq!(buckets[3], (u64::MAX, 4));
assert_eq!(h.count(), 4);
assert_eq!(h.sum(), 5 + 50 + 500 + 5000);
}
#[test]
fn test_boundary_values() {
let h = Histogram::new(&[10, 100], 4);
h.record(10); h.record(100);
let buckets = h.buckets_cumulative();
assert_eq!(buckets[0], (10, 1));
assert_eq!(buckets[1], (100, 2));
}
#[test]
fn test_latency_buckets() {
let h = Histogram::with_latency_buckets(4);
h.record(5); h.record(1_000); h.record(1_000_000);
assert_eq!(h.count(), 3);
}
}