ricecoder_lsp/
performance.rs1use std::collections::HashMap;
7use std::sync::{Arc, RwLock};
8use std::time::{Duration, Instant};
9use tracing::{info, warn};
10
11#[derive(Debug, Clone)]
13pub struct OperationMetrics {
14 pub name: String,
16 pub count: u64,
18 pub total_time_ms: f64,
20 pub min_time_ms: f64,
22 pub max_time_ms: f64,
24}
25
26impl OperationMetrics {
27 pub fn avg_time_ms(&self) -> f64 {
29 if self.count == 0 {
30 0.0
31 } else {
32 self.total_time_ms / self.count as f64
33 }
34 }
35}
36
37pub struct PerformanceTracker {
39 metrics: Arc<RwLock<HashMap<String, OperationMetrics>>>,
41 targets: Arc<RwLock<HashMap<String, f64>>>,
43}
44
45impl PerformanceTracker {
46 pub fn new() -> Self {
48 Self {
49 metrics: Arc::new(RwLock::new(HashMap::new())),
50 targets: Arc::new(RwLock::new(HashMap::new())),
51 }
52 }
53
54 pub fn set_target(&self, operation: String, max_time_ms: f64) {
56 let mut targets = self.targets.write().unwrap();
57 targets.insert(operation, max_time_ms);
58 }
59
60 pub fn record(&self, operation: String, duration: Duration) {
62 let time_ms = duration.as_secs_f64() * 1000.0;
63
64 let mut metrics = self.metrics.write().unwrap();
65 let entry = metrics
66 .entry(operation.clone())
67 .or_insert_with(|| OperationMetrics {
68 name: operation.clone(),
69 count: 0,
70 total_time_ms: 0.0,
71 min_time_ms: f64::MAX,
72 max_time_ms: 0.0,
73 });
74
75 entry.count += 1;
76 entry.total_time_ms += time_ms;
77 entry.min_time_ms = entry.min_time_ms.min(time_ms);
78 entry.max_time_ms = entry.max_time_ms.max(time_ms);
79
80 let targets = self.targets.read().unwrap();
82 if let Some(&target) = targets.get(&operation) {
83 if time_ms > target {
84 warn!(
85 "Performance target exceeded for {}: {:.2}ms > {:.2}ms",
86 operation, time_ms, target
87 );
88 }
89 }
90 }
91
92 pub fn get_metrics(&self, operation: &str) -> Option<OperationMetrics> {
94 let metrics = self.metrics.read().unwrap();
95 metrics.get(operation).cloned()
96 }
97
98 pub fn all_metrics(&self) -> Vec<OperationMetrics> {
100 let metrics = self.metrics.read().unwrap();
101 metrics.values().cloned().collect()
102 }
103
104 pub fn clear(&self) {
106 let mut metrics = self.metrics.write().unwrap();
107 metrics.clear();
108 }
109
110 pub fn print_report(&self) {
112 let metrics = self.metrics.read().unwrap();
113
114 if metrics.is_empty() {
115 info!("No performance metrics recorded");
116 return;
117 }
118
119 info!("=== Performance Report ===");
120 for (_, metric) in metrics.iter() {
121 info!(
122 "{}: count={}, avg={:.2}ms, min={:.2}ms, max={:.2}ms",
123 metric.name,
124 metric.count,
125 metric.avg_time_ms(),
126 metric.min_time_ms,
127 metric.max_time_ms
128 );
129 }
130 }
131}
132
133impl Default for PerformanceTracker {
134 fn default() -> Self {
135 Self::new()
136 }
137}
138
139pub struct Timer {
141 start: Instant,
142 operation: String,
143 tracker: Arc<PerformanceTracker>,
144}
145
146impl Timer {
147 pub fn new(operation: String, tracker: Arc<PerformanceTracker>) -> Self {
149 Self {
150 start: Instant::now(),
151 operation,
152 tracker,
153 }
154 }
155
156 pub fn stop(self) {
158 let duration = self.start.elapsed();
159 let operation = self.operation.clone();
160 self.tracker.record(operation, duration);
161 }
162}
163
164impl Drop for Timer {
165 fn drop(&mut self) {
166 let duration = self.start.elapsed();
167 let operation = self.operation.clone();
168 self.tracker.record(operation, duration);
169 }
170}
171
172pub struct PerformanceAnalyzer {
174 tracker: Arc<PerformanceTracker>,
175}
176
177impl PerformanceAnalyzer {
178 pub fn new(tracker: Arc<PerformanceTracker>) -> Self {
180 Self { tracker }
181 }
182
183 pub fn identify_slow_operations(&self) -> Vec<(String, f64, f64)> {
185 let metrics = self.tracker.metrics.read().unwrap();
186 let targets = self.tracker.targets.read().unwrap();
187
188 let mut slow_ops = Vec::new();
189
190 for (name, metric) in metrics.iter() {
191 if let Some(&target) = targets.get(name) {
192 let avg = metric.avg_time_ms();
193 if avg > target {
194 slow_ops.push((name.clone(), avg, target));
195 }
196 }
197 }
198
199 slow_ops.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
200 slow_ops
201 }
202
203 pub fn slowest_operations(&self, limit: usize) -> Vec<OperationMetrics> {
205 let mut metrics = self.tracker.all_metrics();
206 metrics.sort_by(|a, b| b.avg_time_ms().partial_cmp(&a.avg_time_ms()).unwrap());
207 metrics.into_iter().take(limit).collect()
208 }
209
210 pub fn most_time_spent(&self, limit: usize) -> Vec<OperationMetrics> {
212 let mut metrics = self.tracker.all_metrics();
213 metrics.sort_by(|a, b| b.total_time_ms.partial_cmp(&a.total_time_ms).unwrap());
214 metrics.into_iter().take(limit).collect()
215 }
216}
217
218#[derive(Debug, Clone)]
220pub struct OptimizationRecommendation {
221 pub operation: String,
223 pub current_time_ms: f64,
225 pub target_time_ms: f64,
227 pub recommendation: String,
229}
230
231impl OptimizationRecommendation {
232 pub fn improvement_needed(&self) -> f64 {
234 if self.target_time_ms == 0.0 {
235 0.0
236 } else {
237 ((self.current_time_ms - self.target_time_ms) / self.target_time_ms) * 100.0
238 }
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245 use std::thread;
246
247 #[test]
248 fn test_performance_tracker_record() {
249 let tracker = PerformanceTracker::new();
250 let duration = Duration::from_millis(100);
251
252 tracker.record("test_op".to_string(), duration);
253
254 let metrics = tracker.get_metrics("test_op").unwrap();
255 assert_eq!(metrics.count, 1);
256 assert!(metrics.total_time_ms >= 100.0);
257 }
258
259 #[test]
260 fn test_performance_tracker_multiple_records() {
261 let tracker = PerformanceTracker::new();
262
263 tracker.record("test_op".to_string(), Duration::from_millis(100));
264 tracker.record("test_op".to_string(), Duration::from_millis(200));
265 tracker.record("test_op".to_string(), Duration::from_millis(150));
266
267 let metrics = tracker.get_metrics("test_op").unwrap();
268 assert_eq!(metrics.count, 3);
269 assert!(metrics.avg_time_ms() >= 150.0);
270 assert!(metrics.min_time_ms >= 100.0);
271 assert!(metrics.max_time_ms >= 200.0);
272 }
273
274 #[test]
275 fn test_timer_auto_record() {
276 let tracker = Arc::new(PerformanceTracker::new());
277
278 {
279 let _timer = Timer::new("test_op".to_string(), tracker.clone());
280 thread::sleep(Duration::from_millis(50));
281 }
282
283 let metrics = tracker.get_metrics("test_op").unwrap();
284 assert_eq!(metrics.count, 1);
285 assert!(metrics.total_time_ms >= 50.0);
286 }
287
288 #[test]
289 fn test_performance_target() {
290 let tracker = PerformanceTracker::new();
291 tracker.set_target("test_op".to_string(), 100.0);
292
293 tracker.record("test_op".to_string(), Duration::from_millis(50));
294
295 let metrics = tracker.get_metrics("test_op").unwrap();
296 assert_eq!(metrics.count, 1);
297 }
298
299 #[test]
300 fn test_performance_analyzer_slowest() {
301 let tracker = Arc::new(PerformanceTracker::new());
302
303 tracker.record("op1".to_string(), Duration::from_millis(100));
304 tracker.record("op2".to_string(), Duration::from_millis(200));
305 tracker.record("op3".to_string(), Duration::from_millis(150));
306
307 let analyzer = PerformanceAnalyzer::new(tracker);
308 let slowest = analyzer.slowest_operations(2);
309
310 assert_eq!(slowest.len(), 2);
311 assert_eq!(slowest[0].name, "op2");
312 assert_eq!(slowest[1].name, "op3");
313 }
314
315 #[test]
316 fn test_optimization_recommendation() {
317 let rec = OptimizationRecommendation {
318 operation: "test_op".to_string(),
319 current_time_ms: 200.0,
320 target_time_ms: 100.0,
321 recommendation: "Optimize parsing".to_string(),
322 };
323
324 assert_eq!(rec.improvement_needed(), 100.0);
325 }
326}