agentic_robotics_rt/
latency.rs1use hdrhistogram::Histogram;
4use parking_lot::Mutex;
5use std::sync::Arc;
6use std::time::{Duration, Instant};
7
8pub struct LatencyTracker {
10 histogram: Arc<Mutex<Histogram<u64>>>,
11 name: String,
12}
13
14impl LatencyTracker {
15 pub fn new(name: impl Into<String>) -> Self {
17 let histogram = Histogram::<u64>::new(3)
19 .expect("Failed to create histogram");
20
21 Self {
22 histogram: Arc::new(Mutex::new(histogram)),
23 name: name.into(),
24 }
25 }
26
27 pub fn record(&self, duration: Duration) {
29 let micros = duration.as_micros() as u64;
30 if let Some(mut hist) = self.histogram.try_lock() {
31 let _ = hist.record(micros);
32 }
33 }
34
35 pub fn stats(&self) -> LatencyStats {
37 let hist = self.histogram.lock();
38
39 LatencyStats {
40 name: self.name.clone(),
41 count: hist.len(),
42 min: hist.min(),
43 max: hist.max(),
44 mean: hist.mean(),
45 p50: hist.value_at_quantile(0.50),
46 p90: hist.value_at_quantile(0.90),
47 p99: hist.value_at_quantile(0.99),
48 p999: hist.value_at_quantile(0.999),
49 }
50 }
51
52 pub fn reset(&self) {
54 self.histogram.lock().reset();
55 }
56
57 pub fn measure(&self) -> LatencyMeasurement {
59 LatencyMeasurement {
60 tracker: self.clone(),
61 start: Instant::now(),
62 }
63 }
64}
65
66impl Clone for LatencyTracker {
67 fn clone(&self) -> Self {
68 Self {
69 histogram: self.histogram.clone(),
70 name: self.name.clone(),
71 }
72 }
73}
74
75#[derive(Debug, Clone)]
77pub struct LatencyStats {
78 pub name: String,
79 pub count: u64,
80 pub min: u64,
81 pub max: u64,
82 pub mean: f64,
83 pub p50: u64,
84 pub p90: u64,
85 pub p99: u64,
86 pub p999: u64,
87}
88
89impl std::fmt::Display for LatencyStats {
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 write!(
92 f,
93 "{}: count={}, min={}µs, max={}µs, mean={:.2}µs, p50={}µs, p90={}µs, p99={}µs, p99.9={}µs",
94 self.name, self.count, self.min, self.max, self.mean, self.p50, self.p90, self.p99, self.p999
95 )
96 }
97}
98
99pub struct LatencyMeasurement {
101 tracker: LatencyTracker,
102 start: Instant,
103}
104
105impl Drop for LatencyMeasurement {
106 fn drop(&mut self) {
107 let duration = self.start.elapsed();
108 self.tracker.record(duration);
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[test]
117 fn test_latency_tracker() {
118 let tracker = LatencyTracker::new("test");
119
120 tracker.record(Duration::from_micros(100));
122 tracker.record(Duration::from_micros(200));
123 tracker.record(Duration::from_micros(300));
124
125 let stats = tracker.stats();
126 assert_eq!(stats.count, 3);
127 assert!(stats.min >= 100);
128 assert!(stats.max <= 300);
129 assert!(stats.mean > 0.0);
130 }
131
132 #[test]
133 fn test_latency_measurement() {
134 let tracker = LatencyTracker::new("measurement");
135
136 {
137 let _measurement = tracker.measure();
138 std::thread::sleep(Duration::from_micros(100));
139 }
140
141 let stats = tracker.stats();
142 assert_eq!(stats.count, 1);
143 assert!(stats.min >= 100);
144 }
145}