use std::mem;
use super::MetricKind;
use crate::protocol::Distribution;
pub(crate) struct Histogram(SegVec<f64>);
impl MetricKind for Histogram {
type Output = Distribution;
type Shared = ();
type Value = f64;
fn new(_: Self::Shared) -> Self {
Self(SegVec::default())
}
fn update(&mut self, value: Self::Value) {
self.0.push(value);
}
fn merge(self, out: &mut Self::Output) -> usize {
let segments = self.0.into_segments();
let mut additional_size = segments.capacity() * mem::size_of::<Vec<f64>>();
for segment in segments {
out.add(&segment);
additional_size += segment.capacity() * mem::size_of::<f64>();
}
additional_size
}
}
struct SegVec<T> {
active: Vec<T>,
bag: Vec<Vec<T>>,
}
const INITIAL_LIMIT: usize = 8;
const GROWTH_FACTOR: usize = 4;
impl<T> Default for SegVec<T> {
fn default() -> Self {
Self {
active: vec_with_exact_capacity(INITIAL_LIMIT),
bag: Vec::new(),
}
}
}
impl<T> SegVec<T> {
fn push(&mut self, value: T) {
if self.active.len() == self.active.capacity() {
self.new_segment();
}
#[cfg(test)]
let capacity = self.active.capacity();
self.active.push(value);
#[cfg(test)]
assert_eq!(self.active.capacity(), capacity);
}
#[cold]
fn new_segment(&mut self) {
let new = vec_with_exact_capacity(self.active.capacity() * GROWTH_FACTOR);
let old = mem::replace(&mut self.active, new);
self.bag.push(old);
}
fn into_segments(mut self) -> Vec<Vec<T>> {
if !self.active.is_empty() {
self.bag.push(self.active);
}
self.bag
}
}
fn vec_with_exact_capacity<T>(capacity: usize) -> Vec<T> {
let mut vec = Vec::new();
vec.reserve_exact(capacity);
debug_assert_eq!(vec.capacity(), capacity);
vec
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn seg_vec() {
let mut vec = SegVec::default();
let iters = 2024;
for i in 0..iters {
vec.push(i);
}
let segments = vec.into_segments();
for (no, segment) in segments.iter().enumerate() {
let expected_capacity = INITIAL_LIMIT * GROWTH_FACTOR.pow(no as u32);
assert_eq!(segment.capacity(), expected_capacity);
if no + 1 == segments.len() {
assert_ne!(segment.len(), 0);
} else {
assert_eq!(segment.len(), segment.capacity());
}
}
assert_eq!(
segments.iter().map(|segment| segment.len()).sum::<usize>(),
iters
);
segments
.into_iter()
.flatten()
.enumerate()
.for_each(|(i, v)| {
assert_eq!(i, v);
});
}
}