litellm_rs/utils/perf/
optimizer.rs

1//! Performance optimization utilities
2//!
3//! This module provides utilities to optimize runtime performance,
4//! reduce allocations, and improve overall system efficiency.
5
6#![allow(dead_code)] // Tool module - functions may be used in the future
7
8use parking_lot::RwLock;
9use std::collections::HashMap;
10use std::sync::Arc;
11use std::time::{Duration, Instant};
12use tracing::{debug, info, warn};
13
14/// Performance metrics collector
15#[derive(Debug, Clone)]
16pub struct PerformanceMetrics {
17    /// Function call counts
18    pub call_counts: Arc<RwLock<HashMap<String, u64>>>,
19    /// Function execution times
20    pub execution_times: Arc<RwLock<HashMap<String, Vec<Duration>>>>,
21    /// Memory allocation tracking
22    pub allocation_counts: Arc<RwLock<HashMap<String, u64>>>,
23    /// Cache hit rates
24    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    /// Create a new performance metrics collector
52    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    /// Record a function call
62    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    /// Record function execution time
68    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    /// Record memory allocation
77    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    /// Record cache hit
83    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    /// Record cache miss
91    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    /// Get performance report
99    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    /// Reset all metrics
114    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/// Performance report
123#[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    /// Print performance summary
133    pub fn print_summary(&self) {
134        info!("=== Performance Report ===");
135
136        // Top called functions
137        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        // Slowest functions
146        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        // Memory allocations
162        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        // Cache performance
171        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    /// Get optimization recommendations
184    pub fn get_recommendations(&self) -> Vec<String> {
185        let mut recommendations = Vec::new();
186
187        // Check for frequently called functions
188        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        // Check for slow functions
198        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        // Check cache hit rates
211        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        // Check for excessive allocations
222        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
235/// Performance timer for measuring execution time
236pub struct PerformanceTimer {
237    start: Instant,
238    function_name: String,
239    metrics: Arc<PerformanceMetrics>,
240}
241
242impl PerformanceTimer {
243    /// Start timing a function
244    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 for easy performance timing
270#[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
281/// Global performance metrics instance
282use once_cell::sync::Lazy;
283pub static GLOBAL_METRICS: Lazy<Arc<PerformanceMetrics>> =
284    Lazy::new(|| Arc::new(PerformanceMetrics::new()));
285
286/// Convenience function to get global metrics
287pub fn global_metrics() -> Arc<PerformanceMetrics> {
288    GLOBAL_METRICS.clone()
289}
290
291/// Start performance monitoring background task
292pub 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)); // 5 minutes
297
298        loop {
299            interval.tick().await;
300
301            let report = metrics.get_report();
302            debug!("Performance monitoring tick");
303
304            // Print recommendations if any
305            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}