Skip to main content

rez_lsp_server/performance/
mod.rs

1//! Performance monitoring and optimization for the Rez LSP server.
2
3pub mod cache;
4pub mod metrics;
5pub mod profiler;
6
7pub use cache::{CacheManager, CacheStats};
8pub use metrics::{MetricsCollector, PerformanceMetrics};
9pub use profiler::{Profiler, ProfilerGuard};
10
11use std::time::{Duration, Instant};
12
13/// Performance configuration settings.
14#[derive(Debug, Clone)]
15pub struct PerformanceConfig {
16    /// Enable performance monitoring
17    pub enable_monitoring: bool,
18    /// Enable caching
19    pub enable_caching: bool,
20    /// Cache size limit in MB
21    pub cache_size_mb: usize,
22    /// Cache TTL in seconds
23    pub cache_ttl_seconds: u64,
24    /// Enable profiling
25    pub enable_profiling: bool,
26    /// Maximum number of metrics to keep in memory
27    pub max_metrics_history: usize,
28}
29
30impl Default for PerformanceConfig {
31    fn default() -> Self {
32        Self {
33            enable_monitoring: true,
34            enable_caching: true,
35            cache_size_mb: 100,
36            cache_ttl_seconds: 300,  // 5 minutes
37            enable_profiling: false, // Disabled by default for production
38            max_metrics_history: 1000,
39        }
40    }
41}
42
43/// A simple timer for measuring operation duration.
44pub struct Timer {
45    start: Instant,
46    name: String,
47}
48
49impl Timer {
50    /// Start a new timer with the given name.
51    pub fn new(name: impl Into<String>) -> Self {
52        Self {
53            start: Instant::now(),
54            name: name.into(),
55        }
56    }
57
58    /// Get the elapsed time since the timer was started.
59    pub fn elapsed(&self) -> Duration {
60        self.start.elapsed()
61    }
62
63    /// Get the elapsed time in milliseconds.
64    pub fn elapsed_ms(&self) -> u64 {
65        self.elapsed().as_millis() as u64
66    }
67
68    /// Get the timer name.
69    pub fn name(&self) -> &str {
70        &self.name
71    }
72}
73
74impl Drop for Timer {
75    fn drop(&mut self) {
76        let elapsed = self.elapsed_ms();
77        if elapsed > 100 {
78            // Log slow operations
79            tracing::warn!("Slow operation '{}' took {}ms", self.name, elapsed);
80        } else {
81            tracing::debug!("Operation '{}' took {}ms", self.name, elapsed);
82        }
83    }
84}
85
86/// Macro for timing operations.
87#[macro_export]
88macro_rules! time_operation {
89    ($name:expr, $block:block) => {{
90        let _timer = $crate::performance::Timer::new($name);
91        $block
92    }};
93}
94
95/// Macro for timing async operations.
96#[macro_export]
97macro_rules! time_async_operation {
98    ($name:expr, $block:block) => {{
99        let _timer = $crate::performance::Timer::new($name);
100        $block
101    }};
102}
103
104/// Performance optimization hints.
105#[derive(Debug, Clone)]
106pub enum OptimizationHint {
107    /// Cache this result
108    Cache { key: String, ttl_seconds: u64 },
109    /// Parallelize this operation
110    Parallelize { max_concurrency: usize },
111    /// Use incremental processing
112    Incremental { checkpoint_interval: usize },
113    /// Debounce this operation
114    Debounce { delay_ms: u64 },
115}
116
117/// Performance statistics for the entire system.
118#[derive(Debug, Clone)]
119pub struct SystemPerformanceStats {
120    /// Total number of operations performed
121    pub total_operations: u64,
122    /// Average operation time in milliseconds
123    pub avg_operation_time_ms: f64,
124    /// Number of cache hits
125    pub cache_hits: u64,
126    /// Number of cache misses
127    pub cache_misses: u64,
128    /// Cache hit ratio (0.0 to 1.0)
129    pub cache_hit_ratio: f64,
130    /// Memory usage in MB
131    pub memory_usage_mb: f64,
132    /// Number of active profiling sessions
133    pub active_profiles: usize,
134}
135
136impl SystemPerformanceStats {
137    /// Create empty performance stats.
138    pub fn new() -> Self {
139        Self {
140            total_operations: 0,
141            avg_operation_time_ms: 0.0,
142            cache_hits: 0,
143            cache_misses: 0,
144            cache_hit_ratio: 0.0,
145            memory_usage_mb: 0.0,
146            active_profiles: 0,
147        }
148    }
149
150    /// Update cache statistics.
151    pub fn update_cache_stats(&mut self, hits: u64, misses: u64) {
152        self.cache_hits = hits;
153        self.cache_misses = misses;
154        let total = hits + misses;
155        self.cache_hit_ratio = if total > 0 {
156            hits as f64 / total as f64
157        } else {
158            0.0
159        };
160    }
161
162    /// Update operation statistics.
163    pub fn update_operation_stats(&mut self, total_ops: u64, avg_time_ms: f64) {
164        self.total_operations = total_ops;
165        self.avg_operation_time_ms = avg_time_ms;
166    }
167
168    /// Update memory usage.
169    pub fn update_memory_usage(&mut self, usage_mb: f64) {
170        self.memory_usage_mb = usage_mb;
171    }
172
173    /// Update active profiles count.
174    pub fn update_active_profiles(&mut self, count: usize) {
175        self.active_profiles = count;
176    }
177}
178
179impl Default for SystemPerformanceStats {
180    fn default() -> Self {
181        Self::new()
182    }
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188    use std::thread;
189    use std::time::Duration;
190
191    #[test]
192    fn test_timer_creation() {
193        let timer = Timer::new("test_operation");
194        assert_eq!(timer.name(), "test_operation");
195        assert!(timer.elapsed().as_nanos() > 0);
196    }
197
198    #[test]
199    fn test_timer_elapsed() {
200        let timer = Timer::new("test");
201        thread::sleep(Duration::from_millis(10));
202        assert!(timer.elapsed_ms() >= 10);
203    }
204
205    #[test]
206    fn test_performance_config_default() {
207        let config = PerformanceConfig::default();
208        assert!(config.enable_monitoring);
209        assert!(config.enable_caching);
210        assert_eq!(config.cache_size_mb, 100);
211        assert_eq!(config.cache_ttl_seconds, 300);
212        assert!(!config.enable_profiling);
213        assert_eq!(config.max_metrics_history, 1000);
214    }
215
216    #[test]
217    fn test_system_performance_stats() {
218        let mut stats = SystemPerformanceStats::new();
219
220        stats.update_cache_stats(80, 20);
221        assert_eq!(stats.cache_hits, 80);
222        assert_eq!(stats.cache_misses, 20);
223        assert_eq!(stats.cache_hit_ratio, 0.8);
224
225        stats.update_operation_stats(1000, 25.5);
226        assert_eq!(stats.total_operations, 1000);
227        assert_eq!(stats.avg_operation_time_ms, 25.5);
228
229        stats.update_memory_usage(150.5);
230        assert_eq!(stats.memory_usage_mb, 150.5);
231
232        stats.update_active_profiles(3);
233        assert_eq!(stats.active_profiles, 3);
234    }
235
236    #[test]
237    fn test_cache_hit_ratio_calculation() {
238        let mut stats = SystemPerformanceStats::new();
239
240        // Test with no operations
241        stats.update_cache_stats(0, 0);
242        assert_eq!(stats.cache_hit_ratio, 0.0);
243
244        // Test with perfect hit ratio
245        stats.update_cache_stats(100, 0);
246        assert_eq!(stats.cache_hit_ratio, 1.0);
247
248        // Test with no hits
249        stats.update_cache_stats(0, 100);
250        assert_eq!(stats.cache_hit_ratio, 0.0);
251
252        // Test with mixed results
253        stats.update_cache_stats(75, 25);
254        assert_eq!(stats.cache_hit_ratio, 0.75);
255    }
256
257    #[tokio::test]
258    async fn test_time_operation_macro() {
259        let result = time_operation!("test_macro", {
260            thread::sleep(Duration::from_millis(1));
261            42
262        });
263        assert_eq!(result, 42);
264    }
265
266    #[tokio::test]
267    async fn test_time_async_operation_macro() {
268        let result = time_async_operation!("test_async_macro", {
269            tokio::time::sleep(Duration::from_millis(1)).await;
270            "hello"
271        });
272        assert_eq!(result, "hello");
273    }
274}