memscope_rs/core/
smart_optimization.rs

1//! Smart optimization based on actual performance analysis
2//!
3//! This module provides targeted optimizations based on real performance data,
4//! avoiding the over-engineering that led to performance degradation.
5
6use crate::core::safe_operations::SafeLock;
7use parking_lot::Mutex;
8use std::borrow::ToOwned;
9use std::sync::atomic::{AtomicU64, Ordering};
10use std::time::{Duration, Instant};
11
12/// Smart mutex that chooses the best implementation based on usage patterns
13pub enum SmartMutex<T> {
14    /// Use standard mutex for low-contention scenarios
15    Standard(std::sync::Mutex<T>),
16    /// Use parking_lot for high-contention scenarios
17    ParkingLot(parking_lot::Mutex<T>),
18}
19
20impl<T> SmartMutex<T> {
21    /// Create a new smart mutex (starts with standard, can upgrade)
22    pub fn new(data: T) -> Self {
23        SmartMutex::Standard(std::sync::Mutex::new(data))
24    }
25
26    /// Lock with automatic contention detection
27    pub fn lock(&self) -> SmartMutexGuard<'_, T> {
28        match self {
29            SmartMutex::Standard(mutex) => {
30                let start = Instant::now();
31                let guard = mutex.safe_lock().expect("Failed to lock standard mutex");
32                let wait_time = start.elapsed();
33
34                // If we waited too long, this might benefit from parking_lot
35                if wait_time > Duration::from_micros(100) {
36                    // Log this for potential upgrade
37                    record_contention("std_mutex", wait_time);
38                }
39
40                SmartMutexGuard::Standard(guard)
41            }
42            SmartMutex::ParkingLot(mutex) => SmartMutexGuard::ParkingLot(mutex.lock()),
43        }
44    }
45
46    /// Try to lock without blocking
47    pub fn try_lock(&self) -> Option<SmartMutexGuard<'_, T>> {
48        match self {
49            SmartMutex::Standard(mutex) => mutex.try_lock().ok().map(SmartMutexGuard::Standard),
50            SmartMutex::ParkingLot(mutex) => mutex.try_lock().map(SmartMutexGuard::ParkingLot),
51        }
52    }
53}
54
55pub enum SmartMutexGuard<'a, T> {
56    Standard(std::sync::MutexGuard<'a, T>),
57    ParkingLot(parking_lot::MutexGuard<'a, T>),
58}
59
60impl<'a, T> std::ops::Deref for SmartMutexGuard<'a, T> {
61    type Target = T;
62
63    fn deref(&self) -> &Self::Target {
64        match self {
65            SmartMutexGuard::Standard(guard) => guard,
66            SmartMutexGuard::ParkingLot(guard) => guard,
67        }
68    }
69}
70
71impl<'a, T> std::ops::DerefMut for SmartMutexGuard<'a, T> {
72    fn deref_mut(&mut self) -> &mut Self::Target {
73        match self {
74            SmartMutexGuard::Standard(guard) => guard,
75            SmartMutexGuard::ParkingLot(guard) => guard,
76        }
77    }
78}
79
80/// Simple contention tracking (much lighter than our previous approach)
81static CONTENTION_COUNTER: AtomicU64 = AtomicU64::new(0);
82static TOTAL_WAIT_TIME_NS: AtomicU64 = AtomicU64::new(0);
83
84fn record_contention(mutex_type: &str, wait_time: Duration) {
85    CONTENTION_COUNTER.fetch_add(1, Ordering::Relaxed);
86    TOTAL_WAIT_TIME_NS.fetch_add(wait_time.as_nanos() as u64, Ordering::Relaxed);
87
88    // Simple logging without heavy overhead
89    if CONTENTION_COUNTER.load(Ordering::Relaxed) % 1000 == 0 {
90        let avg_wait =
91            TOTAL_WAIT_TIME_NS.load(Ordering::Relaxed) / CONTENTION_COUNTER.load(Ordering::Relaxed);
92        tracing::debug!(
93            "Mutex contention detected: {} avg wait: {}ns",
94            mutex_type,
95            avg_wait
96        );
97    }
98}
99
100/// Lightweight unwrap replacement that doesn't panic in release builds
101pub trait SafeUnwrap<T> {
102    /// Unwrap with fallback value in release builds
103    fn safe_unwrap(self, fallback: T) -> T;
104
105    /// Unwrap with error context but no panic in release
106    fn safe_unwrap_or_log(self, context: &str, fallback: T) -> T;
107}
108
109impl<T> SafeUnwrap<T> for Option<T> {
110    fn safe_unwrap(self, fallback: T) -> T {
111        match self {
112            Some(value) => value,
113            None => {
114                #[cfg(debug_assertions)]
115                {
116                    tracing::warn!("safe_unwrap called on None, using fallback");
117                    fallback
118                }
119
120                #[cfg(not(debug_assertions))]
121                {
122                    tracing::warn!("safe_unwrap called on None, using fallback");
123                    fallback
124                }
125            }
126        }
127    }
128
129    fn safe_unwrap_or_log(self, context: &str, fallback: T) -> T {
130        match self {
131            Some(value) => value,
132            None => {
133                tracing::warn!("safe_unwrap_or_log failed in context: {}", context);
134                fallback
135            }
136        }
137    }
138}
139
140impl<T, E: std::fmt::Debug> SafeUnwrap<T> for Result<T, E> {
141    fn safe_unwrap(self, _fallback: T) -> T {
142        match self {
143            Ok(value) => value,
144            Err(e) => {
145                #[cfg(debug_assertions)]
146                panic!("Called safe_unwrap on Err: {e:?}");
147
148                #[cfg(not(debug_assertions))]
149                {
150                    tracing::warn!("safe_unwrap called on Err: {e:?}, using fallback");
151                    _fallback
152                }
153            }
154        }
155    }
156
157    fn safe_unwrap_or_log(self, context: &str, fallback: T) -> T {
158        match self {
159            Ok(value) => value,
160            Err(e) => {
161                tracing::warn!(
162                    "safe_unwrap_or_log failed in context: {context} - error: {:?}",
163                    e
164                );
165                fallback
166            }
167        }
168    }
169}
170
171/// Efficient clone reduction for hot paths
172pub trait SmartClone<T: ?Sized + ToOwned> {
173    /// Clone only if necessary, otherwise return reference
174    fn smart_clone(&self) -> std::borrow::Cow<'_, T>;
175}
176
177impl SmartClone<str> for String {
178    fn smart_clone(&self) -> std::borrow::Cow<'_, str> {
179        std::borrow::Cow::Borrowed(self)
180    }
181}
182
183impl<T: Clone> SmartClone<[T]> for Vec<T> {
184    fn smart_clone(&self) -> std::borrow::Cow<'_, [T]> {
185        std::borrow::Cow::Borrowed(self)
186    }
187}
188
189/// Macro for smart unwrapping (removed - use safe_operations instead)
190/// Performance-aware statistics that use the right tool for the job
191pub struct SmartStats {
192    // Use atomics for high-frequency simple counters
193    pub allocation_count: AtomicU64,
194    pub deallocation_count: AtomicU64,
195
196    // Use mutex for complex data that's accessed less frequently
197    pub detailed_stats: Mutex<DetailedStats>,
198}
199
200#[derive(Default)]
201pub struct DetailedStats {
202    pub allocation_sizes: Vec<usize>,
203    pub allocation_times: Vec<Duration>,
204    pub peak_memory: usize,
205}
206
207impl SmartStats {
208    pub fn new() -> Self {
209        Self {
210            allocation_count: AtomicU64::new(0),
211            deallocation_count: AtomicU64::new(0),
212            detailed_stats: Mutex::new(DetailedStats::default()),
213        }
214    }
215
216    /// Fast path for simple counting
217    pub fn record_allocation(&self) {
218        self.allocation_count.fetch_add(1, Ordering::Relaxed);
219    }
220
221    /// Slower path for detailed tracking (only when needed)
222    pub fn record_detailed_allocation(&self, size: usize, time: Duration) {
223        self.allocation_count.fetch_add(1, Ordering::Relaxed);
224
225        // Only lock when we need detailed stats
226        if let Some(mut stats) = self.detailed_stats.try_lock() {
227            stats.allocation_sizes.push(size);
228            stats.allocation_times.push(time);
229        }
230        // If lock fails, we just skip detailed tracking - no big deal
231    }
232
233    pub fn get_simple_stats(&self) -> (u64, u64) {
234        (
235            self.allocation_count.load(Ordering::Relaxed),
236            self.deallocation_count.load(Ordering::Relaxed),
237        )
238    }
239}
240
241impl Default for SmartStats {
242    fn default() -> Self {
243        Self::new()
244    }
245}
246
247#[cfg(test)]
248mod tests {
249    use super::*;
250    use std::sync::Arc;
251    use std::thread;
252
253    #[test]
254    fn test_smart_mutex_basic() {
255        let mutex = SmartMutex::new(42);
256        {
257            let guard = mutex.lock();
258            assert_eq!(*guard, 42);
259        }
260
261        {
262            let mut guard = mutex.lock();
263            *guard = 100;
264        }
265
266        {
267            let guard = mutex.lock();
268            assert_eq!(*guard, 100);
269        }
270    }
271
272    #[test]
273    fn test_smart_mutex_try_lock() {
274        let mutex = SmartMutex::new("test_value");
275
276        // Test successful try_lock
277        if let Some(guard) = mutex.try_lock() {
278            assert_eq!(*guard, "test_value");
279        } else {
280            panic!("try_lock should succeed when mutex is not held");
281        }
282
283        // Test try_lock when already locked (in a controlled way)
284        let mutex2 = Arc::new(SmartMutex::new(0));
285        let mutex2_clone = mutex2.clone();
286
287        let _guard = mutex2.lock();
288        // Now try_lock should fail
289        assert!(mutex2_clone.try_lock().is_none());
290    }
291
292    #[test]
293    fn test_smart_mutex_parking_lot() {
294        // Test with ParkingLot variant
295        let mutex = SmartMutex::ParkingLot(parking_lot::Mutex::new("parking_lot_test"));
296
297        {
298            let guard = mutex.lock();
299            assert_eq!(*guard, "parking_lot_test");
300        }
301
302        // Test try_lock with ParkingLot
303        {
304            if let Some(guard) = mutex.try_lock() {
305                assert_eq!(*guard, "parking_lot_test");
306            } else {
307                panic!("ParkingLot try_lock should succeed");
308            };
309        }
310    }
311
312    #[test]
313    fn test_smart_mutex_guard_deref() {
314        let mutex = SmartMutex::new(vec![1, 2, 3]);
315        let guard = mutex.lock();
316
317        // Test Deref
318        assert_eq!(guard.len(), 3);
319        assert_eq!(guard[0], 1);
320    }
321
322    #[test]
323    fn test_smart_mutex_guard_deref_mut() {
324        let mutex = SmartMutex::new(vec![1, 2, 3]);
325        let mut guard = mutex.lock();
326
327        // Test DerefMut
328        guard.push(4);
329        assert_eq!(guard.len(), 4);
330        guard[0] = 10;
331        assert_eq!(guard[0], 10);
332    }
333
334    #[test]
335    fn test_contention_recording() {
336        // Reset counters (though they're global, we can at least test they increment)
337        let initial_count = CONTENTION_COUNTER.load(Ordering::Relaxed);
338        let initial_wait = TOTAL_WAIT_TIME_NS.load(Ordering::Relaxed);
339
340        // Record some contention
341        record_contention("test_mutex", Duration::from_micros(50));
342
343        assert_eq!(
344            CONTENTION_COUNTER.load(Ordering::Relaxed),
345            initial_count + 1
346        );
347        assert!(TOTAL_WAIT_TIME_NS.load(Ordering::Relaxed) > initial_wait);
348
349        // Test multiple contentions
350        for _ in 0..10 {
351            record_contention("test_mutex", Duration::from_micros(100));
352        }
353
354        assert_eq!(
355            CONTENTION_COUNTER.load(Ordering::Relaxed),
356            initial_count + 11
357        );
358    }
359
360    #[test]
361    fn test_safe_unwrap_option() {
362        // Test Some variant
363        let some_value = Some(42);
364        assert_eq!(some_value.safe_unwrap(0), 42);
365
366        // Test None variant
367        let none_value: Option<i32> = None;
368        assert_eq!(none_value.safe_unwrap(99), 99);
369
370        // Test with string
371        let some_string = Some("hello".to_string());
372        assert_eq!(some_string.safe_unwrap("default".to_string()), "hello");
373
374        let none_string: Option<String> = None;
375        assert_eq!(none_string.safe_unwrap("fallback".to_string()), "fallback");
376    }
377
378    #[test]
379    fn test_safe_unwrap_or_log_option() {
380        // Test Some variant
381        let some_value = Some(100);
382        assert_eq!(some_value.safe_unwrap_or_log("test_context", 0), 100);
383
384        // Test None variant
385        let none_value: Option<i32> = None;
386        assert_eq!(none_value.safe_unwrap_or_log("none_context", 50), 50);
387
388        // Test with different types
389        let some_vec = Some(vec![1, 2, 3]);
390        assert_eq!(
391            some_vec.safe_unwrap_or_log("vec_context", vec![]),
392            vec![1, 2, 3]
393        );
394    }
395
396    #[test]
397    fn test_safe_unwrap_result() {
398        // Test Ok variant
399        let ok_result: Result<i32, String> = Ok(42);
400        assert_eq!(ok_result.safe_unwrap(0), 42);
401
402        // Test Err variant (should use fallback in release mode)
403        #[cfg(not(debug_assertions))]
404        {
405            let err_result: Result<i32, String> = Err("error message".to_string());
406            assert_eq!(err_result.safe_unwrap(99), 99);
407        }
408
409        // In debug mode, this would panic, so we skip it
410        #[cfg(debug_assertions)]
411        {
412            // Just test Ok case in debug mode
413            let ok_result2: Result<String, &str> = Ok("success".to_string());
414            assert_eq!(ok_result2.safe_unwrap("fallback".to_string()), "success");
415        }
416    }
417
418    #[test]
419    fn test_safe_unwrap_or_log_result() {
420        // Test Ok variant
421        let ok_result: Result<i32, String> = Ok(100);
422        assert_eq!(ok_result.safe_unwrap_or_log("ok_context", 0), 100);
423
424        // Test Err variant
425        let err_result: Result<i32, String> = Err("test error".to_string());
426        assert_eq!(err_result.safe_unwrap_or_log("err_context", 50), 50);
427
428        // Test with complex error type
429        let err_result2: Result<Vec<u8>, std::io::Error> = Err(std::io::Error::new(
430            std::io::ErrorKind::NotFound,
431            "not found",
432        ));
433        assert_eq!(
434            err_result2.safe_unwrap_or_log("io_err", vec![1, 2]),
435            vec![1, 2]
436        );
437    }
438
439    #[test]
440    fn test_smart_clone_string() {
441        let string = String::from("hello world");
442        let cloned = string.smart_clone();
443
444        // Should return a borrowed Cow
445        assert!(matches!(cloned, std::borrow::Cow::Borrowed(_)));
446        assert_eq!(&*cloned, "hello world");
447    }
448
449    #[test]
450    fn test_smart_clone_vec() {
451        let vec = vec![1, 2, 3, 4, 5];
452        let cloned = vec.smart_clone();
453
454        // Should return a borrowed Cow
455        assert!(matches!(cloned, std::borrow::Cow::Borrowed(_)));
456        assert_eq!(&*cloned, &[1, 2, 3, 4, 5]);
457
458        // Test with different types
459        let string_vec = vec!["a".to_string(), "b".to_string()];
460        let cloned2 = string_vec.smart_clone();
461        assert!(matches!(cloned2, std::borrow::Cow::Borrowed(_)));
462        assert_eq!(cloned2.len(), 2);
463    }
464
465    #[test]
466    fn test_smart_stats_basic() {
467        let stats = SmartStats::new();
468
469        // Test initial state
470        let (allocs, deallocs) = stats.get_simple_stats();
471        assert_eq!(allocs, 0);
472        assert_eq!(deallocs, 0);
473
474        // Test allocation recording
475        for _ in 0..100 {
476            stats.record_allocation();
477        }
478
479        let (allocs, deallocs) = stats.get_simple_stats();
480        assert_eq!(allocs, 100);
481        assert_eq!(deallocs, 0);
482
483        // Test deallocation counting
484        for _ in 0..50 {
485            stats.deallocation_count.fetch_add(1, Ordering::Relaxed);
486        }
487
488        let (allocs, deallocs) = stats.get_simple_stats();
489        assert_eq!(allocs, 100);
490        assert_eq!(deallocs, 50);
491    }
492
493    #[test]
494    fn test_smart_stats_detailed() {
495        let stats = SmartStats::new();
496
497        // Record detailed allocations
498        stats.record_detailed_allocation(1024, Duration::from_micros(10));
499        stats.record_detailed_allocation(2048, Duration::from_micros(20));
500        stats.record_detailed_allocation(512, Duration::from_micros(5));
501
502        // Check counters
503        let (allocs, _) = stats.get_simple_stats();
504        assert_eq!(allocs, 3);
505
506        // Check detailed stats
507        let detailed = stats.detailed_stats.lock();
508        assert_eq!(detailed.allocation_sizes.len(), 3);
509        assert_eq!(detailed.allocation_sizes[0], 1024);
510        assert_eq!(detailed.allocation_sizes[1], 2048);
511        assert_eq!(detailed.allocation_sizes[2], 512);
512
513        assert_eq!(detailed.allocation_times.len(), 3);
514        assert_eq!(detailed.allocation_times[0], Duration::from_micros(10));
515    }
516
517    #[test]
518    fn test_smart_stats_peak_memory() {
519        let stats = SmartStats::new();
520
521        // Update peak memory
522        {
523            let mut detailed = stats.detailed_stats.lock();
524            detailed.peak_memory = 1000;
525            detailed.allocation_sizes.push(500);
526        }
527
528        // Verify
529        let detailed = stats.detailed_stats.lock();
530        assert_eq!(detailed.peak_memory, 1000);
531        assert_eq!(detailed.allocation_sizes[0], 500);
532    }
533
534    #[test]
535    fn test_smart_stats_concurrent() {
536        let stats = Arc::new(SmartStats::new());
537        let mut handles = vec![];
538
539        // Test concurrent allocation recording
540        for _ in 0..10 {
541            let stats_clone = stats.clone();
542            let handle = thread::spawn(move || {
543                for _ in 0..100 {
544                    stats_clone.record_allocation();
545                }
546            });
547            handles.push(handle);
548        }
549
550        for handle in handles {
551            handle.join().unwrap();
552        }
553
554        let (allocs, _) = stats.get_simple_stats();
555        assert_eq!(allocs, 1000);
556    }
557
558    #[test]
559    fn test_smart_stats_default() {
560        let stats = SmartStats::default();
561        let (allocs, deallocs) = stats.get_simple_stats();
562        assert_eq!(allocs, 0);
563        assert_eq!(deallocs, 0);
564
565        // Verify detailed stats are also default
566        let detailed = stats.detailed_stats.lock();
567        assert!(detailed.allocation_sizes.is_empty());
568        assert!(detailed.allocation_times.is_empty());
569        assert_eq!(detailed.peak_memory, 0);
570    }
571
572    #[test]
573    fn test_detailed_stats_default() {
574        let detailed = DetailedStats::default();
575        assert!(detailed.allocation_sizes.is_empty());
576        assert!(detailed.allocation_times.is_empty());
577        assert_eq!(detailed.peak_memory, 0);
578    }
579}