1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::sync::Arc;
9use std::sync::atomic::{AtomicU64, Ordering};
10use std::time::{Duration, Instant};
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct Metrics {
15 pub throughput: u64,
17 pub latency: u64,
19 pub memory_usage: u64,
21 pub lock_wait_time: u64,
23 pub timestamp: u64,
25 pub label: Option<String>,
27}
28
29impl Metrics {
30 pub fn new(throughput: u64, latency: u64, memory_usage: u64, lock_wait_time: u64) -> Self {
32 Self {
33 throughput,
34 latency,
35 memory_usage,
36 lock_wait_time,
37 timestamp: std::time::SystemTime::now()
38 .duration_since(std::time::UNIX_EPOCH)
39 .unwrap_or_default()
40 .as_millis() as u64,
41 label: None,
42 }
43 }
44
45 pub fn with_label(mut self, label: impl Into<String>) -> Self {
47 self.label = Some(label.into());
48 self
49 }
50
51 pub fn performance_score(&self) -> f64 {
53 let throughput_score = (self.throughput as f64 / 1_000_000.0).min(50.0); let latency_penalty = (self.latency as f64 / 100.0).min(25.0); let lock_penalty = (self.lock_wait_time as f64 / 100.0).min(25.0); (throughput_score + 50.0 - latency_penalty - lock_penalty).clamp(0.0, 100.0)
59 }
60}
61
62impl Default for Metrics {
63 fn default() -> Self {
64 Self::new(0, 0, 0, 0)
65 }
66}
67
68#[derive(Debug)]
70pub struct AtomicMetrics {
71 throughput: AtomicU64,
72 latency: AtomicU64,
73 memory_usage: AtomicU64,
74 lock_wait_time: AtomicU64,
75 start_time: Instant,
76}
77
78impl AtomicMetrics {
79 pub fn new() -> Self {
81 Self {
82 throughput: AtomicU64::new(0),
83 latency: AtomicU64::new(0),
84 memory_usage: AtomicU64::new(0),
85 lock_wait_time: AtomicU64::new(0),
86 start_time: Instant::now(),
87 }
88 }
89
90 #[inline]
92 pub fn record_throughput(&self, bytes_per_sec: u64) {
93 self.throughput.store(bytes_per_sec, Ordering::Relaxed);
94 }
95
96 #[inline]
98 pub fn record_latency(&self, ms: u64) {
99 self.latency.store(ms, Ordering::Relaxed);
100 }
101
102 #[inline]
104 pub fn record_memory(&self, bytes: u64) {
105 self.memory_usage.store(bytes, Ordering::Relaxed);
106 }
107
108 #[inline]
110 pub fn record_lock_wait(&self, ms: u64) {
111 self.lock_wait_time.fetch_add(ms, Ordering::Relaxed);
112 }
113
114 pub fn snapshot(&self) -> Metrics {
116 Metrics::new(
117 self.throughput.load(Ordering::Relaxed),
118 self.latency.load(Ordering::Relaxed),
119 self.memory_usage.load(Ordering::Relaxed),
120 self.lock_wait_time.load(Ordering::Relaxed),
121 )
122 }
123
124 pub fn elapsed(&self) -> Duration {
126 self.start_time.elapsed()
127 }
128
129 pub fn reset(&self) {
131 self.throughput.store(0, Ordering::Relaxed);
132 self.latency.store(0, Ordering::Relaxed);
133 self.memory_usage.store(0, Ordering::Relaxed);
134 self.lock_wait_time.store(0, Ordering::Relaxed);
135 }
136}
137
138impl Default for AtomicMetrics {
139 fn default() -> Self {
140 Self::new()
141 }
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct PerformanceReport {
147 pub metrics: HashMap<String, Vec<Metrics>>,
149 pub generated_at: u64,
151 pub duration_ms: u64,
153 pub summary: ReportSummary,
155}
156
157impl PerformanceReport {
158 pub fn new(metrics: HashMap<String, Vec<Metrics>>, duration_ms: u64) -> Self {
160 let summary = Self::calculate_summary(&metrics);
161 Self {
162 metrics,
163 generated_at: std::time::SystemTime::now()
164 .duration_since(std::time::UNIX_EPOCH)
165 .unwrap_or_default()
166 .as_millis() as u64,
167 duration_ms,
168 summary,
169 }
170 }
171
172 fn calculate_summary(metrics: &HashMap<String, Vec<Metrics>>) -> ReportSummary {
174 let mut total_throughput = 0u64;
175 let mut total_latency = 0u64;
176 let mut total_memory = 0u64;
177 let mut total_lock_wait = 0u64;
178 let mut count = 0usize;
179
180 for metric_list in metrics.values() {
181 for m in metric_list.iter() {
182 total_throughput += m.throughput;
183 total_latency += m.latency;
184 total_memory += m.memory_usage;
185 total_lock_wait += m.lock_wait_time;
186 count += 1;
187 }
188 }
189
190 ReportSummary {
191 avg_throughput: if count > 0 {
192 total_throughput / count as u64
193 } else {
194 0
195 },
196 avg_latency: if count > 0 {
197 total_latency / count as u64
198 } else {
199 0
200 },
201 avg_memory_usage: if count > 0 {
202 total_memory / count as u64
203 } else {
204 0
205 },
206 avg_lock_wait_time: if count > 0 {
207 total_lock_wait / count as u64
208 } else {
209 0
210 },
211 total_samples: count,
212 }
213 }
214}
215
216#[derive(Debug, Clone, Serialize, Deserialize)]
218pub struct ReportSummary {
219 pub avg_throughput: u64,
221 pub avg_latency: u64,
223 pub avg_memory_usage: u64,
225 pub avg_lock_wait_time: u64,
227 pub total_samples: usize,
229}
230
231pub struct PerformanceMonitor {
233 metrics: Arc<tokio::sync::RwLock<HashMap<String, Vec<Metrics>>>>,
234 start_time: Instant,
235}
236
237impl PerformanceMonitor {
238 pub fn new() -> Self {
240 Self {
241 metrics: Arc::new(tokio::sync::RwLock::new(HashMap::new())),
242 start_time: Instant::now(),
243 }
244 }
245
246 pub fn elapsed(&self) -> Duration {
248 self.start_time.elapsed()
249 }
250}
251
252impl Default for PerformanceMonitor {
253 fn default() -> Self {
254 Self::new()
255 }
256}
257
258impl PerformanceMonitor {
259 pub fn record_metric(&self, label: &str, metrics: Metrics) {
261 if let Ok(mut guard) = self.metrics.try_write() {
265 guard
266 .entry(label.to_string())
267 .or_insert_with(Vec::new)
268 .push(metrics);
269 }
270 }
271
272 pub fn generate_report(&self) -> PerformanceReport {
274 let metrics = self
277 .metrics
278 .try_read()
279 .map(|g| g.clone())
280 .unwrap_or_else(|_| HashMap::new());
281 PerformanceReport::new(metrics, self.elapsed().as_millis() as u64)
282 }
283
284 pub fn export_json(&self) -> String {
286 let report = self.generate_report();
287 serde_json::to_string_pretty(&report).unwrap_or_else(|_| "{}".to_string())
288 }
289
290 pub fn export_text(&self) -> String {
292 let report = self.generate_report();
293 let mut output = String::new();
294
295 output.push_str("Performance Report\n");
296 output.push_str("==================\n");
297 output.push_str(&format!("Generated at: {} ms\n", report.generated_at));
298 output.push_str(&format!("Duration: {} ms\n\n", report.duration_ms));
299
300 output.push_str("Summary:\n");
301 output.push_str("--------\n");
302 output.push_str(&format!(
303 " Total samples: {}\n",
304 report.summary.total_samples
305 ));
306 output.push_str(&format!(
307 " Avg throughput: {} bytes/sec\n",
308 report.summary.avg_throughput
309 ));
310 output.push_str(&format!(
311 " Avg latency: {} ms\n",
312 report.summary.avg_latency
313 ));
314 output.push_str(&format!(
315 " Avg memory usage: {} bytes\n",
316 report.summary.avg_memory_usage
317 ));
318 output.push_str(&format!(
319 " Avg lock wait time: {} ms\n\n",
320 report.summary.avg_lock_wait_time
321 ));
322
323 output.push_str("Detailed Metrics:\n");
324 output.push_str("-----------------\n");
325 for (label, metrics_list) in &report.metrics {
326 output.push_str(&format!("\n[{}]\n", label));
327 for (i, m) in metrics_list.iter().enumerate() {
328 output.push_str(&format!(
329 " Sample {}: throughput={} B/s, latency={} ms, memory={} B, lock_wait={} ms\n",
330 i + 1,
331 m.throughput,
332 m.latency,
333 m.memory_usage,
334 m.lock_wait_time
335 ));
336 }
337 }
338
339 output
340 }
341}
342
343pub struct ScopedTimer {
345 label: String,
346 start: Instant,
347 monitor: Arc<PerformanceMonitor>,
348}
349
350impl ScopedTimer {
351 pub fn new(label: impl Into<String>, monitor: Arc<PerformanceMonitor>) -> Self {
353 Self {
354 label: label.into(),
355 start: Instant::now(),
356 monitor,
357 }
358 }
359}
360
361impl Drop for ScopedTimer {
362 fn drop(&mut self) {
363 let elapsed = self.start.elapsed().as_millis() as u64;
364 let metrics = Metrics::new(0, elapsed, 0, 0).with_label(&self.label);
365 self.monitor.record_metric(&self.label, metrics);
366 }
367}
368
369#[macro_export]
371macro_rules! scoped_perf_timer {
372 ($label:expr, $monitor:expr) => {
373 $crate::util::perf_monitor::ScopedTimer::new($label, $monitor)
374 };
375}
376
377#[cfg(test)]
378mod tests {
379 use super::*;
380
381 #[test]
382 fn test_metrics_creation() {
383 let m = Metrics::new(1000, 50, 1024 * 1024, 10);
384 assert_eq!(m.throughput, 1000);
385 assert_eq!(m.latency, 50);
386 assert_eq!(m.memory_usage, 1024 * 1024);
387 assert_eq!(m.lock_wait_time, 10);
388 assert!(m.timestamp > 0);
389 }
390
391 #[test]
392 fn test_metrics_with_label() {
393 let m = Metrics::new(1000, 50, 1024, 10).with_label("download");
394 assert_eq!(m.label, Some("download".to_string()));
395 }
396
397 #[test]
398 fn test_performance_score() {
399 let m1 = Metrics::new(10_000_000, 10, 1024, 5);
401 let score1 = m1.performance_score();
402 assert!(score1 > 50.0, "Score should be > 50, got {}", score1);
403
404 let m2 = Metrics::new(1000, 1000, 1024, 100);
406 let score2 = m2.performance_score();
407 assert!(score2 < score1, "Score should be lower, got {}", score2);
408 }
409
410 #[test]
411 fn test_atomic_metrics() {
412 let am = AtomicMetrics::new();
413 am.record_throughput(1000);
414 am.record_latency(50);
415 am.record_memory(1024);
416 am.record_lock_wait(10);
417
418 let snapshot = am.snapshot();
419 assert_eq!(snapshot.throughput, 1000);
420 assert_eq!(snapshot.latency, 50);
421 assert_eq!(snapshot.memory_usage, 1024);
422 assert_eq!(snapshot.lock_wait_time, 10);
423 }
424
425 #[test]
426 fn test_atomic_metrics_reset() {
427 let am = AtomicMetrics::new();
428 am.record_throughput(1000);
429 am.record_latency(50);
430 am.reset();
431
432 let snapshot = am.snapshot();
433 assert_eq!(snapshot.throughput, 0);
434 assert_eq!(snapshot.latency, 0);
435 }
436
437 #[test]
438 fn test_performance_monitor() {
439 let monitor = PerformanceMonitor::new();
440 let m1 = Metrics::new(1000, 50, 1024, 10);
441 let m2 = Metrics::new(2000, 30, 2048, 5);
442
443 monitor.record_metric("download", m1);
444 monitor.record_metric("download", m2);
445
446 let report = monitor.generate_report();
447 assert!(report.metrics.contains_key("download"));
448 assert_eq!(report.metrics.get("download").unwrap().len(), 2);
449 assert_eq!(report.summary.total_samples, 2);
450 assert_eq!(report.summary.avg_throughput, 1500); }
452
453 #[test]
454 fn test_export_json() {
455 let monitor = PerformanceMonitor::new();
456 let m = Metrics::new(1000, 50, 1024, 10);
457 monitor.record_metric("test", m);
458
459 let json = monitor.export_json();
460 assert!(json.contains("test"));
461 assert!(json.contains("throughput"));
462 }
463
464 #[test]
465 fn test_export_text() {
466 let monitor = PerformanceMonitor::new();
467 let m = Metrics::new(1000, 50, 1024, 10);
468 monitor.record_metric("test", m);
469
470 let text = monitor.export_text();
471 assert!(text.contains("Performance Report"));
472 assert!(text.contains("test"));
473 assert!(text.contains("1000 B/s"));
474 }
475
476 #[test]
477 fn test_scoped_timer() {
478 let monitor = Arc::new(PerformanceMonitor::new());
479 {
480 let _timer = ScopedTimer::new("operation", monitor.clone());
481 std::thread::sleep(std::time::Duration::from_millis(10));
482 }
483
484 let report = monitor.generate_report();
485 assert!(report.metrics.contains_key("operation"));
486 let metrics = report.metrics.get("operation").unwrap();
487 assert!(!metrics.is_empty());
488 assert!(metrics[0].latency >= 10);
489 }
490
491 #[tokio::test]
492 async fn test_concurrent_recording() {
493 let monitor = Arc::new(PerformanceMonitor::new());
494 let mut handles = vec![];
495
496 for i in 0..10 {
497 let m = monitor.clone();
498 handles.push(tokio::spawn(async move {
499 let metric = Metrics::new(i * 100, i * 10, i * 1024, i);
500 m.record_metric("concurrent", metric);
501 }));
502 }
503
504 for handle in handles {
505 handle.await.unwrap();
506 }
507
508 let report = monitor.generate_report();
509 assert!(report.metrics.contains_key("concurrent"));
510 }
513}