1use crate::prelude::HashMap;
7#[allow(unused_imports)]
8use crate::prelude::*;
9use std::time::{Duration, Instant};
10
11#[derive(Debug)]
13pub struct ScopedTimer {
14 name: String,
16 start: Instant,
18 running: bool,
20 elapsed: Duration,
22}
23
24impl ScopedTimer {
25 #[must_use]
27 pub fn new(name: impl Into<String>) -> Self {
28 Self {
29 name: name.into(),
30 start: Instant::now(),
31 running: true,
32 elapsed: Duration::ZERO,
33 }
34 }
35
36 pub fn stop(&mut self) {
38 if self.running {
39 self.elapsed += self.start.elapsed();
40 self.running = false;
41 }
42 }
43
44 pub fn restart(&mut self) {
46 if !self.running {
47 self.start = Instant::now();
48 self.running = true;
49 }
50 }
51
52 #[must_use]
54 pub fn elapsed(&self) -> Duration {
55 if self.running {
56 self.elapsed + self.start.elapsed()
57 } else {
58 self.elapsed
59 }
60 }
61
62 #[must_use]
64 pub fn name(&self) -> &str {
65 &self.name
66 }
67}
68
69impl Drop for ScopedTimer {
70 fn drop(&mut self) {
71 if self.running {
72 self.stop();
73 }
74 }
75}
76
77#[derive(Debug, Default)]
79pub struct Profiler {
80 timers: HashMap<String, Duration>,
82 counters: HashMap<String, u64>,
84 active: HashMap<String, Instant>,
86}
87
88impl Profiler {
89 #[must_use]
91 pub fn new() -> Self {
92 Self::default()
93 }
94
95 pub fn start_timer(&mut self, name: impl Into<String>) {
97 let name = name.into();
98 self.active.insert(name, Instant::now());
99 }
100
101 pub fn stop_timer(&mut self, name: &str) {
103 if let Some(start) = self.active.remove(name) {
104 let elapsed = start.elapsed();
105 *self
106 .timers
107 .entry(name.to_string())
108 .or_insert(Duration::ZERO) += elapsed;
109 }
110 }
111
112 pub fn increment(&mut self, name: impl Into<String>) {
114 self.increment_by(name, 1);
115 }
116
117 pub fn increment_by(&mut self, name: impl Into<String>, amount: u64) {
119 *self.counters.entry(name.into()).or_insert(0) += amount;
120 }
121
122 #[must_use]
124 pub fn get_timer(&self, name: &str) -> Option<Duration> {
125 self.timers.get(name).copied()
126 }
127
128 #[must_use]
130 pub fn get_counter(&self, name: &str) -> Option<u64> {
131 self.counters.get(name).copied()
132 }
133
134 #[must_use]
136 pub fn timers(&self) -> &HashMap<String, Duration> {
137 &self.timers
138 }
139
140 #[must_use]
142 pub fn counters(&self) -> &HashMap<String, u64> {
143 &self.counters
144 }
145
146 pub fn clear(&mut self) {
148 self.timers.clear();
149 self.counters.clear();
150 self.active.clear();
151 }
152
153 #[must_use]
155 pub fn total_time(&self) -> Duration {
156 self.timers.values().sum()
157 }
158
159 #[must_use]
161 pub fn report(&self) -> String {
162 let mut output = String::new();
163
164 output.push_str("═══════════════════════════════════════════════════════════════\n");
165 output.push_str(" PROFILING REPORT \n");
166 output.push_str("═══════════════════════════════════════════════════════════════\n\n");
167
168 if !self.timers.is_empty() {
169 output.push_str("TIMERS:\n");
170 let mut timer_vec: Vec<_> = self.timers.iter().collect();
171 timer_vec.sort_by(|a, b| b.1.cmp(a.1)); let total = self.total_time();
174
175 for (name, duration) in timer_vec {
176 let pct = if total > Duration::ZERO {
177 100.0 * duration.as_secs_f64() / total.as_secs_f64()
178 } else {
179 0.0
180 };
181
182 output.push_str(&format!(
183 " {:<30} {:>10.3}s ({:>5.1}%)\n",
184 name,
185 duration.as_secs_f64(),
186 pct
187 ));
188 }
189
190 output.push_str(&format!("\n Total Time: {:.3}s\n", total.as_secs_f64()));
191 output.push('\n');
192 }
193
194 if !self.counters.is_empty() {
195 output.push_str("COUNTERS:\n");
196 let mut counter_vec: Vec<_> = self.counters.iter().collect();
197 counter_vec.sort_by(|a, b| b.1.cmp(a.1)); for (name, count) in counter_vec {
200 output.push_str(&format!(" {:<30} {:>12}\n", name, count));
201 }
202 output.push('\n');
203 }
204
205 output.push_str("═══════════════════════════════════════════════════════════════\n");
206
207 output
208 }
209}
210
211pub struct AutoTimer {
213 name: String,
214 start: Instant,
215}
216
217impl AutoTimer {
218 #[must_use]
220 pub fn new(name: impl Into<String>) -> Self {
221 Self {
222 name: name.into(),
223 start: Instant::now(),
224 }
225 }
226
227 #[must_use]
229 pub fn elapsed(&self) -> Duration {
230 self.start.elapsed()
231 }
232}
233
234impl Drop for AutoTimer {
235 fn drop(&mut self) {
236 tracing::debug!("{}: {:.3}s", self.name, self.start.elapsed().as_secs_f64());
237 }
238}
239
240#[macro_export]
242macro_rules! time_scope {
243 ($name:expr) => {
244 let _timer = $crate::profiling::AutoTimer::new($name);
245 };
246}
247
248#[derive(Debug, Default, Clone)]
250pub struct PerformanceMetrics {
251 pub total_ops: u64,
253 pub total_time: Duration,
255 pub peak_ops_per_sec: f64,
257 pub avg_ops_per_sec: f64,
259}
260
261impl PerformanceMetrics {
262 #[must_use]
264 pub fn new() -> Self {
265 Self::default()
266 }
267
268 pub fn update(&mut self, ops: u64, time: Duration) {
270 self.total_ops += ops;
271 self.total_time += time;
272
273 let secs = time.as_secs_f64();
274 if secs > 0.0 {
275 let ops_per_sec = ops as f64 / secs;
276 if ops_per_sec > self.peak_ops_per_sec {
277 self.peak_ops_per_sec = ops_per_sec;
278 }
279 }
280
281 let total_secs = self.total_time.as_secs_f64();
282 if total_secs > 0.0 {
283 self.avg_ops_per_sec = self.total_ops as f64 / total_secs;
284 }
285 }
286
287 pub fn reset(&mut self) {
289 self.total_ops = 0;
290 self.total_time = Duration::ZERO;
291 self.peak_ops_per_sec = 0.0;
292 self.avg_ops_per_sec = 0.0;
293 }
294}
295
296#[cfg(test)]
297mod tests {
298 use super::*;
299 use std::thread;
300
301 #[test]
302 fn test_scoped_timer() {
303 let mut timer = ScopedTimer::new("test");
304 thread::sleep(Duration::from_millis(10));
305 timer.stop();
306
307 assert!(timer.elapsed() >= Duration::from_millis(10));
308 assert_eq!(timer.name(), "test");
309 }
310
311 #[test]
312 fn test_scoped_timer_restart() {
313 let mut timer = ScopedTimer::new("test");
314 thread::sleep(Duration::from_millis(5));
315 timer.stop();
316
317 let first_elapsed = timer.elapsed();
318
319 timer.restart();
320 thread::sleep(Duration::from_millis(5));
321 timer.stop();
322
323 assert!(timer.elapsed() >= first_elapsed);
324 }
325
326 #[test]
327 fn test_profiler_timers() {
328 let mut profiler = Profiler::new();
329
330 profiler.start_timer("operation1");
331 thread::sleep(Duration::from_millis(10));
332 profiler.stop_timer("operation1");
333
334 let elapsed = profiler.get_timer("operation1");
335 assert!(elapsed.is_some());
336 assert!(elapsed.expect("Timer must have elapsed duration") >= Duration::from_millis(10));
337 }
338
339 #[test]
340 fn test_profiler_counters() {
341 let mut profiler = Profiler::new();
342
343 profiler.increment("counter1");
344 profiler.increment("counter1");
345 profiler.increment_by("counter2", 5);
346
347 assert_eq!(profiler.get_counter("counter1"), Some(2));
348 assert_eq!(profiler.get_counter("counter2"), Some(5));
349 }
350
351 #[test]
352 fn test_profiler_clear() {
353 let mut profiler = Profiler::new();
354
355 profiler.increment("counter");
356 profiler.start_timer("timer");
357 profiler.stop_timer("timer");
358
359 profiler.clear();
360
361 assert_eq!(profiler.counters().len(), 0);
362 assert_eq!(profiler.timers().len(), 0);
363 }
364
365 #[test]
366 fn test_profiler_report() {
367 let mut profiler = Profiler::new();
368
369 profiler.start_timer("test_timer");
370 profiler.stop_timer("test_timer");
371 profiler.increment("test_counter");
372
373 let report = profiler.report();
374 assert!(report.contains("PROFILING REPORT"));
375 assert!(report.contains("TIMERS:"));
376 assert!(report.contains("COUNTERS:"));
377 }
378
379 #[test]
380 fn test_auto_timer() {
381 let timer = AutoTimer::new("test");
382 thread::sleep(Duration::from_millis(10));
383 assert!(timer.elapsed() >= Duration::from_millis(10));
384 }
385
386 #[test]
387 fn test_performance_metrics() {
388 let mut metrics = PerformanceMetrics::new();
389
390 metrics.update(1000, Duration::from_secs(1));
391 assert_eq!(metrics.total_ops, 1000);
392 assert!(metrics.avg_ops_per_sec > 0.0);
393
394 metrics.update(2000, Duration::from_secs(1));
395 assert_eq!(metrics.total_ops, 3000);
396 }
397
398 #[test]
399 fn test_performance_metrics_reset() {
400 let mut metrics = PerformanceMetrics::new();
401
402 metrics.update(1000, Duration::from_secs(1));
403 metrics.reset();
404
405 assert_eq!(metrics.total_ops, 0);
406 assert_eq!(metrics.total_time, Duration::ZERO);
407 }
408}