const PLAT_BITS: u32 = 8;
const PLAT_VAL: u32 = 1 << PLAT_BITS;
const PLAT_GROUP_NR: usize = 19;
const PLAT_NR: usize = PLAT_GROUP_NR * PLAT_VAL as usize;
pub(crate) const PLIST: [f64; 5] = [20.0, 50.0, 90.0, 99.0, 99.9];
fn plat_val_to_idx(val: u32) -> usize {
let msb = if val == 0 {
0
} else {
31 - val.leading_zeros()
};
if msb <= PLAT_BITS {
return val as usize;
}
let error_bits = msb - PLAT_BITS;
let base = (error_bits + 1) << PLAT_BITS;
let offset = (PLAT_VAL - 1) & (val >> error_bits);
((base + offset) as usize).min(PLAT_NR - 1)
}
fn plat_idx_to_val(idx: usize) -> u32 {
debug_assert!(idx < PLAT_NR, "bucket index {idx} out of range");
if idx < (PLAT_VAL as usize) << 1 {
return idx as u32;
}
let error_bits = (idx >> PLAT_BITS) as u32 - 1;
let base = 1u32 << (error_bits + PLAT_BITS);
let k = (idx % PLAT_VAL as usize) as f64;
(base as f64 + (k + 0.5) * (1u32 << error_bits) as f64) as u32
}
#[derive(Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub(crate) struct PlatStats {
#[serde(with = "plat_array_serde")]
plat: Box<[u32; PLAT_NR]>,
nr_samples: u64,
max: u32,
min: u32,
}
mod plat_array_serde {
use super::PLAT_NR;
use serde::Deserialize;
use serde::de::Error as _;
#[allow(clippy::borrowed_box)]
pub(super) fn serialize<S>(arr: &Box<[u32; PLAT_NR]>, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.collect_seq(arr.iter())
}
pub(super) fn deserialize<'de, D>(deserializer: D) -> Result<Box<[u32; PLAT_NR]>, D::Error>
where
D: serde::Deserializer<'de>,
{
let v: Vec<u32> = Vec::deserialize(deserializer)?;
let len = v.len();
let boxed: Box<[u32]> = v.into_boxed_slice();
boxed.try_into().map_err(|_| {
D::Error::custom(format!(
"PlatStats histogram length {len} != PLAT_NR {PLAT_NR}"
))
})
}
}
impl Default for PlatStats {
fn default() -> Self {
Self {
plat: vec![0u32; PLAT_NR]
.into_boxed_slice()
.try_into()
.expect("PLAT_NR-length zeroed histogram"),
nr_samples: 0,
max: 0,
min: 0,
}
}
}
impl core::fmt::Debug for PlatStats {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PlatStats")
.field("nr_samples", &self.nr_samples)
.field("min", &self.min)
.field("max", &self.max)
.finish_non_exhaustive()
}
}
impl PlatStats {
pub(crate) fn add_lat(&mut self, us: u32) {
if us > self.max {
self.max = us;
}
if self.min == 0 || us < self.min {
self.min = us;
}
self.plat[plat_val_to_idx(us)] += 1;
self.nr_samples += 1;
}
pub(crate) fn combine(&mut self, other: &PlatStats) {
for i in 0..PLAT_NR {
self.plat[i] += other.plat[i];
}
self.nr_samples += other.nr_samples;
if other.max > self.max {
self.max = other.max;
}
if other.min != 0 && (self.min == 0 || other.min < self.min) {
self.min = other.min;
}
}
pub(crate) fn percentiles(&self) -> Percentiles {
let nr = self.nr_samples;
let len = PLIST.len();
let mut values = [0u32; 5];
let mut counts = [0u64; 5];
let mut sum: u64 = 0;
let mut j = 0usize;
let mut is_last = false;
let mut i = 0usize;
while i < PLAT_NR && !is_last {
sum += self.plat[i] as u64;
while (sum as f64) >= PLIST[j] / 100.0 * nr as f64 {
values[j] = plat_idx_to_val(i);
counts[j] = sum;
is_last = j == len - 1;
if is_last {
break;
}
j += 1;
}
i += 1;
}
let mut last = 0u64;
for idx in 1..len {
last += counts[idx - 1];
counts[idx] -= last;
}
Percentiles {
values,
counts,
min: self.min,
max: self.max,
nr_samples: nr,
}
}
pub(crate) fn take(&mut self) -> PlatStats {
std::mem::take(self)
}
pub(crate) fn sample_count(&self) -> u64 {
self.nr_samples
}
}
pub(crate) struct Percentiles {
pub(crate) values: [u32; 5],
pub(crate) counts: [u64; 5],
pub(crate) min: u32,
pub(crate) max: u32,
pub(crate) nr_samples: u64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Pct {
P20,
P50,
P90,
P99,
P999,
}
impl Pct {
fn index(self) -> usize {
match self {
Pct::P20 => 0,
Pct::P50 => 1,
Pct::P90 => 2,
Pct::P99 => 3,
Pct::P999 => 4,
}
}
}
impl Percentiles {
pub(crate) fn value_at(&self, p: Pct) -> u32 {
self.values[p.index()]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn plat_val_to_idx_identity_range() {
assert_eq!(plat_val_to_idx(0), 0);
assert_eq!(plat_val_to_idx(1), 1);
assert_eq!(plat_val_to_idx(255), 255);
assert_eq!(plat_val_to_idx(256), 256); assert_eq!(plat_val_to_idx(511), 511); }
#[test]
fn plat_val_to_idx_log_group_and_clamp() {
assert_eq!(plat_val_to_idx(512), 512);
assert_eq!(plat_val_to_idx(513), 512);
assert_eq!(plat_val_to_idx(u32::MAX), PLAT_NR - 1);
}
#[test]
fn plat_idx_round_trips_identity_range() {
for idx in [0usize, 1, 255, 256, 511] {
assert_eq!(plat_idx_to_val(idx), idx as u32);
}
}
#[test]
fn plat_idx_to_val_mean_of_range() {
assert_eq!(plat_idx_to_val(512), 513);
assert_eq!(plat_idx_to_val(513), 515);
}
#[test]
fn empty_histogram_yields_zero_percentiles_len5() {
let s = PlatStats::default();
let p = s.percentiles();
assert_eq!(p.nr_samples, 0);
assert_eq!(p.values, [0, 0, 0, 0, 0]);
assert_eq!(p.min, 0);
assert_eq!(p.max, 0);
assert_eq!(p.value_at(Pct::P99), 0);
}
#[test]
fn single_sample_all_percentiles_equal_that_value() {
let mut s = PlatStats::default();
s.add_lat(100);
let p = s.percentiles();
assert_eq!(p.nr_samples, 1);
assert_eq!(p.values, [100, 100, 100, 100, 100]);
assert_eq!(p.min, 100);
assert_eq!(p.max, 100);
assert_eq!(p.counts, [1, 0, 0, 0, 0]);
}
#[test]
fn zero_sample_records_bucket_but_not_min() {
let mut s = PlatStats::default();
s.add_lat(0);
let p = s.percentiles();
assert_eq!(p.nr_samples, 1);
assert_eq!(p.min, 0);
assert_eq!(p.max, 0);
assert_eq!(p.values, [0, 0, 0, 0, 0]);
}
#[test]
fn percentiles_split_across_buckets() {
let mut s = PlatStats::default();
for _ in 0..99 {
s.add_lat(10);
}
s.add_lat(10000);
let p = s.percentiles();
assert_eq!(p.nr_samples, 100);
assert_eq!(p.min, 10);
assert_eq!(p.max, 10000);
assert_eq!(p.value_at(Pct::P50), 10);
assert_eq!(p.value_at(Pct::P90), 10);
assert_eq!(p.value_at(Pct::P99), 10);
let p999 = p.value_at(Pct::P999);
assert!(p999 >= 10000, "p99.9 should be the tail bucket: {p999}");
}
#[test]
fn value_at_pins_each_pct_to_distinct_bucket() {
let mut s = PlatStats::default();
for _ in 0..250 {
s.add_lat(10); }
for _ in 0..300 {
s.add_lat(20); }
for _ in 0..370 {
s.add_lat(30); }
for _ in 0..75 {
s.add_lat(40); }
for _ in 0..5 {
s.add_lat(50); }
let p = s.percentiles();
assert_eq!(p.nr_samples, 1000);
assert_eq!(p.value_at(Pct::P20), 10);
assert_eq!(p.value_at(Pct::P50), 20);
assert_eq!(p.value_at(Pct::P90), 30);
assert_eq!(p.value_at(Pct::P99), 40);
assert_eq!(p.value_at(Pct::P999), 50);
}
#[test]
fn combine_folds_buckets_counts_and_extremes() {
let mut a = PlatStats::default();
a.add_lat(5);
a.add_lat(50);
let mut b = PlatStats::default();
b.add_lat(7);
b.add_lat(9000);
a.combine(&b);
let p = a.percentiles();
assert_eq!(p.nr_samples, 4);
assert_eq!(p.min, 5); assert_eq!(p.max, 9000);
assert_eq!(p.value_at(Pct::P20), 5);
assert_eq!(p.value_at(Pct::P50), 7);
assert_eq!(p.value_at(Pct::P90), 9008);
assert_eq!(p.value_at(Pct::P99), 9008);
assert_eq!(p.value_at(Pct::P999), 9008);
}
#[test]
fn combine_empty_does_not_zero_min() {
let mut a = PlatStats::default();
a.add_lat(42);
assert_eq!(a.percentiles().min, 42);
let empty = PlatStats::default();
a.combine(&empty);
assert_eq!(
a.percentiles().min,
42,
"guarded: folding an empty operand must not zero min",
);
assert_eq!(a.percentiles().nr_samples, 1, "empty fold adds no samples");
}
#[test]
fn take_snapshots_and_resets_for_per_phase() {
let mut s = PlatStats::default();
s.add_lat(100);
s.add_lat(200);
let phase1 = s.take();
assert_eq!(phase1.percentiles().nr_samples, 2);
assert_eq!(s.percentiles().nr_samples, 0);
s.add_lat(300);
assert_eq!(s.percentiles().nr_samples, 1);
assert_eq!(s.percentiles().min, 300);
}
#[test]
fn platstats_serde_roundtrips_json_and_postcard() {
let mut s = PlatStats::default();
s.add_lat(5);
s.add_lat(9000);
s.add_lat(0); let json = serde_json::to_string(&s).expect("serialize json");
let back: PlatStats = serde_json::from_str(&json).expect("deserialize json");
assert_eq!(s, back, "json roundtrip preserves histogram + extremes");
let bytes = postcard::to_stdvec(&s).expect("serialize postcard");
let back2: PlatStats = postcard::from_bytes(&bytes).expect("deserialize postcard");
assert_eq!(
s, back2,
"postcard roundtrip preserves histogram + extremes"
);
assert_eq!(back2.percentiles().values, s.percentiles().values);
}
#[test]
fn platstats_deserialize_wrong_len_is_fail_loud() {
let bad = r#"{"plat":[1,2,3],"nr_samples":0,"max":0,"min":0}"#;
let err = serde_json::from_str::<PlatStats>(bad).expect_err("wrong-len must fail");
assert!(
err.to_string().contains("PLAT_NR"),
"error names the length mismatch: {err}"
);
}
}