memscope_rs/lockfree/
sampling.rs

1//! Intelligent sampling configuration for lock-free tracking
2//!
3//! This module defines sampling strategies optimized for high-concurrency
4//! scenarios where capturing every allocation would create performance bottlenecks.
5
6/// Sampling configuration for intelligent allocation tracking
7///
8/// Uses dual-dimension sampling (size + frequency) to balance performance
9/// with data completeness. Large allocations and high-frequency patterns
10/// receive priority sampling.
11#[derive(Debug, Clone)]
12pub struct SamplingConfig {
13    /// Sample rate for large allocations - usually 100% to catch memory leaks
14    pub large_allocation_rate: f64,
15    /// Sample rate for medium allocations - balanced approach
16    pub medium_allocation_rate: f64,
17    /// Sample rate for small allocations - low to reduce overhead
18    pub small_allocation_rate: f64,
19    /// Size threshold for large allocations (bytes)
20    pub large_threshold: usize,
21    /// Size threshold for medium allocations (bytes)
22    pub medium_threshold: usize,
23    /// Frequency threshold for sampling boost
24    pub frequency_threshold: u64,
25}
26
27impl Default for SamplingConfig {
28    /// Default configuration optimized for typical applications
29    ///
30    /// Captures all large allocations, moderate sampling of medium allocations,
31    /// and light sampling of small allocations to maintain performance.
32    fn default() -> Self {
33        Self {
34            large_allocation_rate: 1.0,  // 100% - catch all potential leaks
35            medium_allocation_rate: 0.1, // 10% - balanced coverage
36            small_allocation_rate: 0.01, // 1% - minimal overhead
37            large_threshold: 10 * 1024,  // 10KB threshold
38            medium_threshold: 1024,      // 1KB threshold
39            frequency_threshold: 10,     // Boost after 10 occurrences
40        }
41    }
42}
43
44impl SamplingConfig {
45    /// Creates high-precision configuration for debugging scenarios
46    ///
47    /// Higher sampling rates for more complete data capture at the cost
48    /// of increased performance overhead.
49    pub fn high_precision() -> Self {
50        Self {
51            large_allocation_rate: 1.0,
52            medium_allocation_rate: 0.5, // 50% sampling
53            small_allocation_rate: 0.1,  // 10% sampling
54            large_threshold: 4 * 1024,   // 4KB threshold
55            medium_threshold: 512,       // 512B threshold
56            frequency_threshold: 5,      // Earlier boost
57        }
58    }
59
60    /// Creates performance-optimized configuration for production
61    ///
62    /// Minimal sampling to reduce overhead while still capturing
63    /// the most critical allocation patterns.
64    pub fn performance_optimized() -> Self {
65        Self {
66            large_allocation_rate: 1.0,
67            medium_allocation_rate: 0.05, // 5% sampling
68            small_allocation_rate: 0.001, // 0.1% sampling
69            large_threshold: 50 * 1024,   // 50KB threshold
70            medium_threshold: 5 * 1024,   // 5KB threshold
71            frequency_threshold: 50,      // Higher boost threshold
72        }
73    }
74
75    /// Creates configuration for memory leak detection
76    ///
77    /// Optimized to catch large allocations and allocation patterns
78    /// that might indicate memory leaks.
79    pub fn leak_detection() -> Self {
80        Self {
81            large_allocation_rate: 1.0,
82            medium_allocation_rate: 0.8, // High sampling for leaks
83            small_allocation_rate: 0.01,
84            large_threshold: 1024,  // 1KB threshold (lower)
85            medium_threshold: 256,  // 256B threshold
86            frequency_threshold: 3, // Quick boost for patterns
87        }
88    }
89
90    /// Creates configuration for demonstrations and testing
91    ///
92    /// Maximum sampling rates to ensure all data is visible in demos and tests.
93    /// Not suitable for production due to very high performance overhead.
94    pub fn demo() -> Self {
95        Self {
96            large_allocation_rate: 1.0,  // 100% - all large allocations
97            medium_allocation_rate: 1.0, // 100% - all medium allocations
98            small_allocation_rate: 1.0,  // 100% - all small allocations (for demo)
99            large_threshold: 8 * 1024,   // 8KB threshold (catch more as "large")
100            medium_threshold: 256,       // 256B threshold (catch more as "medium")
101            frequency_threshold: 1,      // Immediate frequency boost
102        }
103    }
104
105    /// Validates configuration parameters
106    ///
107    /// Ensures all rates are between 0.0 and 1.0 and thresholds are reasonable.
108    pub fn validate(&self) -> Result<(), String> {
109        if !(0.0..=1.0).contains(&self.large_allocation_rate) {
110            return Err("Large allocation rate must be between 0.0 and 1.0".to_string());
111        }
112        if !(0.0..=1.0).contains(&self.medium_allocation_rate) {
113            return Err("Medium allocation rate must be between 0.0 and 1.0".to_string());
114        }
115        if !(0.0..=1.0).contains(&self.small_allocation_rate) {
116            return Err("Small allocation rate must be between 0.0 and 1.0".to_string());
117        }
118        if self.large_threshold <= self.medium_threshold {
119            return Err("Large threshold must be greater than medium threshold".to_string());
120        }
121        if self.medium_threshold == 0 {
122            return Err("Medium threshold must be greater than 0".to_string());
123        }
124        Ok(())
125    }
126
127    /// Calculates expected sampling rate for given allocation size
128    ///
129    /// Returns the base sampling rate before frequency adjustments.
130    pub fn base_sampling_rate(&self, size: usize) -> f64 {
131        if size >= self.large_threshold {
132            self.large_allocation_rate
133        } else if size >= self.medium_threshold {
134            self.medium_allocation_rate
135        } else {
136            self.small_allocation_rate
137        }
138    }
139
140    /// Calculates frequency multiplier for sampling boost
141    ///
142    /// High-frequency allocations get increased sampling rates to identify
143    /// performance hotspots.
144    pub fn frequency_multiplier(&self, frequency: u64) -> f64 {
145        if frequency > self.frequency_threshold {
146            // Logarithmic boost to prevent excessive sampling
147            (frequency as f64 / self.frequency_threshold as f64).min(10.0)
148        } else {
149            1.0
150        }
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157
158    #[test]
159    fn test_default_config_validation() {
160        let config = SamplingConfig::default();
161        assert!(config.validate().is_ok());
162    }
163
164    #[test]
165    fn test_preset_configs_validation() {
166        assert!(SamplingConfig::high_precision().validate().is_ok());
167        assert!(SamplingConfig::performance_optimized().validate().is_ok());
168        assert!(SamplingConfig::leak_detection().validate().is_ok());
169    }
170
171    #[test]
172    fn test_invalid_config_validation() {
173        let mut config = SamplingConfig {
174            large_allocation_rate: 1.5,
175            ..Default::default()
176        };
177        assert!(config.validate().is_err());
178
179        // Test invalid thresholds
180        config.large_allocation_rate = 1.0;
181        config.large_threshold = 500;
182        config.medium_threshold = 1000;
183        assert!(config.validate().is_err());
184    }
185
186    #[test]
187    fn test_sampling_rate_calculation() {
188        let config = SamplingConfig::default();
189
190        // Test large allocation
191        assert_eq!(config.base_sampling_rate(20 * 1024), 1.0);
192
193        // Test medium allocation
194        assert_eq!(config.base_sampling_rate(5 * 1024), 0.1);
195
196        // Test small allocation
197        assert_eq!(config.base_sampling_rate(512), 0.01);
198    }
199
200    #[test]
201    fn test_frequency_multiplier() {
202        let config = SamplingConfig::default();
203
204        // Test below threshold
205        assert_eq!(config.frequency_multiplier(5), 1.0);
206
207        // Test above threshold
208        assert!(config.frequency_multiplier(20) > 1.0);
209
210        // Test capping at 10x
211        assert_eq!(config.frequency_multiplier(1000), 10.0);
212    }
213}