memscope_rs/unified/strategies/
mod.rs

1// Strategy Implementations for Unified Memory Tracking
2// Provides concrete implementations for different runtime environments
3// Maintains zero-lock architecture and high performance standards
4
5//! # Memory Tracking Strategies
6//!
7//! This module provides concrete implementations of memory tracking strategies
8//! optimized for different runtime environments.
9//!
10//! ## Available Strategies
11//!
12//! - [`SingleThreadStrategy`] - Optimized for single-threaded applications
13//! - [`ThreadLocalStrategy`] - Thread-local storage for multi-threaded apps  
14//! - [`AsyncStrategy`] - Task-local tracking for async applications
15//! - [`HybridStrategy`] - Combined approach for complex environments
16//!
17//! ## Design Principles
18//!
19//! All strategies implement the unified `MemoryTracker` trait and follow:
20//! - **Zero-lock architecture**: No mutex or rwlock usage
21//! - **Minimal overhead**: <3% performance impact target
22//! - **Data compatibility**: Consistent export formats
23//! - **Graceful fallback**: Error recovery without data loss
24
25pub mod async_strategy;
26pub mod hybrid_strategy;
27pub mod single_thread;
28pub mod thread_local;
29
30// Re-export strategy implementations
31pub use async_strategy::AsyncStrategy;
32pub use hybrid_strategy::HybridStrategy;
33pub use single_thread::SingleThreadStrategy;
34pub use thread_local::ThreadLocalStrategy;
35
36use crate::unified::tracking_dispatcher::{MemoryTracker, TrackerConfig, TrackerError};
37use std::collections::HashMap;
38use tracing::{debug, info};
39
40/// Strategy factory for creating appropriate tracker implementations
41/// Provides centralized strategy creation with proper configuration
42pub struct StrategyFactory {
43    /// Configuration templates for each strategy type
44    strategy_configs: HashMap<String, TrackerConfig>,
45    /// Performance monitoring for strategy selection
46    performance_history: PerformanceHistory,
47}
48
49/// Performance history tracking for strategy optimization
50/// Maintains historical performance data to guide strategy selection
51#[derive(Debug, Clone)]
52pub struct PerformanceHistory {
53    /// Strategy performance records
54    pub strategy_performance: HashMap<String, StrategyPerformance>,
55    /// Total tracking sessions recorded
56    pub total_sessions: u64,
57    /// Average overhead across all strategies
58    pub average_overhead_percent: f64,
59}
60
61/// Performance metrics for individual strategy
62/// Tracks key performance indicators for strategy evaluation
63#[derive(Debug, Clone)]
64pub struct StrategyPerformance {
65    /// Total sessions using this strategy
66    pub session_count: u64,
67    /// Average memory overhead percentage
68    pub avg_overhead_percent: f64,
69    /// Average initialization time (microseconds)
70    pub avg_init_time_us: f64,
71    /// Success rate (0.0 to 1.0)
72    pub success_rate: f64,
73    /// User satisfaction score (0.0 to 1.0)
74    pub satisfaction_score: f64,
75}
76
77impl Default for PerformanceHistory {
78    /// Initialize performance history with empty records
79    fn default() -> Self {
80        Self {
81            strategy_performance: HashMap::new(),
82            total_sessions: 0,
83            average_overhead_percent: 0.0,
84        }
85    }
86}
87
88impl Default for StrategyPerformance {
89    /// Initialize strategy performance with neutral metrics
90    fn default() -> Self {
91        Self {
92            session_count: 0,
93            avg_overhead_percent: 2.0, // Conservative estimate
94            avg_init_time_us: 100.0,   // 100 microseconds baseline
95            success_rate: 1.0,         // Optimistic default
96            satisfaction_score: 0.8,   // Good default score
97        }
98    }
99}
100
101impl StrategyFactory {
102    /// Create new strategy factory with default configurations
103    /// Initializes templates optimized for different use cases
104    pub fn new() -> Self {
105        let mut strategy_configs = HashMap::new();
106
107        // Single-thread optimized configuration
108        strategy_configs.insert(
109            "single_thread".to_string(),
110            TrackerConfig {
111                sample_rate: 1.0,      // Full sampling for single-thread
112                max_overhead_mb: 32,   // Conservative memory limit
113                thread_affinity: None, // No affinity needed
114                custom_params: HashMap::new(),
115            },
116        );
117
118        // Multi-thread optimized configuration
119        strategy_configs.insert(
120            "thread_local".to_string(),
121            TrackerConfig {
122                sample_rate: 0.8,      // Reduced sampling for performance
123                max_overhead_mb: 64,   // Higher limit for multi-thread
124                thread_affinity: None, // Dynamic affinity
125                custom_params: HashMap::new(),
126            },
127        );
128
129        // Async optimized configuration
130        strategy_configs.insert(
131            "async".to_string(),
132            TrackerConfig {
133                sample_rate: 0.9,      // High sampling for async tracking
134                max_overhead_mb: 48,   // Moderate memory limit
135                thread_affinity: None, // No affinity for async
136                custom_params: HashMap::new(),
137            },
138        );
139
140        // Hybrid configuration balancing all needs
141        strategy_configs.insert(
142            "hybrid".to_string(),
143            TrackerConfig {
144                sample_rate: 0.7,      // Balanced sampling
145                max_overhead_mb: 96,   // Higher limit for complexity
146                thread_affinity: None, // Complex affinity patterns
147                custom_params: HashMap::new(),
148            },
149        );
150
151        Self {
152            strategy_configs,
153            performance_history: PerformanceHistory::default(),
154        }
155    }
156
157    /// Create single-thread strategy instance with optimized configuration
158    /// Best for simple, sequential applications
159    pub fn create_single_thread_strategy(&self) -> Result<Box<dyn MemoryTracker>, TrackerError> {
160        debug!("Creating single-thread strategy");
161
162        let config = self
163            .strategy_configs
164            .get("single_thread")
165            .cloned()
166            .unwrap_or_default();
167
168        let mut strategy = SingleThreadStrategy::new();
169        strategy.initialize(config)?;
170
171        info!("Single-thread strategy created successfully");
172        Ok(Box::new(strategy))
173    }
174
175    /// Create thread-local strategy instance for multi-threaded applications
176    /// Optimized for applications with multiple worker threads
177    pub fn create_thread_local_strategy(&self) -> Result<Box<dyn MemoryTracker>, TrackerError> {
178        debug!("Creating thread-local strategy");
179
180        let config = self
181            .strategy_configs
182            .get("thread_local")
183            .cloned()
184            .unwrap_or_default();
185
186        let mut strategy = ThreadLocalStrategy::new();
187        strategy.initialize(config)?;
188
189        info!("Thread-local strategy created successfully");
190        Ok(Box::new(strategy))
191    }
192
193    /// Create async strategy instance for async/await applications
194    /// Specialized for futures and task-based concurrency
195    pub fn create_async_strategy(&self) -> Result<Box<dyn MemoryTracker>, TrackerError> {
196        debug!("Creating async strategy");
197
198        let config = self
199            .strategy_configs
200            .get("async")
201            .cloned()
202            .unwrap_or_default();
203
204        let mut strategy = AsyncStrategy::new();
205        strategy.initialize(config)?;
206
207        info!("Async strategy created successfully");
208        Ok(Box::new(strategy))
209    }
210
211    /// Create hybrid strategy instance for complex applications
212    /// Handles mixed thread and async environments
213    pub fn create_hybrid_strategy(&self) -> Result<Box<dyn MemoryTracker>, TrackerError> {
214        debug!("Creating hybrid strategy");
215
216        let config = self
217            .strategy_configs
218            .get("hybrid")
219            .cloned()
220            .unwrap_or_default();
221
222        let mut strategy = HybridStrategy::new();
223        strategy.initialize(config)?;
224
225        info!("Hybrid strategy created successfully");
226        Ok(Box::new(strategy))
227    }
228
229    /// Record strategy performance for future optimization
230    /// Updates performance history with session metrics
231    pub fn record_performance(&mut self, strategy_name: &str, performance: StrategyPerformance) {
232        debug!("Recording performance for strategy: {}", strategy_name);
233
234        let (avg_overhead_percent, success_rate) = {
235            let entry = self
236                .performance_history
237                .strategy_performance
238                .entry(strategy_name.to_string())
239                .or_default();
240
241            // Update with exponential moving average
242            let weight = 0.1; // 10% weight for new data
243            entry.avg_overhead_percent = (1.0 - weight) * entry.avg_overhead_percent
244                + weight * performance.avg_overhead_percent;
245            entry.avg_init_time_us =
246                (1.0 - weight) * entry.avg_init_time_us + weight * performance.avg_init_time_us;
247            entry.success_rate =
248                (1.0 - weight) * entry.success_rate + weight * performance.success_rate;
249            entry.satisfaction_score =
250                (1.0 - weight) * entry.satisfaction_score + weight * performance.satisfaction_score;
251
252            entry.session_count += 1;
253
254            (entry.avg_overhead_percent, entry.success_rate)
255        };
256
257        self.performance_history.total_sessions += 1;
258
259        // Update average overhead across all strategies
260        self.update_average_overhead();
261
262        info!(
263            "Performance recorded for {}: overhead={:.2}%, success={:.2}%",
264            strategy_name,
265            avg_overhead_percent,
266            success_rate * 100.0
267        );
268    }
269
270    /// Update average overhead calculation across all strategies
271    fn update_average_overhead(&mut self) {
272        if self.performance_history.strategy_performance.is_empty() {
273            return;
274        }
275
276        let total_overhead: f64 = self
277            .performance_history
278            .strategy_performance
279            .values()
280            .map(|perf| perf.avg_overhead_percent)
281            .sum();
282
283        self.performance_history.average_overhead_percent =
284            total_overhead / self.performance_history.strategy_performance.len() as f64;
285    }
286
287    /// Get performance history for analysis
288    /// Provides read-only access to historical performance data
289    pub fn get_performance_history(&self) -> &PerformanceHistory {
290        &self.performance_history
291    }
292
293    /// Recommend optimal strategy based on performance history
294    /// Uses historical data to suggest best strategy for given requirements
295    pub fn recommend_strategy(&self, requirements: &StrategyRequirements) -> String {
296        debug!("Recommending strategy for requirements: {:?}", requirements);
297
298        let mut best_strategy = "single_thread".to_string();
299        let mut best_score = 0.0;
300
301        for (strategy_name, performance) in &self.performance_history.strategy_performance {
302            let score = self.calculate_strategy_score(strategy_name, performance, requirements);
303
304            if score > best_score {
305                best_score = score;
306                best_strategy = strategy_name.clone();
307            }
308        }
309
310        info!(
311            "Recommended strategy: {} (score: {:.2})",
312            best_strategy, best_score
313        );
314        best_strategy
315    }
316
317    /// Calculate strategy fitness score for given requirements
318    fn calculate_strategy_score(
319        &self,
320        _strategy_name: &str,
321        performance: &StrategyPerformance,
322        requirements: &StrategyRequirements,
323    ) -> f64 {
324        // Weight factors for different requirements
325        let overhead_weight = 0.3;
326        let speed_weight = 0.3;
327        let reliability_weight = 0.4;
328
329        // Overhead score (lower is better)
330        let overhead_score = if requirements.max_overhead_percent > 0.0 {
331            (requirements.max_overhead_percent - performance.avg_overhead_percent).max(0.0)
332                / requirements.max_overhead_percent
333        } else {
334            1.0 - (performance.avg_overhead_percent / 10.0) // Assume 10% is maximum acceptable
335        };
336
337        // Speed score (lower init time is better)
338        let speed_score = 1.0 - (performance.avg_init_time_us / 10000.0).min(1.0); // 10ms max
339
340        // Reliability score
341        let reliability_score = performance.success_rate * performance.satisfaction_score;
342
343        let score = overhead_weight * overhead_score
344            + speed_weight * speed_score
345            + reliability_weight * reliability_score;
346
347        score.clamp(0.0, 1.0) // Clamp to [0, 1]
348    }
349}
350
351impl Default for StrategyFactory {
352    /// Initialize strategy factory with default configurations
353    fn default() -> Self {
354        Self::new()
355    }
356}
357
358/// Strategy selection requirements
359/// Defines criteria for optimal strategy selection
360#[derive(Debug, Clone)]
361pub struct StrategyRequirements {
362    /// Maximum acceptable overhead percentage
363    pub max_overhead_percent: f64,
364    /// Preferred initialization time (microseconds)
365    pub preferred_init_time_us: f64,
366    /// Minimum required success rate
367    pub min_success_rate: f64,
368    /// Environment constraints
369    pub environment_constraints: Vec<String>,
370}
371
372impl Default for StrategyRequirements {
373    /// Default requirements for typical use cases
374    fn default() -> Self {
375        Self {
376            max_overhead_percent: 5.0,
377            preferred_init_time_us: 1000.0, // 1ms
378            min_success_rate: 0.95,
379            environment_constraints: vec![],
380        }
381    }
382}
383
384#[cfg(test)]
385mod tests {
386    use super::*;
387
388    #[test]
389    fn test_strategy_factory_creation() {
390        let factory = StrategyFactory::new();
391        assert_eq!(factory.strategy_configs.len(), 4);
392        assert!(factory.strategy_configs.contains_key("single_thread"));
393        assert!(factory.strategy_configs.contains_key("thread_local"));
394        assert!(factory.strategy_configs.contains_key("async"));
395        assert!(factory.strategy_configs.contains_key("hybrid"));
396    }
397
398    #[test]
399    fn test_single_thread_strategy_creation() {
400        let factory = StrategyFactory::new();
401        let result = factory.create_single_thread_strategy();
402        assert!(result.is_ok());
403    }
404
405    #[test]
406    fn test_all_strategy_creation() {
407        let factory = StrategyFactory::new();
408
409        // Test all strategy types
410        assert!(factory.create_single_thread_strategy().is_ok());
411        assert!(factory.create_thread_local_strategy().is_ok());
412        assert!(factory.create_async_strategy().is_ok());
413        assert!(factory.create_hybrid_strategy().is_ok());
414    }
415
416    #[test]
417    fn test_performance_recording() {
418        let mut factory = StrategyFactory::new();
419        let performance = StrategyPerformance {
420            session_count: 1,
421            avg_overhead_percent: 2.5,
422            avg_init_time_us: 150.0,
423            success_rate: 0.98,
424            satisfaction_score: 0.9,
425        };
426
427        factory.record_performance("single_thread", performance);
428
429        let history = factory.get_performance_history();
430        assert_eq!(history.total_sessions, 1);
431        assert!(history.strategy_performance.contains_key("single_thread"));
432    }
433
434    #[test]
435    fn test_strategy_recommendation() {
436        let mut factory = StrategyFactory::new();
437
438        // Record some performance data
439        factory.record_performance(
440            "single_thread",
441            StrategyPerformance {
442                avg_overhead_percent: 1.0,
443                success_rate: 0.99,
444                satisfaction_score: 0.95,
445                ..Default::default()
446            },
447        );
448
449        let requirements = StrategyRequirements::default();
450        let recommendation = factory.recommend_strategy(&requirements);
451
452        assert_eq!(recommendation, "single_thread");
453    }
454
455    #[test]
456    fn test_performance_history_update() {
457        let mut factory = StrategyFactory::new();
458
459        // Record multiple performance entries
460        for i in 0..5 {
461            factory.record_performance(
462                "test_strategy",
463                StrategyPerformance {
464                    avg_overhead_percent: 2.0 + i as f64 * 0.1,
465                    ..Default::default()
466                },
467            );
468        }
469
470        let history = factory.get_performance_history();
471        assert_eq!(history.total_sessions, 5);
472        assert!(history.average_overhead_percent > 0.0);
473    }
474}