Skip to main content

optirs_gpu/memory/management/
mod.rs

1// GPU memory management modules
2//
3// This module provides advanced GPU memory management capabilities including
4// garbage collection, prefetching, eviction policies, and defragmentation.
5
6pub mod defragmentation;
7pub mod eviction_policies;
8pub mod garbage_collection;
9pub mod prefetching;
10
11use std::collections::HashMap;
12use std::ffi::c_void;
13use std::time::{Duration, Instant};
14
15use crate::memory::management::eviction_policies::EvictionConfig;
16
17pub use garbage_collection::{
18    GCConfig, GCStats, GarbageCollectionEngine, GarbageCollector, GenerationalCollector,
19    IncrementalCollector, MarkSweepCollector, ReferenceTracker,
20};
21
22pub use prefetching::{
23    AccessHistoryTracker, PrefetchCache, PrefetchConfig, PrefetchStrategy, PrefetchingEngine,
24    SequentialPrefetcher, StridePrefetcher,
25};
26
27pub use eviction_policies::{
28    ARCPolicy, ClockPolicy, EvictionEngine, EvictionPerformanceMonitor, EvictionPolicy, FIFOPolicy,
29    LFUPolicy, LRUPolicy, MemoryRegion, WorkloadAwarePolicy,
30};
31
32pub use defragmentation::{
33    CompactionAlgorithm, CompactionStrategy, DefragConfig, DefragError, DefragmentationEngine,
34    SlidingCompactionStrategy, ThreadSafeDefragmentationEngine, TwoPointerCompactionStrategy,
35};
36
37/// Unified memory management configuration
38#[derive(Debug, Clone)]
39pub struct MemoryManagementConfig {
40    /// Garbage collection configuration
41    pub gc_config: GCConfig,
42    /// Prefetching configuration  
43    pub prefetch_config: PrefetchConfig,
44    /// Defragmentation configuration
45    pub defrag_config: DefragConfig,
46    /// Enable background management
47    pub enable_background_management: bool,
48    /// Management thread count
49    pub management_threads: usize,
50    /// Memory pressure threshold
51    pub memory_pressure_threshold: f64,
52    /// Performance monitoring interval
53    pub monitoring_interval: Duration,
54}
55
56impl Default for MemoryManagementConfig {
57    fn default() -> Self {
58        Self {
59            gc_config: GCConfig::default(),
60            prefetch_config: PrefetchConfig::default(),
61            defrag_config: DefragConfig::default(),
62            enable_background_management: true,
63            management_threads: 2,
64            memory_pressure_threshold: 0.8,
65            monitoring_interval: Duration::from_millis(100),
66        }
67    }
68}
69
70/// Integrated memory management system
71pub struct IntegratedMemoryManager {
72    /// Garbage collection engine
73    gc_engine: GarbageCollectionEngine,
74    /// Prefetching engine
75    prefetch_engine: PrefetchingEngine,
76    /// Eviction engine
77    eviction_engine: EvictionEngine,
78    /// Defragmentation engine
79    defrag_engine: DefragmentationEngine,
80    /// Configuration
81    config: MemoryManagementConfig,
82    /// Management statistics
83    stats: ManagementStats,
84    /// Background management enabled
85    background_enabled: bool,
86}
87
88/// Memory management statistics
89#[derive(Debug, Clone, Default)]
90pub struct ManagementStats {
91    pub gc_collections: u64,
92    pub objects_collected: u64,
93    pub bytes_freed_by_gc: u64,
94    pub prefetch_requests: u64,
95    pub prefetch_hits: u64,
96    pub prefetch_accuracy: f64,
97    pub evictions_performed: u64,
98    pub bytes_evicted: u64,
99    pub defragmentation_cycles: u64,
100    pub fragmentation_reduced: usize,
101    pub total_management_time: Duration,
102    pub memory_pressure_events: u64,
103}
104
105impl IntegratedMemoryManager {
106    /// Create new integrated memory manager
107    pub fn new(config: MemoryManagementConfig) -> Self {
108        let gc_engine = GarbageCollectionEngine::new(config.gc_config.clone());
109        let prefetch_engine = PrefetchingEngine::new(config.prefetch_config.clone());
110        let eviction_engine = EvictionEngine::new(EvictionConfig::default());
111        let defrag_engine = DefragmentationEngine::new(config.defrag_config.clone());
112
113        Self {
114            gc_engine,
115            prefetch_engine,
116            eviction_engine,
117            defrag_engine,
118            config,
119            stats: ManagementStats::default(),
120            background_enabled: false,
121        }
122    }
123
124    /// Start background memory management
125    pub fn start_background_management(&mut self) -> Result<(), MemoryManagementError> {
126        if !self.config.enable_background_management {
127            return Err(MemoryManagementError::BackgroundManagementDisabled);
128        }
129
130        self.background_enabled = true;
131        Ok(())
132    }
133
134    /// Stop background memory management
135    pub fn stop_background_management(&mut self) {
136        self.background_enabled = false;
137    }
138
139    /// Run garbage collection
140    ///
141    /// `memory_regions` describes the caller's current view of live
142    /// allocations, but it is intentionally not registered with
143    /// [`GarbageCollectionEngine`]: that engine only selects a collector for
144    /// a region when `region.utilization < mark_threshold` (see
145    /// `MarkSweepCollector::can_collect`), while every region built by the
146    /// current caller is a single allocation at 100% utilization. Feeding
147    /// such regions in would turn every call into a hard
148    /// `GCError::NoSuitableCollector` instead of today's harmless no-op.
149    /// Tracked as a finding rather than force-integrated; see the crate's
150    /// lint/unwrap sweep notes.
151    pub fn run_garbage_collection(
152        &mut self,
153        _memory_regions: &HashMap<usize, MemoryRegion>,
154    ) -> Result<usize, MemoryManagementError> {
155        let start_time = Instant::now();
156
157        let gc_results = self
158            .gc_engine
159            .collect()
160            .map_err(|e| MemoryManagementError::GarbageCollectionFailed(format!("{:?}", e)))?;
161        let bytes_freed: usize = gc_results.iter().map(|r| r.bytes_collected).sum();
162
163        self.stats.gc_collections += 1;
164        self.stats.bytes_freed_by_gc += bytes_freed as u64;
165        self.stats.total_management_time += start_time.elapsed();
166
167        Ok(bytes_freed)
168    }
169
170    /// Perform prefetch operation
171    ///
172    /// Records the access with the [`PrefetchingEngine`] so its access-history
173    /// tracker and pattern strategies see real data; the returned bool
174    /// reflects whether this access was itself a hit against data the engine
175    /// had already prefetched (a genuine cache hit), not a fabricated value.
176    pub fn prefetch(
177        &mut self,
178        address: *mut c_void,
179        size: usize,
180        access_pattern: Option<&str>,
181    ) -> Result<bool, MemoryManagementError> {
182        let start_time = Instant::now();
183
184        let access_type = match access_pattern {
185            Some(pattern) if pattern.eq_ignore_ascii_case("write") => {
186                prefetching::AccessType::Write
187            }
188            Some(pattern) if pattern.eq_ignore_ascii_case("read") => prefetching::AccessType::Read,
189            _ => prefetching::AccessType::ReadWrite,
190        };
191
192        let hits_before = self.prefetch_engine.get_stats().successful_prefetches;
193        self.prefetch_engine
194            .record_access(prefetching::MemoryAccess {
195                address: address as usize,
196                size,
197                timestamp: Instant::now(),
198                access_type,
199                context_id: 0,
200                kernel_id: None,
201            });
202        let prefetched = self.prefetch_engine.get_stats().successful_prefetches > hits_before;
203
204        self.stats.prefetch_requests += 1;
205        if prefetched {
206            self.stats.prefetch_hits += 1;
207        }
208
209        self.stats.prefetch_accuracy =
210            self.stats.prefetch_hits as f64 / self.stats.prefetch_requests as f64;
211        self.stats.total_management_time += start_time.elapsed();
212
213        Ok(prefetched)
214    }
215
216    /// Perform memory eviction
217    ///
218    /// Registers `memory_regions` with the [`EvictionEngine`] (most-pressured,
219    /// then largest, region first) and evicts real objects from it via the
220    /// engine's active policy until `target_bytes` have been reclaimed or
221    /// there is nothing left to evict. The returned count is the true sum of
222    /// evicted object sizes, not an estimate.
223    pub fn evict_memory(
224        &mut self,
225        target_bytes: usize,
226        memory_regions: &HashMap<usize, MemoryRegion>,
227    ) -> Result<usize, MemoryManagementError> {
228        let start_time = Instant::now();
229        let mut bytes_evicted = 0usize;
230
231        let mut ordered: Vec<&MemoryRegion> = memory_regions.values().collect();
232        ordered.sort_by(|a, b| {
233            b.pressure
234                .partial_cmp(&a.pressure)
235                .unwrap_or(std::cmp::Ordering::Equal)
236                .then_with(|| b.size.cmp(&a.size))
237        });
238
239        for region in ordered {
240            if bytes_evicted >= target_bytes {
241                break;
242            }
243
244            self.eviction_engine.register_region(
245                region.base_addr,
246                region.size,
247                region.region_type.clone(),
248            );
249            for object in region.objects.values() {
250                self.eviction_engine
251                    .add_object(region.base_addr, object.clone())
252                    .map_err(|e| MemoryManagementError::EvictionFailed(format!("{:?}", e)))?;
253            }
254
255            let victims = self
256                .eviction_engine
257                .evict(region.base_addr, target_bytes - bytes_evicted)
258                .map_err(|e| MemoryManagementError::EvictionFailed(format!("{:?}", e)))?;
259
260            for victim_addr in &victims {
261                if let Some(object) = region.objects.get(victim_addr) {
262                    bytes_evicted += object.size;
263                }
264            }
265        }
266
267        self.stats.evictions_performed += 1;
268        self.stats.bytes_evicted += bytes_evicted as u64;
269        self.stats.total_management_time += start_time.elapsed();
270
271        Ok(bytes_evicted)
272    }
273
274    /// Run defragmentation
275    ///
276    /// `memory_regions` is intentionally not registered with
277    /// [`DefragmentationEngine`]: both built-in compaction strategies require
278    /// `MemoryLayoutTracker::get_total_free_space() > 0` to be eligible
279    /// (`can_handle`), but the current caller only ever reports one region
280    /// per live allocation with no free space (region size == its single
281    /// object's size). Registering that data would not change the outcome
282    /// (`DefragError::NoSuitableStrategy` either way) and free-space
283    /// tracking would need to be added at the caller first. Tracked as a
284    /// finding rather than force-integrated; see the crate's lint/unwrap
285    /// sweep notes.
286    pub fn defragment(
287        &mut self,
288        _memory_regions: &HashMap<usize, MemoryRegion>,
289    ) -> Result<usize, MemoryManagementError> {
290        let start_time = Instant::now();
291
292        let compaction_result = self
293            .defrag_engine
294            .defragment()
295            .map_err(|e| MemoryManagementError::DefragmentationFailed(format!("{:?}", e)))?;
296
297        let fragmentation_reduced = compaction_result.bytes_moved;
298
299        self.stats.defragmentation_cycles += 1;
300        self.stats.fragmentation_reduced += fragmentation_reduced;
301        self.stats.total_management_time += start_time.elapsed();
302
303        Ok(fragmentation_reduced)
304    }
305
306    /// Check memory pressure and trigger appropriate management
307    pub fn handle_memory_pressure(
308        &mut self,
309        memory_usage_ratio: f64,
310        memory_regions: &HashMap<usize, MemoryRegion>,
311    ) -> Result<(), MemoryManagementError> {
312        if memory_usage_ratio > self.config.memory_pressure_threshold {
313            self.stats.memory_pressure_events += 1;
314
315            // Try garbage collection first
316            let _ = self.run_garbage_collection(memory_regions)?;
317
318            // If still under pressure, try eviction
319            if memory_usage_ratio > 0.9 {
320                let target_eviction =
321                    (memory_usage_ratio - self.config.memory_pressure_threshold) * 1_000_000.0; // Estimate bytes
322                let _ = self.evict_memory(target_eviction as usize, memory_regions)?;
323            }
324
325            // If severely fragmented, run defragmentation
326            if memory_usage_ratio > 0.95 {
327                let _ = self.defragment(memory_regions)?;
328            }
329        }
330
331        Ok(())
332    }
333
334    /// Update access patterns for adaptive management
335    ///
336    /// Feeds the access into the [`PrefetchingEngine`]'s access-history
337    /// tracker (mapped from this module's coarse [`AccessType`] to
338    /// [`prefetching::AccessType`]) so its pattern strategies observe real
339    /// traffic instead of being permanently starved of data.
340    pub fn update_access_pattern(
341        &mut self,
342        address: *mut c_void,
343        size: usize,
344        access_type: AccessType,
345    ) -> Result<(), MemoryManagementError> {
346        let mapped_type = match access_type {
347            AccessType::Read | AccessType::Sequential => prefetching::AccessType::Read,
348            AccessType::Write => prefetching::AccessType::Write,
349            AccessType::ReadWrite | AccessType::Random => prefetching::AccessType::ReadWrite,
350        };
351
352        self.prefetch_engine
353            .record_access(prefetching::MemoryAccess {
354                address: address as usize,
355                size,
356                timestamp: Instant::now(),
357                access_type: mapped_type,
358                context_id: 0,
359                kernel_id: None,
360            });
361
362        Ok(())
363    }
364
365    /// Get management statistics
366    pub fn get_stats(&self) -> &ManagementStats {
367        &self.stats
368    }
369
370    /// Get garbage collection stats
371    pub fn get_gc_stats(&self) -> GCStats {
372        self.gc_engine.get_stats().clone()
373    }
374
375    /// Get prefetch performance
376    pub fn get_prefetch_performance(&self) -> PrefetchPerformance {
377        PrefetchPerformance {
378            requests: self.stats.prefetch_requests,
379            hits: self.stats.prefetch_hits,
380            accuracy: self.stats.prefetch_accuracy,
381            // FEATURE STATUS (v1.0.0): Prefetch cache size tracking pending
382            // Will be implemented when prefetch_engine.get_cache_size() is available (v1.1.0+)
383            cache_size: 0,
384        }
385    }
386
387    /// Optimize management policies based on workload
388    pub fn optimize_policies(&mut self) -> Result<(), MemoryManagementError> {
389        // FEATURE STATUS (v1.0.0): Adaptive policy optimization pending
390        //
391        // The individual policy engines (GC, eviction, prefetch) are functional,
392        // but automatic policy selection based on workload analysis requires
393        // runtime profiling capabilities planned for v1.1.0.
394        //
395        // PLANNED (v1.1.0+):
396        // - let access_patterns = self.prefetch_engine.analyze_access_patterns();
397        // - self.gc_engine.set_preferred_strategy("generational");
398        // - self.eviction_engine.set_active_policy("lru");
399        //
400        // For v1.0.0, users can manually configure policies through the
401        // MemoryManagementConfig during initialization.
402
403        Ok(())
404    }
405}
406
407/// Memory access types for pattern tracking
408#[derive(Debug, Clone)]
409pub enum AccessType {
410    Read,
411    Write,
412    ReadWrite,
413    Sequential,
414    Random,
415}
416
417/// Prefetch performance metrics
418#[derive(Debug, Clone)]
419pub struct PrefetchPerformance {
420    pub requests: u64,
421    pub hits: u64,
422    pub accuracy: f64,
423    pub cache_size: usize,
424}
425
426/// Access pattern analysis
427#[derive(Debug, Clone)]
428pub struct AccessPatterns {
429    pub temporal_locality: f64,
430    pub spatial_locality: f64,
431    pub frequency_based: bool,
432    pub stride_patterns: Vec<i64>,
433}
434
435/// Memory management errors
436#[derive(Debug, Clone)]
437pub enum MemoryManagementError {
438    GarbageCollectionFailed(String),
439    PrefetchFailed(String),
440    EvictionFailed(String),
441    DefragmentationFailed(String),
442    BackgroundManagementDisabled,
443    InvalidConfiguration(String),
444    InternalError(String),
445}
446
447impl std::fmt::Display for MemoryManagementError {
448    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
449        match self {
450            MemoryManagementError::GarbageCollectionFailed(msg) => {
451                write!(f, "Garbage collection failed: {}", msg)
452            }
453            MemoryManagementError::PrefetchFailed(msg) => write!(f, "Prefetch failed: {}", msg),
454            MemoryManagementError::EvictionFailed(msg) => write!(f, "Eviction failed: {}", msg),
455            MemoryManagementError::DefragmentationFailed(msg) => {
456                write!(f, "Defragmentation failed: {}", msg)
457            }
458            MemoryManagementError::BackgroundManagementDisabled => {
459                write!(f, "Background management is disabled")
460            }
461            MemoryManagementError::InvalidConfiguration(msg) => {
462                write!(f, "Invalid configuration: {}", msg)
463            }
464            MemoryManagementError::InternalError(msg) => write!(f, "Internal error: {}", msg),
465        }
466    }
467}
468
469impl std::error::Error for MemoryManagementError {}
470
471#[cfg(test)]
472mod tests {
473    use super::*;
474
475    #[test]
476    fn test_integrated_manager_creation() {
477        let config = MemoryManagementConfig::default();
478        let manager = IntegratedMemoryManager::new(config);
479        assert!(!manager.background_enabled);
480    }
481
482    #[test]
483    fn test_background_management() {
484        let config = MemoryManagementConfig::default();
485        let mut manager = IntegratedMemoryManager::new(config);
486        let result = manager.start_background_management();
487        assert!(result.is_ok());
488        assert!(manager.background_enabled);
489    }
490
491    #[test]
492    fn test_evict_memory_reclaims_real_bytes() {
493        use super::eviction_policies::{CacheObject, ObjectPriority, ObjectType, RegionType};
494
495        let config = MemoryManagementConfig::default();
496        let mut manager = IntegratedMemoryManager::new(config);
497
498        let object_size = 4096usize;
499        let mut objects = HashMap::new();
500        objects.insert(
501            0x1000,
502            CacheObject {
503                address: 0x1000,
504                size: object_size,
505                created_at: Instant::now(),
506                last_access: Instant::now(),
507                access_count: 1,
508                access_frequency: 1.0,
509                priority: ObjectPriority::Normal,
510                kernel_context: None,
511                object_type: ObjectType::Data,
512                eviction_cost: 1.0,
513                replacement_cost: 1.0,
514            },
515        );
516        let mut memory_regions = HashMap::new();
517        memory_regions.insert(
518            0x1000,
519            MemoryRegion {
520                base_addr: 0x1000,
521                size: object_size,
522                objects,
523                region_type: RegionType::Buffer,
524                pressure: 1.0,
525                last_eviction: None,
526            },
527        );
528
529        let evicted = manager
530            .evict_memory(object_size, &memory_regions)
531            .expect("eviction against a registered region should succeed");
532        assert_eq!(evicted, object_size);
533        assert_eq!(manager.get_stats().bytes_evicted, object_size as u64);
534        assert_eq!(manager.get_stats().evictions_performed, 1);
535    }
536
537    #[test]
538    fn test_evict_memory_empty_regions_evicts_nothing() {
539        let config = MemoryManagementConfig::default();
540        let mut manager = IntegratedMemoryManager::new(config);
541
542        let evicted = manager
543            .evict_memory(4096, &HashMap::new())
544            .expect("eviction over no regions should still succeed");
545        assert_eq!(evicted, 0);
546    }
547
548    #[test]
549    fn test_prefetch_records_access_and_updates_stats() {
550        let config = MemoryManagementConfig::default();
551        let mut manager = IntegratedMemoryManager::new(config);
552
553        let result = manager.prefetch(std::ptr::null_mut(), 128, Some("sequential"));
554        assert!(result.is_ok());
555        assert_eq!(manager.get_stats().prefetch_requests, 1);
556    }
557
558    #[test]
559    fn test_update_access_pattern_feeds_prefetch_engine() {
560        let config = MemoryManagementConfig::default();
561        let mut manager = IntegratedMemoryManager::new(config);
562
563        // Four consecutive forward accesses (64 bytes apart, same "thread")
564        // form a sequential run of length >= SequentialConfig's
565        // min_sequence_length (3), which should make the engine's
566        // SequentialPrefetcher strategy fire and queue real requests -- proof
567        // that the access data actually reaches the prefetching engine
568        // rather than being dropped on the floor.
569        let base = 0x10000usize;
570        for i in 0..4u64 {
571            let ptr = (base + i as usize * 64) as *mut std::ffi::c_void;
572            let result = manager.update_access_pattern(ptr, 64, AccessType::Sequential);
573            assert!(result.is_ok());
574        }
575
576        assert!(manager.prefetch_engine.get_stats().total_requests > 0);
577    }
578
579    #[test]
580    fn test_stats_initialization() {
581        let config = MemoryManagementConfig::default();
582        let manager = IntegratedMemoryManager::new(config);
583        let stats = manager.get_stats();
584        assert_eq!(stats.gc_collections, 0);
585        assert_eq!(stats.prefetch_requests, 0);
586    }
587}