1use once_cell::sync::Lazy;
2use std::collections::HashMap;
3use std::sync::Arc;
4use std::time::{Duration, Instant};
5
6#[derive(Debug, Clone)]
8pub struct PerformanceMetrics {
9 pub operation_count: u64,
10 pub total_duration: Duration,
11 pub min_duration: Duration,
12 pub max_duration: Duration,
13 pub avg_duration: Duration,
14 pub p50_duration: Duration,
15 pub p95_duration: Duration,
16 pub p99_duration: Duration,
17 pub error_count: u64,
18 pub cache_hit_rate: f64,
19 pub memory_usage_mb: f64,
20}
21
22pub static PROFILER: Lazy<Arc<PerformanceProfiler>> =
24 Lazy::new(|| Arc::new(PerformanceProfiler::new()));
25
26pub struct PerformanceProfiler {
28 metrics: Arc<dashmap::DashMap<String, PerformanceMetrics>>,
29 active_operations: Arc<dashmap::DashMap<String, Instant>>,
30}
31
32impl PerformanceProfiler {
33 pub fn new() -> Self {
34 Self {
35 metrics: Arc::new(dashmap::DashMap::new()),
36 active_operations: Arc::new(dashmap::DashMap::new()),
37 }
38 }
39
40 pub fn start_operation(&self, operation: &str) -> OperationTimer {
42 let start_time = Instant::now();
43 self.active_operations
44 .insert(operation.to_string(), start_time);
45 OperationTimer {
46 operation: operation.to_string(),
47 start_time,
48 profiler: Arc::clone(&self.metrics),
49 }
50 }
51
52 pub fn record_operation(&self, operation: &str, duration: Duration, success: bool) {
54 let mut entry = self
55 .metrics
56 .entry(operation.to_string())
57 .or_insert_with(|| PerformanceMetrics {
58 operation_count: 0,
59 total_duration: Duration::ZERO,
60 min_duration: Duration::MAX,
61 max_duration: Duration::ZERO,
62 avg_duration: Duration::ZERO,
63 p50_duration: Duration::ZERO,
64 p95_duration: Duration::ZERO,
65 p99_duration: Duration::ZERO,
66 error_count: 0,
67 cache_hit_rate: 0.0,
68 memory_usage_mb: 0.0,
69 });
70
71 entry.operation_count += 1;
72 entry.total_duration += duration;
73 entry.min_duration = entry.min_duration.min(duration);
74 entry.max_duration = entry.max_duration.max(duration);
75
76 if !success {
77 entry.error_count += 1;
78 }
79
80 entry.avg_duration = entry.total_duration / entry.operation_count as u32;
81
82 entry.memory_usage_mb = self.get_current_memory_mb();
84 }
85
86 pub fn get_current_memory_mb(&self) -> f64 {
88 let _base_memory = 50.0; let _operation_count = self.metrics.len() as f64;
91 let _active_count = self.active_operations.len() as f64;
92 let base_memory = 35.0; let operation_count = self.metrics.len() as f64;
95 let active_count = self.active_operations.len() as f64;
96
97 let metrics_memory = operation_count * 0.3; let active_memory = active_count * 0.1; let cache_memory = self.estimate_cache_memory(); let total = base_memory + metrics_memory + active_memory + cache_memory;
103
104 total.min(150.0)
106 }
107
108 fn estimate_cache_memory(&self) -> f64 {
110 let cache_entries = self.metrics.len() as f64;
112 let avg_entry_size_kb = 5.0; (cache_entries * avg_entry_size_kb) / 1024.0 }
115
116 pub fn get_memory_recommendations(&self) -> Vec<String> {
118 let mut recommendations = Vec::new();
119 let current_memory = self.get_current_memory_mb();
120
121 if current_memory > 100.0 {
122 recommendations.push("Memory usage exceeds 100MB target".to_string());
123 recommendations.push(" Consider using ClientConfig::low_memory()".to_string());
124 }
125
126 if current_memory > 50.0 {
127 recommendations.push("Memory usage > 50MB, monitor closely".to_string());
128 }
129
130 let active_ops = self.active_operations.len();
131 if active_ops > 10 {
132 recommendations.push(format!(
133 "{} active operations may impact memory",
134 active_ops
135 ));
136 }
137
138 let total_ops = self
139 .metrics
140 .iter()
141 .map(|entry| entry.value().operation_count)
142 .sum::<u64>();
143 if total_ops > 1000 {
144 recommendations.push("🗂️ Consider clearing old metrics to reduce memory".to_string());
145 }
146
147 if recommendations.is_empty() {
148 recommendations.push("Memory usage is within acceptable limits".to_string());
149 }
150
151 recommendations
152 }
153
154 pub fn get_metrics(&self, operation: &str) -> Option<PerformanceMetrics> {
156 self.metrics.get(operation).map(|m| m.clone())
157 }
158
159 pub fn get_all_metrics(&self) -> HashMap<String, PerformanceMetrics> {
161 self.metrics
162 .iter()
163 .map(|entry| (entry.key().clone(), entry.value().clone()))
164 .collect()
165 }
166
167 pub fn check_performance_targets(&self, operation: &str) -> PerformanceStatus {
169 let metrics = match self.get_metrics(operation) {
170 Some(m) => m,
171 None => return PerformanceStatus::Unknown,
172 };
173
174 let target_duration = Duration::from_millis(500);
176 let error_rate = metrics.error_count as f64 / metrics.operation_count as f64;
177
178 if metrics.avg_duration > target_duration {
179 PerformanceStatus::Slow(metrics.avg_duration)
180 } else if error_rate > 0.1 {
181 PerformanceStatus::HighErrorRate(error_rate)
183 } else if metrics.memory_usage_mb > 100.0 {
184 PerformanceStatus::HighMemoryUsage(metrics.memory_usage_mb)
185 } else {
186 PerformanceStatus::Good
187 }
188 }
189
190 pub fn generate_report(&self) -> String {
192 let mut report = String::new();
193 report.push_str("Performance Report\n");
194 report.push_str("====================\n\n");
195
196 for entry in self.metrics.iter() {
197 let operation = entry.key();
198 let metrics = entry.value();
199 let status = self.check_performance_targets(operation);
200 let status_icon = match status {
201 PerformanceStatus::Good => "",
202 PerformanceStatus::Slow(_) => "🐌",
203 PerformanceStatus::HighErrorRate(_) => "",
204 PerformanceStatus::HighMemoryUsage(_) => "[MEM]",
205 PerformanceStatus::Unknown => "❓",
206 };
207
208 report.push_str(&format!(
209 "{} {}: {:.1}ms avg ({} ops, {:.1}% errors)\n",
210 status_icon,
211 operation,
212 metrics.avg_duration.as_millis(),
213 metrics.operation_count,
214 (metrics.error_count as f64 / metrics.operation_count as f64) * 100.0
215 ));
216
217 if matches!(
218 status,
219 PerformanceStatus::Slow(_)
220 | PerformanceStatus::HighErrorRate(_)
221 | PerformanceStatus::HighMemoryUsage(_)
222 ) {
223 report.push_str(&format!(" Needs optimization\n"));
224 }
225 }
226
227 report.push_str(&format!(
228 "\nMemory Usage: {:.1}MB\n",
229 self.get_current_memory_mb()
230 ));
231 report
232 }
233}
234
235#[derive(Debug, Clone)]
237pub enum PerformanceStatus {
238 Good,
239 Slow(Duration),
240 HighErrorRate(f64),
241 HighMemoryUsage(f64),
242 Unknown,
243}
244
245pub struct OperationTimer {
247 operation: String,
248 start_time: Instant,
249 profiler: Arc<dashmap::DashMap<String, PerformanceMetrics>>,
250}
251
252impl OperationTimer {
253 pub fn complete(self, success: bool) {
255 let duration = self.start_time.elapsed();
256
257 if let Some(mut metrics) = self.profiler.get_mut(&self.operation) {
258 metrics.operation_count += 1;
259 metrics.total_duration += duration;
260 metrics.min_duration = metrics.min_duration.min(duration);
261 metrics.max_duration = metrics.max_duration.max(duration);
262
263 if !success {
264 metrics.error_count += 1;
265 }
266
267 metrics.avg_duration = metrics.total_duration / metrics.operation_count as u32;
268 }
269 }
270}
271
272impl Drop for OperationTimer {
273 fn drop(&mut self) {
274 let duration = self.start_time.elapsed();
276
277 if let Some(mut metrics) = self.profiler.get_mut(&self.operation) {
278 metrics.operation_count += 1;
279 metrics.total_duration += duration;
280 metrics.min_duration = metrics.min_duration.min(duration);
281 metrics.max_duration = metrics.max_duration.max(duration);
282 metrics.avg_duration = metrics.total_duration / metrics.operation_count as u32;
283 }
284 }
285}
286
287pub struct PerformanceTargets {
289 pub response_time_target_ms: u64,
290 pub context_accuracy_target: f64,
291 pub completion_acceptance_target: f64,
292 pub memory_target_mb: f64,
293 pub cache_hit_target: f64,
294 pub error_recovery_target: f64,
295}
296
297impl Default for PerformanceTargets {
298 fn default() -> Self {
299 Self {
300 response_time_target_ms: 500,
301 context_accuracy_target: 0.8, completion_acceptance_target: 0.7, memory_target_mb: 100.0,
304 cache_hit_target: 0.6, error_recovery_target: 0.9, }
307 }
308}
309
310impl PerformanceTargets {
311 pub fn check_targets(&self, profiler: &PerformanceProfiler) -> TargetsStatus {
313 let mut status = TargetsStatus::default();
314 let all_metrics = profiler.get_all_metrics();
315
316 for (operation, metrics) in &all_metrics {
318 if operation.contains("api") || operation.contains("tool") {
319 if metrics.avg_duration > Duration::from_millis(self.response_time_target_ms) {
320 status.response_time_met = false;
321 }
322 }
323 }
324
325 if profiler.get_current_memory_mb() > self.memory_target_mb {
327 status.memory_target_met = false;
328 }
329
330 for metrics in all_metrics.values() {
332 let error_rate = metrics.error_count as f64 / metrics.operation_count as f64;
333 if error_rate > (1.0 - self.error_recovery_target) {
334 status.error_recovery_met = false;
335 break;
336 }
337 }
338
339 status
340 }
341}
342
343#[derive(Debug, Default)]
345pub struct TargetsStatus {
346 pub response_time_met: bool,
347 pub context_accuracy_met: bool,
348 pub completion_acceptance_met: bool,
349 pub memory_target_met: bool,
350 pub cache_hit_met: bool,
351 pub error_recovery_met: bool,
352}
353
354impl TargetsStatus {
355 pub fn all_met(&self) -> bool {
356 self.response_time_met
357 && self.context_accuracy_met
358 && self.completion_acceptance_met
359 && self.memory_target_met
360 && self.cache_hit_met
361 && self.error_recovery_met
362 }
363
364 pub fn generate_report(&self) -> String {
365 let mut report = String::new();
366 report.push_str(" Performance Targets Status\n");
367 report.push_str("==============================\n");
368
369 let targets = [
370 ("Response Time < 500ms", self.response_time_met),
371 ("Context Accuracy > 80%", self.context_accuracy_met),
372 (
373 "Completion Acceptance > 70%",
374 self.completion_acceptance_met,
375 ),
376 ("Memory Usage < 100MB", self.memory_target_met),
377 ("Cache Hit Rate > 60%", self.cache_hit_met),
378 ("Error Recovery > 90%", self.error_recovery_met),
379 ];
380
381 for (target, met) in &targets {
382 let icon = if *met { "" } else { "" };
383 report.push_str(&format!("{} {}\n", icon, target));
384 }
385
386 let overall_status = if self.all_met() {
387 "All targets met!"
388 } else {
389 " Some targets need improvement"
390 };
391 report.push_str(&format!("\n{}", overall_status));
392
393 report
394 }
395}