anno_eval/eval/
profiling.rs1use std::collections::HashMap;
6use std::time::{Duration, Instant};
7
8#[derive(Debug, Clone)]
10pub struct Profiler {
11 timings: HashMap<String, Vec<Duration>>,
12 current_timers: HashMap<String, Instant>,
13}
14
15impl Profiler {
16 pub fn new() -> Self {
18 Self {
19 timings: HashMap::new(),
20 current_timers: HashMap::new(),
21 }
22 }
23
24 pub fn start(&mut self, operation: &str) {
26 self.current_timers
27 .insert(operation.to_string(), Instant::now());
28 }
29
30 pub fn stop(&mut self, operation: &str) {
32 if let Some(start) = self.current_timers.remove(operation) {
33 let duration = start.elapsed();
34 self.timings
35 .entry(operation.to_string())
36 .or_default()
37 .push(duration);
38 }
39 }
40
41 pub fn time<F, R>(&mut self, operation: &str, f: F) -> R
43 where
44 F: FnOnce() -> R,
45 {
46 self.start(operation);
47 let result = f();
48 self.stop(operation);
49 result
50 }
51
52 pub fn summary(&self) -> HashMap<String, TimingStats> {
54 self.timings
55 .iter()
56 .map(|(name, durations)| {
57 let total: Duration = durations.iter().sum();
58 let count = durations.len();
59 let avg = if count > 0 {
60 total / count as u32
61 } else {
62 Duration::ZERO
63 };
64 let min = durations.iter().min().copied().unwrap_or(Duration::ZERO);
65 let max = durations.iter().max().copied().unwrap_or(Duration::ZERO);
66
67 (
68 name.clone(),
69 TimingStats {
70 total,
71 count,
72 avg,
73 min,
74 max,
75 },
76 )
77 })
78 .collect()
79 }
80
81 pub fn print_summary(&self) {
83 let summary = self.summary();
84 eprintln!("\n=== Profiling Summary ===");
85 eprintln!(
86 "{:<30} {:>10} {:>10} {:>10} {:>10} {:>10}",
87 "Operation", "Count", "Total (ms)", "Avg (ms)", "Min (ms)", "Max (ms)"
88 );
89 eprintln!("{}", "-".repeat(90));
90
91 let mut sorted: Vec<_> = summary.iter().collect();
92 sorted.sort_by(|a, b| b.1.total.cmp(&a.1.total)); for (name, stats) in sorted {
95 eprintln!(
96 "{:<30} {:>10} {:>10.2} {:>10.2} {:>10.2} {:>10.2}",
97 name,
98 stats.count,
99 stats.total.as_secs_f64() * 1000.0,
100 stats.avg.as_secs_f64() * 1000.0,
101 stats.min.as_secs_f64() * 1000.0,
102 stats.max.as_secs_f64() * 1000.0,
103 );
104 }
105 eprintln!();
106 }
107}
108
109impl Default for Profiler {
110 fn default() -> Self {
111 Self::new()
112 }
113}
114
115#[derive(Debug, Clone)]
117pub struct TimingStats {
118 pub total: Duration,
120 pub count: usize,
122 pub avg: Duration,
124 pub min: Duration,
126 pub max: Duration,
128}
129
130#[cfg(feature = "eval-profiling")]
132thread_local! {
133 static PROFILER: std::cell::RefCell<Profiler> = std::cell::RefCell::new(Profiler::new());
134}
135
136#[cfg(feature = "eval-profiling")]
138pub fn start(operation: &str) {
139 PROFILER.with(|p| p.borrow_mut().start(operation));
140}
141
142#[cfg(feature = "eval-profiling")]
144pub fn stop(operation: &str) {
145 PROFILER.with(|p| p.borrow_mut().stop(operation));
146}
147
148#[cfg(feature = "eval-profiling")]
150pub fn time<F, R>(operation: &str, f: F) -> R
151where
152 F: FnOnce() -> R,
153{
154 PROFILER.with(|p| p.borrow_mut().time(operation, f))
155}
156
157#[cfg(feature = "eval-profiling")]
159pub fn print_summary() {
160 PROFILER.with(|p| p.borrow().print_summary());
161}
162
163#[cfg(not(feature = "eval-profiling"))]
165pub fn start(_operation: &str) {}
166
167#[cfg(not(feature = "eval-profiling"))]
168pub fn stop(_operation: &str) {}
169
170#[cfg(not(feature = "eval-profiling"))]
171pub fn time<F, R>(_operation: &str, f: F) -> R
172where
173 F: FnOnce() -> R,
174{
175 f()
176}
177
178#[cfg(not(feature = "eval-profiling"))]
179pub fn print_summary() {}