use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LatencyStats {
pub min: Duration,
pub max: Duration,
pub mean: Duration,
pub p50: Duration,
pub p95: Duration,
pub p99: Duration,
}
impl Default for LatencyStats {
fn default() -> Self {
Self {
min: Duration::ZERO,
max: Duration::ZERO,
mean: Duration::ZERO,
p50: Duration::ZERO,
p95: Duration::ZERO,
p99: Duration::ZERO,
}
}
}
#[must_use]
pub fn compute_latency_percentiles(samples: &[Duration]) -> LatencyStats {
if samples.is_empty() {
return LatencyStats::default();
}
let mut sorted: Vec<Duration> = samples.to_vec();
sorted.sort();
let n = sorted.len();
let sum: Duration = sorted.iter().sum();
#[allow(clippy::cast_possible_truncation)]
let mean = if n > 0 {
Duration::from_nanos((sum.as_nanos() / n as u128) as u64)
} else {
Duration::ZERO
};
LatencyStats {
min: sorted[0],
max: sorted[n - 1],
mean,
p50: percentile(&sorted, 50),
p95: percentile(&sorted, 95),
p99: percentile(&sorted, 99),
}
}
fn percentile(sorted: &[Duration], p: usize) -> Duration {
if sorted.is_empty() {
return Duration::ZERO;
}
let n = sorted.len();
#[allow(
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss
)]
let idx = ((p as f64 / 100.0) * (n - 1) as f64).round() as usize;
sorted[idx.min(n - 1)]
}
#[cfg(test)]
#[path = "latency_tests.rs"]
mod tests;