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}