Skip to main content

ruvector_profiler/
latency.rs

1#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
2pub struct LatencyRecord {
3    pub sample_id: usize,
4    pub wall_time_us: u64,
5    pub kernel_time_us: u64,
6    pub seq_len: usize,
7}
8
9#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
10pub struct LatencyStats {
11    pub p50_us: u64,
12    pub p95_us: u64,
13    pub p99_us: u64,
14    pub mean_us: f64,
15    pub std_us: f64,
16    pub n: usize,
17}
18
19/// Compute percentile and summary statistics from wall-time latencies.
20pub fn compute_latency_stats(records: &[LatencyRecord]) -> LatencyStats {
21    let n = records.len();
22    if n == 0 {
23        return LatencyStats {
24            p50_us: 0,
25            p95_us: 0,
26            p99_us: 0,
27            mean_us: 0.0,
28            std_us: 0.0,
29            n: 0,
30        };
31    }
32    let mut times: Vec<u64> = records.iter().map(|r| r.wall_time_us).collect();
33    times.sort_unstable();
34    let mean = times.iter().sum::<u64>() as f64 / n as f64;
35    let var = times
36        .iter()
37        .map(|&t| (t as f64 - mean).powi(2))
38        .sum::<f64>()
39        / n as f64;
40    LatencyStats {
41        p50_us: pctl(&times, 50.0),
42        p95_us: pctl(&times, 95.0),
43        p99_us: pctl(&times, 99.0),
44        mean_us: mean,
45        std_us: var.sqrt(),
46        n,
47    }
48}
49
50fn pctl(sorted: &[u64], p: f64) -> u64 {
51    let idx = ((p / 100.0 * sorted.len() as f64).ceil() as usize)
52        .min(sorted.len())
53        .saturating_sub(1);
54    sorted[idx]
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60    fn recs(ts: &[u64]) -> Vec<LatencyRecord> {
61        ts.iter()
62            .enumerate()
63            .map(|(i, &t)| LatencyRecord {
64                sample_id: i,
65                wall_time_us: t,
66                kernel_time_us: t,
67                seq_len: 128,
68            })
69            .collect()
70    }
71
72    #[test]
73    fn empty() {
74        assert_eq!(compute_latency_stats(&[]).n, 0);
75    }
76    #[test]
77    fn single() {
78        let s = compute_latency_stats(&recs(&[42]));
79        assert_eq!((s.p50_us, s.p99_us, s.n), (42, 42, 1));
80    }
81    #[test]
82    fn multi() {
83        let s = compute_latency_stats(&recs(&[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]));
84        assert_eq!(s.p50_us, 50);
85        assert!((s.mean_us - 55.0).abs() < 1e-9);
86    }
87    #[test]
88    fn unsorted() {
89        assert_eq!(
90            compute_latency_stats(&recs(&[100, 10, 50, 90, 20])).p50_us,
91            50
92        );
93    }
94}