dx_forge/
profiler.rs

1//! Performance Profiler for Critical Paths
2//!
3//! Provides performance profiling, metrics collection, and bottleneck detection.
4
5use std::collections::HashMap;
6use std::sync::Arc;
7use std::time::{Duration, Instant};
8use parking_lot::RwLock;
9use serde::{Deserialize, Serialize};
10
11/// Performance metric
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct Metric {
14    pub name: String,
15    pub count: u64,
16    pub total_duration: Duration,
17    pub min_duration: Duration,
18    pub max_duration: Duration,
19    pub avg_duration: Duration,
20}
21
22impl Metric {
23    fn new(name: String) -> Self {
24        Self {
25            name,
26            count: 0,
27            total_duration: Duration::ZERO,
28            min_duration: Duration::MAX,
29            max_duration: Duration::ZERO,
30            avg_duration: Duration::ZERO,
31        }
32    }
33
34    fn record(&mut self, duration: Duration) {
35        self.count += 1;
36        self.total_duration += duration;
37        self.min_duration = self.min_duration.min(duration);
38        self.max_duration = self.max_duration.max(duration);
39        self.avg_duration = self.total_duration / self.count as u32;
40    }
41}
42
43/// Profiler for tracking performance metrics
44pub struct Profiler {
45    metrics: Arc<RwLock<HashMap<String, Metric>>>,
46    enabled: bool,
47}
48
49impl Profiler {
50    /// Create a new profiler
51    pub fn new() -> Self {
52        Self {
53            metrics: Arc::new(RwLock::new(HashMap::new())),
54            enabled: true,
55        }
56    }
57
58    /// Enable or disable profiling
59    pub fn set_enabled(&mut self, enabled: bool) {
60        self.enabled = enabled;
61    }
62
63    /// Start profiling a section
64    pub fn start(&self, name: &str) -> ProfileGuard {
65        ProfileGuard {
66            name: name.to_string(),
67            start: Instant::now(),
68            profiler: self.metrics.clone(),
69            enabled: self.enabled,
70        }
71    }
72
73    /// Get all metrics
74    pub fn get_metrics(&self) -> Vec<Metric> {
75        let metrics = self.metrics.read();
76        metrics.values().cloned().collect()
77    }
78
79    /// Get a specific metric
80    pub fn get_metric(&self, name: &str) -> Option<Metric> {
81        let metrics = self.metrics.read();
82        metrics.get(name).cloned()
83    }
84
85    /// Get slowest operations
86    pub fn get_slowest(&self, limit: usize) -> Vec<Metric> {
87        let metrics = self.metrics.read();
88        let mut sorted: Vec<_> = metrics.values().cloned().collect();
89        sorted.sort_by(|a, b| b.avg_duration.cmp(&a.avg_duration));
90        sorted.truncate(limit);
91        sorted
92    }
93
94    /// Get hottest operations (most called)
95    pub fn get_hottest(&self, limit: usize) -> Vec<Metric> {
96        let metrics = self.metrics.read();
97        let mut sorted: Vec<_> = metrics.values().cloned().collect();
98        sorted.sort_by(|a, b| b.count.cmp(&a.count));
99        sorted.truncate(limit);
100        sorted
101    }
102
103    /// Reset metrics
104    pub fn reset(&self) {
105        let mut metrics = self.metrics.write();
106        metrics.clear();
107    }
108
109    /// Print profiling summary
110    pub fn print_summary(&self) {
111        let metrics = self.get_metrics();
112        
113        if metrics.is_empty() {
114            println!("No profiling data collected");
115            return;
116        }
117
118        println!("\n=== Performance Profile ===\n");
119        println!("{:<40} {:>10} {:>15} {:>15} {:>15}",
120            "Operation", "Count", "Total (ms)", "Avg (ms)", "Max (ms)");
121        println!("{:-<100}", "");
122
123        for metric in metrics {
124            println!("{:<40} {:>10} {:>15.2} {:>15.2} {:>15.2}",
125                metric.name,
126                metric.count,
127                metric.total_duration.as_secs_f64() * 1000.0,
128                metric.avg_duration.as_secs_f64() * 1000.0,
129                metric.max_duration.as_secs_f64() * 1000.0,
130            );
131        }
132
133        println!("\n== Slowest Operations ==");
134        for (i, metric) in self.get_slowest(5).iter().enumerate() {
135            println!("{}. {} - {:.2}ms average",
136                i + 1,
137                metric.name,
138                metric.avg_duration.as_secs_f64() * 1000.0
139            );
140        }
141
142        println!("\n== Hottest Operations ==");
143        for (i, metric) in self.get_hottest(5).iter().enumerate() {
144            println!("{}. {} - {} calls",
145                i + 1,
146                metric.name,
147                metric.count
148            );
149        }
150        println!();
151    }
152
153    /// Record a manual measurement
154    pub fn record(&self, name: &str, duration: Duration) {
155        if !self.enabled {
156            return;
157        }
158
159        let mut metrics = self.metrics.write();
160        let metric = metrics
161            .entry(name.to_string())
162            .or_insert_with(|| Metric::new(name.to_string()));
163        metric.record(duration);
164    }
165}
166
167impl Default for Profiler {
168    fn default() -> Self {
169        Self::new()
170    }
171}
172
173/// RAII guard for automatic profiling
174pub struct ProfileGuard {
175    name: String,
176    start: Instant,
177    profiler: Arc<RwLock<HashMap<String, Metric>>>,
178    enabled: bool,
179}
180
181impl Drop for ProfileGuard {
182    fn drop(&mut self) {
183        if !self.enabled {
184            return;
185        }
186
187        let duration = self.start.elapsed();
188        let mut metrics = self.profiler.write();
189        let metric = metrics
190            .entry(self.name.clone())
191            .or_insert_with(|| Metric::new(self.name.clone()));
192        metric.record(duration);
193    }
194}
195
196/// Global profiler instance
197static GLOBAL_PROFILER: once_cell::sync::Lazy<Profiler> = 
198    once_cell::sync::Lazy::new(|| Profiler::new());
199
200/// Profile a code block
201#[allow(unused_macros)]
202#[macro_export]
203macro_rules! profile {
204    ($name:expr) => {
205        let _guard = $crate::profiler::GLOBAL_PROFILER.start($name);
206    };
207}
208
209/// Get the global profiler
210pub fn global_profiler() -> &'static Profiler {
211    &GLOBAL_PROFILER
212}
213
214#[cfg(test)]
215mod tests {
216    use super::*;
217    use std::thread;
218
219    #[test]
220    fn test_profiler() {
221        let profiler = Profiler::new();
222
223        {
224            let _guard = profiler.start("test_operation");
225            thread::sleep(Duration::from_millis(10));
226        }
227
228        let metrics = profiler.get_metrics();
229        assert_eq!(metrics.len(), 1);
230        assert_eq!(metrics[0].count, 1);
231        assert!(metrics[0].total_duration >= Duration::from_millis(10));
232    }
233
234    #[test]
235    fn test_multiple_measurements() {
236        let profiler = Profiler::new();
237
238        for _ in 0..5 {
239            let _guard = profiler.start("repeated_operation");
240            thread::sleep(Duration::from_millis(5));
241        }
242
243        let metric = profiler.get_metric("repeated_operation").unwrap();
244        assert_eq!(metric.count, 5);
245    }
246}