1use std::collections::HashMap;
6use std::sync::Arc;
7use std::time::{Duration, Instant};
8use parking_lot::RwLock;
9use serde::{Deserialize, Serialize};
10
11#[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
43pub struct Profiler {
45 metrics: Arc<RwLock<HashMap<String, Metric>>>,
46 enabled: bool,
47}
48
49impl Profiler {
50 pub fn new() -> Self {
52 Self {
53 metrics: Arc::new(RwLock::new(HashMap::new())),
54 enabled: true,
55 }
56 }
57
58 pub fn set_enabled(&mut self, enabled: bool) {
60 self.enabled = enabled;
61 }
62
63 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 pub fn get_metrics(&self) -> Vec<Metric> {
75 let metrics = self.metrics.read();
76 metrics.values().cloned().collect()
77 }
78
79 pub fn get_metric(&self, name: &str) -> Option<Metric> {
81 let metrics = self.metrics.read();
82 metrics.get(name).cloned()
83 }
84
85 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 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 pub fn reset(&self) {
105 let mut metrics = self.metrics.write();
106 metrics.clear();
107 }
108
109 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 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
173pub 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
196static GLOBAL_PROFILER: once_cell::sync::Lazy<Profiler> =
198 once_cell::sync::Lazy::new(|| Profiler::new());
199
200#[allow(unused_macros)]
202#[macro_export]
203macro_rules! profile {
204 ($name:expr) => {
205 let _guard = $crate::profiler::GLOBAL_PROFILER.start($name);
206 };
207}
208
209pub 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}