1#![allow(dead_code)] use parking_lot::RwLock;
9use std::collections::HashMap;
10use std::sync::Arc;
11use std::time::{Duration, Instant};
12use tracing::{debug, info, warn};
13
14#[derive(Debug, Clone)]
16pub struct PerformanceMetrics {
17 pub call_counts: Arc<RwLock<HashMap<String, u64>>>,
19 pub execution_times: Arc<RwLock<HashMap<String, Vec<Duration>>>>,
21 pub allocation_counts: Arc<RwLock<HashMap<String, u64>>>,
23 pub cache_stats: Arc<RwLock<HashMap<String, CacheStats>>>,
25}
26
27#[derive(Debug, Clone, Default)]
28pub struct CacheStats {
29 pub hits: u64,
30 pub misses: u64,
31 pub total_requests: u64,
32}
33
34impl CacheStats {
35 pub fn hit_rate(&self) -> f64 {
36 if self.total_requests == 0 {
37 0.0
38 } else {
39 self.hits as f64 / self.total_requests as f64
40 }
41 }
42}
43
44impl Default for PerformanceMetrics {
45 fn default() -> Self {
46 Self::new()
47 }
48}
49
50impl PerformanceMetrics {
51 pub fn new() -> Self {
53 Self {
54 call_counts: Arc::new(RwLock::new(HashMap::new())),
55 execution_times: Arc::new(RwLock::new(HashMap::new())),
56 allocation_counts: Arc::new(RwLock::new(HashMap::new())),
57 cache_stats: Arc::new(RwLock::new(HashMap::new())),
58 }
59 }
60
61 pub fn record_call(&self, function_name: &str) {
63 let mut counts = self.call_counts.write();
64 *counts.entry(function_name.to_string()).or_insert(0) += 1;
65 }
66
67 pub fn record_execution_time(&self, function_name: &str, duration: Duration) {
69 let mut times = self.execution_times.write();
70 times
71 .entry(function_name.to_string())
72 .or_default()
73 .push(duration);
74 }
75
76 pub fn record_allocation(&self, context: &str) {
78 let mut counts = self.allocation_counts.write();
79 *counts.entry(context.to_string()).or_insert(0) += 1;
80 }
81
82 pub fn record_cache_hit(&self, cache_name: &str) {
84 let mut stats = self.cache_stats.write();
85 let cache_stat = stats.entry(cache_name.to_string()).or_default();
86 cache_stat.hits += 1;
87 cache_stat.total_requests += 1;
88 }
89
90 pub fn record_cache_miss(&self, cache_name: &str) {
92 let mut stats = self.cache_stats.write();
93 let cache_stat = stats.entry(cache_name.to_string()).or_default();
94 cache_stat.misses += 1;
95 cache_stat.total_requests += 1;
96 }
97
98 pub fn get_report(&self) -> PerformanceReport {
100 let call_counts = self.call_counts.read().clone();
101 let execution_times = self.execution_times.read().clone();
102 let allocation_counts = self.allocation_counts.read().clone();
103 let cache_stats = self.cache_stats.read().clone();
104
105 PerformanceReport {
106 call_counts,
107 execution_times,
108 allocation_counts,
109 cache_stats,
110 }
111 }
112
113 pub fn reset(&self) {
115 self.call_counts.write().clear();
116 self.execution_times.write().clear();
117 self.allocation_counts.write().clear();
118 self.cache_stats.write().clear();
119 }
120}
121
122#[derive(Debug)]
124pub struct PerformanceReport {
125 pub call_counts: HashMap<String, u64>,
126 pub execution_times: HashMap<String, Vec<Duration>>,
127 pub allocation_counts: HashMap<String, u64>,
128 pub cache_stats: HashMap<String, CacheStats>,
129}
130
131impl PerformanceReport {
132 pub fn print_summary(&self) {
134 info!("=== Performance Report ===");
135
136 let mut sorted_calls: Vec<_> = self.call_counts.iter().collect();
138 sorted_calls.sort_by(|a, b| b.1.cmp(a.1));
139
140 info!("Top 10 Most Called Functions:");
141 for (func, count) in sorted_calls.iter().take(10) {
142 info!(" {}: {} calls", func, count);
143 }
144
145 info!("Function Execution Times:");
147 for (func, times) in &self.execution_times {
148 if !times.is_empty() {
149 let avg = times.iter().sum::<Duration>() / times.len() as u32;
150 let max = times.iter().max().unwrap();
151 info!(
152 " {}: avg={:?}, max={:?}, calls={}",
153 func,
154 avg,
155 max,
156 times.len()
157 );
158 }
159 }
160
161 let mut sorted_allocs: Vec<_> = self.allocation_counts.iter().collect();
163 sorted_allocs.sort_by(|a, b| b.1.cmp(a.1));
164
165 info!("Top Memory Allocation Sources:");
166 for (context, count) in sorted_allocs.iter().take(10) {
167 info!(" {}: {} allocations", context, count);
168 }
169
170 info!("Cache Performance:");
172 for (cache, stats) in &self.cache_stats {
173 info!(
174 " {}: hit_rate={:.2}%, hits={}, misses={}",
175 cache,
176 stats.hit_rate() * 100.0,
177 stats.hits,
178 stats.misses
179 );
180 }
181 }
182
183 pub fn get_recommendations(&self) -> Vec<String> {
185 let mut recommendations = Vec::new();
186
187 for (func, count) in &self.call_counts {
189 if *count > 10000 {
190 recommendations.push(format!(
191 "Consider optimizing '{}' - called {} times",
192 func, count
193 ));
194 }
195 }
196
197 for (func, times) in &self.execution_times {
199 if !times.is_empty() {
200 let avg = times.iter().sum::<Duration>() / times.len() as u32;
201 if avg > Duration::from_millis(100) {
202 recommendations.push(format!(
203 "Function '{}' is slow - average execution time: {:?}",
204 func, avg
205 ));
206 }
207 }
208 }
209
210 for (cache, stats) in &self.cache_stats {
212 if stats.hit_rate() < 0.8 && stats.total_requests > 100 {
213 recommendations.push(format!(
214 "Cache '{}' has low hit rate: {:.2}% - consider tuning cache size or TTL",
215 cache,
216 stats.hit_rate() * 100.0
217 ));
218 }
219 }
220
221 for (context, count) in &self.allocation_counts {
223 if *count > 50000 {
224 recommendations.push(format!(
225 "High allocation count in '{}': {} - consider object pooling",
226 context, count
227 ));
228 }
229 }
230
231 recommendations
232 }
233}
234
235pub struct PerformanceTimer {
237 start: Instant,
238 function_name: String,
239 metrics: Arc<PerformanceMetrics>,
240}
241
242impl PerformanceTimer {
243 pub fn start(function_name: &str, metrics: Arc<PerformanceMetrics>) -> Self {
245 metrics.record_call(function_name);
246 Self {
247 start: Instant::now(),
248 function_name: function_name.to_string(),
249 metrics,
250 }
251 }
252}
253
254impl Drop for PerformanceTimer {
255 fn drop(&mut self) {
256 let duration = self.start.elapsed();
257 self.metrics
258 .record_execution_time(&self.function_name, duration);
259
260 if duration > Duration::from_millis(100) {
261 warn!(
262 "Slow function detected: {} took {:?}",
263 self.function_name, duration
264 );
265 }
266 }
267}
268
269#[macro_export]
271macro_rules! time_function {
272 ($metrics:expr, $func_name:expr, $body:block) => {{
273 let _timer = $crate::utils::performance_optimizer::PerformanceTimer::start(
274 $func_name,
275 $metrics.clone(),
276 );
277 $body
278 }};
279}
280
281use once_cell::sync::Lazy;
283pub static GLOBAL_METRICS: Lazy<Arc<PerformanceMetrics>> =
284 Lazy::new(|| Arc::new(PerformanceMetrics::new()));
285
286pub fn global_metrics() -> Arc<PerformanceMetrics> {
288 GLOBAL_METRICS.clone()
289}
290
291pub async fn start_performance_monitoring() {
293 let metrics = global_metrics();
294
295 tokio::spawn(async move {
296 let mut interval = tokio::time::interval(Duration::from_secs(300)); loop {
299 interval.tick().await;
300
301 let report = metrics.get_report();
302 debug!("Performance monitoring tick");
303
304 let recommendations = report.get_recommendations();
306 if !recommendations.is_empty() {
307 warn!("Performance recommendations:");
308 for rec in recommendations {
309 warn!(" - {}", rec);
310 }
311 }
312 }
313 });
314}
315
316#[cfg(test)]
317mod tests {
318 use super::*;
319 use std::time::Duration;
320
321 #[test]
322 fn test_performance_metrics() {
323 let metrics = PerformanceMetrics::new();
324
325 metrics.record_call("test_function");
326 metrics.record_execution_time("test_function", Duration::from_millis(50));
327 metrics.record_allocation("test_context");
328 metrics.record_cache_hit("test_cache");
329 metrics.record_cache_miss("test_cache");
330
331 let report = metrics.get_report();
332 assert_eq!(report.call_counts.get("test_function"), Some(&1));
333 assert_eq!(report.allocation_counts.get("test_context"), Some(&1));
334
335 let cache_stats = report.cache_stats.get("test_cache").unwrap();
336 assert_eq!(cache_stats.hits, 1);
337 assert_eq!(cache_stats.misses, 1);
338 assert_eq!(cache_stats.hit_rate(), 0.5);
339 }
340}