rust_yaml/
profiling.rs

1//! Performance profiling utilities and optimization features
2
3use std::collections::HashMap;
4use std::time::{Duration, Instant};
5
6/// Performance profiler for YAML operations
7#[derive(Debug, Clone)]
8pub struct YamlProfiler {
9    timings: HashMap<String, Vec<Duration>>,
10    memory_usage: HashMap<String, usize>,
11    enabled: bool,
12}
13
14impl YamlProfiler {
15    /// Create a new profiler instance
16    pub fn new() -> Self {
17        Self {
18            timings: HashMap::new(),
19            memory_usage: HashMap::new(),
20            enabled: std::env::var("RUST_YAML_PROFILE").is_ok(),
21        }
22    }
23
24    /// Check if profiling is enabled
25    pub const fn is_enabled(&self) -> bool {
26        self.enabled
27    }
28
29    /// Enable profiling
30    pub const fn enable(&mut self) {
31        self.enabled = true;
32    }
33
34    /// Disable profiling
35    pub const fn disable(&mut self) {
36        self.enabled = false;
37    }
38
39    /// Start timing an operation
40    pub fn time_operation<F, R>(&mut self, operation: &str, func: F) -> R
41    where
42        F: FnOnce() -> R,
43    {
44        if !self.enabled {
45            return func();
46        }
47
48        let start = Instant::now();
49        let result = func();
50        let duration = start.elapsed();
51
52        self.timings
53            .entry(operation.to_string())
54            .or_insert_with(Vec::new)
55            .push(duration);
56
57        result
58    }
59
60    /// Record memory usage for an operation
61    pub fn record_memory(&mut self, operation: &str, bytes: usize) {
62        if !self.enabled {
63            return;
64        }
65
66        self.memory_usage.insert(operation.to_string(), bytes);
67    }
68
69    /// Get average timing for an operation
70    pub fn average_time(&self, operation: &str) -> Option<Duration> {
71        let timings = self.timings.get(operation)?;
72        if timings.is_empty() {
73            return None;
74        }
75
76        let total: Duration = timings.iter().sum();
77        Some(total / timings.len() as u32)
78    }
79
80    /// Get total timing for an operation
81    pub fn total_time(&self, operation: &str) -> Option<Duration> {
82        let timings = self.timings.get(operation)?;
83        Some(timings.iter().sum())
84    }
85
86    /// Get memory usage for an operation
87    pub fn memory_usage(&self, operation: &str) -> Option<usize> {
88        self.memory_usage.get(operation).copied()
89    }
90
91    /// Get all recorded operations
92    pub fn operations(&self) -> Vec<String> {
93        let mut ops: Vec<String> = self.timings.keys().cloned().collect();
94        ops.sort();
95        ops
96    }
97
98    /// Clear all recorded data
99    pub fn clear(&mut self) {
100        self.timings.clear();
101        self.memory_usage.clear();
102    }
103
104    /// Generate a performance report
105    pub fn report(&self) -> String {
106        if !self.enabled {
107            return "Profiling disabled".to_string();
108        }
109
110        let mut report = String::new();
111        report.push_str("=== YAML Performance Report ===\n\n");
112
113        for operation in self.operations() {
114            report.push_str(&format!("Operation: {}\n", operation));
115
116            if let Some(avg_time) = self.average_time(&operation) {
117                report.push_str(&format!("  Average Time: {:?}\n", avg_time));
118            }
119
120            if let Some(total_time) = self.total_time(&operation) {
121                report.push_str(&format!("  Total Time: {:?}\n", total_time));
122            }
123
124            if let Some(memory) = self.memory_usage(&operation) {
125                report.push_str(&format!("  Memory Usage: {} bytes\n", memory));
126            }
127
128            if let Some(timings) = self.timings.get(&operation) {
129                report.push_str(&format!("  Sample Count: {}\n", timings.len()));
130            }
131
132            report.push('\n');
133        }
134
135        report
136    }
137}
138
139impl Default for YamlProfiler {
140    fn default() -> Self {
141        Self::new()
142    }
143}
144
145/// String interning pool for common YAML strings
146#[derive(Debug)]
147pub struct StringInterner {
148    strings: HashMap<String, &'static str>,
149    enabled: bool,
150}
151
152impl StringInterner {
153    /// Create a new string interner
154    pub fn new() -> Self {
155        Self {
156            strings: HashMap::new(),
157            enabled: true,
158        }
159    }
160
161    /// Intern a string, returning a reference to the interned version
162    pub const fn intern(&mut self, s: String) -> String {
163        if !self.enabled {
164            return s;
165        }
166
167        // For now, just return the original string
168        // In a real implementation, we'd use a more sophisticated interner
169        s
170    }
171
172    /// Check if a string is interned
173    pub fn contains(&self, s: &str) -> bool {
174        if !self.enabled {
175            return false;
176        }
177        self.strings.contains_key(s)
178    }
179
180    /// Get statistics about the interner
181    pub fn stats(&self) -> (usize, usize) {
182        let count = self.strings.len();
183        let memory = self.strings.keys().map(|s| s.len()).sum::<usize>();
184        (count, memory)
185    }
186}
187
188impl Default for StringInterner {
189    fn default() -> Self {
190        Self::new()
191    }
192}
193
194/// Memory pool for frequently allocated objects
195#[derive(Debug)]
196pub struct ObjectPool<T> {
197    objects: Vec<T>,
198    enabled: bool,
199}
200
201impl<T> ObjectPool<T> {
202    /// Create a new object pool
203    pub const fn new() -> Self {
204        Self {
205            objects: Vec::new(),
206            enabled: true,
207        }
208    }
209
210    /// Create a new object pool with initial capacity
211    pub fn with_capacity(capacity: usize) -> Self {
212        Self {
213            objects: Vec::with_capacity(capacity),
214            enabled: true,
215        }
216    }
217
218    /// Get an object from the pool, or create a new one
219    pub fn get<F>(&mut self, creator: F) -> T
220    where
221        F: FnOnce() -> T,
222    {
223        if self.enabled && !self.objects.is_empty() {
224            self.objects.pop().unwrap()
225        } else {
226            creator()
227        }
228    }
229
230    /// Return an object to the pool
231    pub fn put(&mut self, object: T) {
232        if self.enabled {
233            self.objects.push(object);
234        }
235    }
236
237    /// Get pool statistics
238    pub const fn stats(&self) -> (usize, usize) {
239        (self.objects.len(), self.objects.capacity())
240    }
241}
242
243impl<T> Default for ObjectPool<T> {
244    fn default() -> Self {
245        Self::new()
246    }
247}
248
249#[cfg(test)]
250mod tests {
251    use super::*;
252
253    #[test]
254    fn test_profiler_basic_functionality() {
255        let mut profiler = YamlProfiler::new();
256        profiler.enable();
257
258        let result = profiler.time_operation("test_op", || {
259            std::thread::sleep(Duration::from_millis(1));
260            42
261        });
262
263        assert_eq!(result, 42);
264        assert!(profiler.average_time("test_op").is_some());
265        assert!(profiler.total_time("test_op").is_some());
266    }
267
268    #[test]
269    fn test_profiler_memory_tracking() {
270        let mut profiler = YamlProfiler::new();
271        profiler.enable();
272
273        profiler.record_memory("allocation", 1024);
274        assert_eq!(profiler.memory_usage("allocation"), Some(1024));
275    }
276
277    #[test]
278    fn test_profiler_disabled() {
279        let mut profiler = YamlProfiler::new();
280        profiler.disable();
281
282        profiler.time_operation("test_op", || 42);
283        assert!(profiler.average_time("test_op").is_none());
284    }
285
286    #[test]
287    fn test_string_interner() {
288        let mut interner = StringInterner::new();
289        let s1 = interner.intern("test".to_string());
290        let s2 = interner.intern("test".to_string());
291
292        // Basic functionality test
293        assert_eq!(s1, s2);
294    }
295
296    #[test]
297    fn test_object_pool() {
298        let mut pool = ObjectPool::new();
299
300        // Put an object in the pool
301        pool.put(String::from("test"));
302
303        // Get it back
304        let retrieved = pool.get(|| String::from("new"));
305        assert_eq!(retrieved, "test");
306
307        // Pool should now be empty, so next get creates new object
308        let new_obj = pool.get(|| String::from("fresh"));
309        assert_eq!(new_obj, "fresh");
310    }
311}