Skip to main content

optirs_gpu/memory/management/
defragmentation.rs

1// Memory defragmentation for GPU memory management
2//
3// This module provides sophisticated defragmentation algorithms to reduce
4// memory fragmentation and improve allocation success rates and performance.
5
6use std::collections::{BTreeMap, HashMap, VecDeque};
7use std::sync::{Arc, Mutex};
8use std::time::{Duration, Instant};
9
10/// Memory defragmentation engine
11pub struct DefragmentationEngine {
12    /// Configuration
13    config: DefragConfig,
14    /// Statistics
15    stats: DefragStats,
16    /// Active defragmentation tasks
17    active_tasks: Vec<DefragTask>,
18    /// Memory layout tracking
19    memory_layout: MemoryLayoutTracker,
20    /// Compaction strategies
21    strategies: Vec<Box<dyn CompactionStrategy>>,
22    /// Performance history
23    performance_history: VecDeque<DefragPerformance>,
24}
25
26/// Defragmentation configuration
27#[derive(Debug, Clone)]
28pub struct DefragConfig {
29    /// Enable automatic defragmentation
30    pub auto_defrag: bool,
31    /// Fragmentation threshold for triggering defrag (0.0-1.0)
32    pub fragmentation_threshold: f64,
33    /// Maximum time to spend on defragmentation per cycle
34    pub max_defrag_time: Duration,
35    /// Minimum free space required before defragmentation
36    pub min_free_space: usize,
37    /// Enable incremental defragmentation
38    pub incremental_defrag: bool,
39    /// Chunk size for incremental operations
40    pub incremental_chunk_size: usize,
41    /// Enable parallel defragmentation
42    pub parallel_defrag: bool,
43    /// Number of worker threads for parallel operations
44    pub worker_threads: usize,
45    /// Compaction algorithm preference
46    pub preferred_algorithm: CompactionAlgorithm,
47    /// Enable statistics collection
48    pub enable_stats: bool,
49}
50
51impl Default for DefragConfig {
52    fn default() -> Self {
53        Self {
54            auto_defrag: true,
55            fragmentation_threshold: 0.3,
56            max_defrag_time: Duration::from_millis(100),
57            min_free_space: 1024 * 1024, // 1MB
58            incremental_defrag: true,
59            incremental_chunk_size: 64 * 1024, // 64KB
60            parallel_defrag: false,
61            worker_threads: 2,
62            preferred_algorithm: CompactionAlgorithm::SlidingCompaction,
63            enable_stats: true,
64        }
65    }
66}
67
68/// Compaction algorithms available
69#[derive(Debug, Clone, PartialEq)]
70pub enum CompactionAlgorithm {
71    /// Simple sliding compaction
72    SlidingCompaction,
73    /// Two-pointer compaction
74    TwoPointer,
75    /// Mark and sweep with compaction
76    MarkSweepCompact,
77    /// Copying garbage collection style
78    CopyingGC,
79    /// Generational compaction
80    Generational,
81    /// Adaptive algorithm selection
82    Adaptive,
83}
84
85/// Defragmentation statistics
86#[derive(Debug, Clone, Default)]
87pub struct DefragStats {
88    /// Total defragmentation cycles
89    pub total_cycles: u64,
90    /// Total bytes moved during defragmentation
91    pub total_bytes_moved: u64,
92    /// Total time spent on defragmentation
93    pub total_time_spent: Duration,
94    /// Average fragmentation reduction per cycle
95    pub average_fragmentation_reduction: f64,
96    /// Successful defragmentation attempts
97    pub successful_cycles: u64,
98    /// Failed defragmentation attempts
99    pub failed_cycles: u64,
100    /// Average cycle time
101    pub average_cycle_time: Duration,
102    /// Peak fragmentation level observed
103    pub peak_fragmentation: f64,
104    /// Current fragmentation level
105    pub current_fragmentation: f64,
106    /// Objects relocated during defragmentation
107    pub objects_relocated: u64,
108    /// Memory compaction efficiency
109    pub compaction_efficiency: f64,
110}
111
112/// Individual defragmentation task
113#[derive(Debug, Clone)]
114pub struct DefragTask {
115    /// Task ID
116    pub id: u64,
117    /// Start address of memory region
118    pub start_addr: usize,
119    /// Size of memory region
120    pub size: usize,
121    /// Algorithm to use
122    pub algorithm: CompactionAlgorithm,
123    /// Task status
124    pub status: TaskStatus,
125    /// Creation time
126    pub created_at: Instant,
127    /// Estimated completion time
128    pub estimated_completion: Option<Duration>,
129    /// Priority level
130    pub priority: TaskPriority,
131}
132
133/// Task status enumeration
134#[derive(Debug, Clone, PartialEq)]
135pub enum TaskStatus {
136    Pending,
137    Running,
138    Paused,
139    Completed,
140    Failed(String),
141    Cancelled,
142}
143
144/// Task priority levels
145#[derive(Debug, Clone, PartialEq, Ord, PartialOrd, Eq)]
146pub enum TaskPriority {
147    Low,
148    Normal,
149    High,
150    Critical,
151}
152
153/// Memory layout tracking for defragmentation
154pub struct MemoryLayoutTracker {
155    /// Free memory regions
156    free_regions: BTreeMap<usize, FreeRegion>,
157    /// Allocated memory blocks
158    allocated_blocks: HashMap<usize, AllocatedBlock>,
159    /// Fragmentation index cache
160    fragmentation_cache: Option<(f64, Instant)>,
161    /// Cache validity duration
162    cache_validity: Duration,
163}
164
165/// Free memory region descriptor
166#[derive(Debug, Clone)]
167pub struct FreeRegion {
168    pub address: usize,
169    pub size: usize,
170    pub age: Duration,
171    pub access_frequency: u32,
172    pub adjacent_to_allocated: bool,
173}
174
175/// Allocated memory block descriptor
176#[derive(Debug, Clone)]
177pub struct AllocatedBlock {
178    pub address: usize,
179    pub size: usize,
180    pub allocation_time: Instant,
181    pub last_access: Option<Instant>,
182    pub access_count: u32,
183    pub is_movable: bool,
184    pub reference_count: u32,
185}
186
187/// Compaction strategy interface
188pub trait CompactionStrategy: Send + Sync {
189    fn name(&self) -> &str;
190    fn can_handle(&self, layout: &MemoryLayoutTracker) -> bool;
191    fn estimate_benefit(&self, layout: &MemoryLayoutTracker) -> f64;
192    fn execute(
193        &mut self,
194        layout: &mut MemoryLayoutTracker,
195    ) -> Result<CompactionResult, DefragError>;
196    fn get_statistics(&self) -> CompactionStats;
197    /// Clear this strategy's accumulated statistics, e.g. as part of
198    /// [`DefragmentationEngine::reset`].
199    fn reset(&mut self);
200}
201
202/// Result of a compaction operation
203#[derive(Debug, Clone)]
204pub struct CompactionResult {
205    pub bytes_moved: usize,
206    pub objects_relocated: u32,
207    pub fragmentation_reduction: f64,
208    pub time_taken: Duration,
209    pub algorithm_used: CompactionAlgorithm,
210    pub efficiency_score: f64,
211}
212
213/// Statistics for compaction strategies
214#[derive(Debug, Clone, Default)]
215pub struct CompactionStats {
216    pub executions: u64,
217    pub total_bytes_moved: u64,
218    pub total_objects_relocated: u64,
219    pub total_time: Duration,
220    pub average_efficiency: f64,
221    pub success_rate: f64,
222}
223
224/// Performance metrics for defragmentation
225#[derive(Debug, Clone)]
226pub struct DefragPerformance {
227    pub timestamp: Instant,
228    pub fragmentation_before: f64,
229    pub fragmentation_after: f64,
230    pub time_taken: Duration,
231    pub bytes_moved: usize,
232    pub algorithm_used: CompactionAlgorithm,
233    pub success: bool,
234}
235
236impl Default for MemoryLayoutTracker {
237    fn default() -> Self {
238        Self::new()
239    }
240}
241
242impl MemoryLayoutTracker {
243    pub fn new() -> Self {
244        Self {
245            free_regions: BTreeMap::new(),
246            allocated_blocks: HashMap::new(),
247            fragmentation_cache: None,
248            cache_validity: Duration::from_millis(100),
249        }
250    }
251
252    /// Calculate current fragmentation index
253    pub fn calculate_fragmentation(&mut self) -> f64 {
254        let now = Instant::now();
255
256        // Check cache validity
257        if let Some((cached_frag, cache_time)) = self.fragmentation_cache {
258            if now.duration_since(cache_time) < self.cache_validity {
259                return cached_frag;
260            }
261        }
262
263        let fragmentation = if self.free_regions.is_empty() {
264            0.0
265        } else {
266            let total_free_space: usize = self.free_regions.values().map(|r| r.size).sum();
267            let largest_free_block = self
268                .free_regions
269                .values()
270                .map(|r| r.size)
271                .max()
272                .unwrap_or(0);
273
274            if total_free_space == 0 {
275                0.0
276            } else {
277                1.0 - (largest_free_block as f64 / total_free_space as f64)
278            }
279        };
280
281        // Cache the result
282        self.fragmentation_cache = Some((fragmentation, now));
283        fragmentation
284    }
285
286    /// Add a free region
287    pub fn add_free_region(&mut self, address: usize, size: usize) {
288        let region = FreeRegion {
289            address,
290            size,
291            age: Duration::from_secs(0),
292            access_frequency: 0,
293            adjacent_to_allocated: self.is_adjacent_to_allocated(address, size),
294        };
295        self.free_regions.insert(address, region);
296        self.invalidate_cache();
297    }
298
299    /// Add an allocated block
300    pub fn add_allocated_block(&mut self, address: usize, size: usize, is_movable: bool) {
301        let block = AllocatedBlock {
302            address,
303            size,
304            allocation_time: Instant::now(),
305            last_access: None,
306            access_count: 0,
307            is_movable,
308            reference_count: 1,
309        };
310        self.allocated_blocks.insert(address, block);
311        self.invalidate_cache();
312    }
313
314    /// Remove a free region
315    pub fn remove_free_region(&mut self, address: usize) -> Option<FreeRegion> {
316        self.invalidate_cache();
317        self.free_regions.remove(&address)
318    }
319
320    /// Remove an allocated block
321    pub fn remove_allocated_block(&mut self, address: usize) -> Option<AllocatedBlock> {
322        self.invalidate_cache();
323        self.allocated_blocks.remove(&address)
324    }
325
326    /// Get total free space
327    pub fn get_total_free_space(&self) -> usize {
328        self.free_regions.values().map(|r| r.size).sum()
329    }
330
331    /// Get largest free block
332    pub fn get_largest_free_block(&self) -> usize {
333        self.free_regions
334            .values()
335            .map(|r| r.size)
336            .max()
337            .unwrap_or(0)
338    }
339
340    /// Get movable blocks for compaction
341    pub fn get_movable_blocks(&self) -> Vec<&AllocatedBlock> {
342        self.allocated_blocks
343            .values()
344            .filter(|b| b.is_movable)
345            .collect()
346    }
347
348    /// Check if address range is adjacent to allocated blocks
349    fn is_adjacent_to_allocated(&self, address: usize, size: usize) -> bool {
350        let end_address = address + size;
351
352        for block in self.allocated_blocks.values() {
353            let block_end = block.address + block.size;
354
355            // Check if regions are adjacent
356            if block_end == address || block.address == end_address {
357                return true;
358            }
359        }
360
361        false
362    }
363
364    /// Invalidate fragmentation cache
365    fn invalidate_cache(&mut self) {
366        self.fragmentation_cache = None;
367    }
368
369    /// Coalesce adjacent free regions
370    pub fn coalesce_free_regions(&mut self) -> usize {
371        let mut coalesced_count = 0;
372        let mut regions_to_remove = Vec::new();
373        let mut regions_to_add = Vec::new();
374
375        let addresses: Vec<usize> = self.free_regions.keys().cloned().collect();
376
377        for &addr in &addresses {
378            if regions_to_remove.contains(&addr) {
379                continue;
380            }
381
382            if let Some(region) = self.free_regions.get(&addr) {
383                let end_addr = addr + region.size;
384
385                // Look for adjacent region
386                if let Some(next_region) = self.free_regions.get(&end_addr) {
387                    // Coalesce regions
388                    let coalesced_region = FreeRegion {
389                        address: addr,
390                        size: region.size + next_region.size,
391                        age: region.age.min(next_region.age),
392                        access_frequency: region.access_frequency + next_region.access_frequency,
393                        adjacent_to_allocated: region.adjacent_to_allocated
394                            || next_region.adjacent_to_allocated,
395                    };
396
397                    regions_to_remove.push(addr);
398                    regions_to_remove.push(end_addr);
399                    regions_to_add.push((addr, coalesced_region));
400                    coalesced_count += 1;
401                }
402            }
403        }
404
405        // Apply changes
406        for addr in regions_to_remove {
407            self.free_regions.remove(&addr);
408        }
409
410        for (addr, region) in regions_to_add {
411            self.free_regions.insert(addr, region);
412        }
413
414        self.invalidate_cache();
415        coalesced_count
416    }
417}
418
419/// Sliding compaction strategy
420pub struct SlidingCompactionStrategy {
421    stats: CompactionStats,
422}
423
424impl Default for SlidingCompactionStrategy {
425    fn default() -> Self {
426        Self::new()
427    }
428}
429
430impl SlidingCompactionStrategy {
431    pub fn new() -> Self {
432        Self {
433            stats: CompactionStats::default(),
434        }
435    }
436}
437
438impl CompactionStrategy for SlidingCompactionStrategy {
439    fn name(&self) -> &str {
440        "SlidingCompaction"
441    }
442
443    fn can_handle(&self, layout: &MemoryLayoutTracker) -> bool {
444        !layout.get_movable_blocks().is_empty() && layout.get_total_free_space() > 0
445    }
446
447    fn estimate_benefit(&self, layout: &MemoryLayoutTracker) -> f64 {
448        let movable_blocks = layout.get_movable_blocks();
449        let total_free = layout.get_total_free_space();
450        let largest_free = layout.get_largest_free_block();
451
452        if total_free == 0 {
453            return 0.0;
454        }
455
456        // Estimate benefit based on potential consolidation
457        let fragmentation_reduction = (total_free - largest_free) as f64 / total_free as f64;
458        let mobility_factor =
459            movable_blocks.len() as f64 / (layout.allocated_blocks.len() as f64 + 1.0);
460
461        fragmentation_reduction * mobility_factor
462    }
463
464    fn execute(
465        &mut self,
466        layout: &mut MemoryLayoutTracker,
467    ) -> Result<CompactionResult, DefragError> {
468        let start_time = Instant::now();
469        let initial_fragmentation = layout.calculate_fragmentation();
470
471        let movable_blocks: Vec<AllocatedBlock> =
472            layout.get_movable_blocks().into_iter().cloned().collect();
473
474        if movable_blocks.is_empty() {
475            return Err(DefragError::NoMovableBlocks);
476        }
477
478        let mut bytes_moved = 0;
479        let mut objects_relocated = 0;
480        let mut compaction_address = 0;
481
482        // Find the starting address for compaction
483        if let Some((&first_free_addr, _)) = layout.free_regions.iter().next() {
484            compaction_address = first_free_addr;
485        }
486
487        // Sort blocks by address for sliding compaction
488        let mut sorted_blocks = movable_blocks;
489        sorted_blocks.sort_by_key(|b| b.address);
490
491        // Perform sliding compaction
492        for block in sorted_blocks {
493            if block.address > compaction_address {
494                // Move block to compaction address
495                layout.remove_allocated_block(block.address);
496                layout.add_allocated_block(compaction_address, block.size, block.is_movable);
497
498                // Add freed space to free regions
499                layout.add_free_region(block.address, block.size);
500
501                bytes_moved += block.size;
502                objects_relocated += 1;
503
504                compaction_address += block.size;
505            } else {
506                compaction_address = block.address + block.size;
507            }
508        }
509
510        // Coalesce free regions after compaction
511        layout.coalesce_free_regions();
512
513        let final_fragmentation = layout.calculate_fragmentation();
514        let fragmentation_reduction = initial_fragmentation - final_fragmentation;
515        let time_taken = start_time.elapsed();
516
517        // Update statistics
518        self.stats.executions += 1;
519        self.stats.total_bytes_moved += bytes_moved as u64;
520        self.stats.total_objects_relocated += objects_relocated as u64;
521        self.stats.total_time += time_taken;
522
523        let efficiency = if bytes_moved > 0 {
524            fragmentation_reduction / (bytes_moved as f64 / 1024.0 / 1024.0) // MB moved
525        } else {
526            0.0
527        };
528
529        self.stats.average_efficiency =
530            (self.stats.average_efficiency * (self.stats.executions - 1) as f64 + efficiency)
531                / self.stats.executions as f64;
532        self.stats.success_rate = 1.0; // All successful for now
533
534        Ok(CompactionResult {
535            bytes_moved,
536            objects_relocated,
537            fragmentation_reduction,
538            time_taken,
539            algorithm_used: CompactionAlgorithm::SlidingCompaction,
540            efficiency_score: efficiency,
541        })
542    }
543
544    fn get_statistics(&self) -> CompactionStats {
545        self.stats.clone()
546    }
547
548    fn reset(&mut self) {
549        self.stats = CompactionStats::default();
550    }
551}
552
553/// Two-pointer compaction strategy
554pub struct TwoPointerCompactionStrategy {
555    stats: CompactionStats,
556}
557
558impl Default for TwoPointerCompactionStrategy {
559    fn default() -> Self {
560        Self::new()
561    }
562}
563
564impl TwoPointerCompactionStrategy {
565    pub fn new() -> Self {
566        Self {
567            stats: CompactionStats::default(),
568        }
569    }
570}
571
572impl CompactionStrategy for TwoPointerCompactionStrategy {
573    fn name(&self) -> &str {
574        "TwoPointer"
575    }
576
577    fn can_handle(&self, layout: &MemoryLayoutTracker) -> bool {
578        layout.get_movable_blocks().len() >= 2 && layout.get_total_free_space() > 0
579    }
580
581    fn estimate_benefit(&self, layout: &MemoryLayoutTracker) -> f64 {
582        let movable_blocks = layout.get_movable_blocks();
583        let free_space = layout.get_total_free_space();
584
585        if movable_blocks.len() < 2 || free_space == 0 {
586            return 0.0;
587        }
588
589        // Estimate benefit based on gap reduction potential
590        let mut addresses: Vec<usize> = movable_blocks.iter().map(|b| b.address).collect();
591        addresses.sort();
592
593        let mut total_gaps = 0;
594        for i in 1..addresses.len() {
595            let gap = addresses[i] - addresses[i - 1];
596            if gap > movable_blocks[i - 1].size {
597                total_gaps += gap - movable_blocks[i - 1].size;
598            }
599        }
600
601        total_gaps as f64 / free_space as f64
602    }
603
604    fn execute(
605        &mut self,
606        layout: &mut MemoryLayoutTracker,
607    ) -> Result<CompactionResult, DefragError> {
608        let start_time = Instant::now();
609        let initial_fragmentation = layout.calculate_fragmentation();
610
611        let movable_blocks: Vec<AllocatedBlock> =
612            layout.get_movable_blocks().into_iter().cloned().collect();
613
614        if movable_blocks.len() < 2 {
615            return Err(DefragError::InsufficientBlocks);
616        }
617
618        let mut bytes_moved = 0;
619        let mut objects_relocated = 0;
620
621        // Sort blocks by address
622        let mut sorted_blocks = movable_blocks;
623        sorted_blocks.sort_by_key(|b| b.address);
624
625        let mut compact_addr = sorted_blocks[0].address;
626
627        // Two-pointer compaction
628        for block in &sorted_blocks {
629            if block.address != compact_addr {
630                // Move block to compact address
631                layout.remove_allocated_block(block.address);
632                layout.add_allocated_block(compact_addr, block.size, block.is_movable);
633
634                // Add freed space to free regions
635                layout.add_free_region(block.address, block.size);
636
637                bytes_moved += block.size;
638                objects_relocated += 1;
639            }
640
641            compact_addr += block.size;
642        }
643
644        // Coalesce free regions
645        layout.coalesce_free_regions();
646
647        let final_fragmentation = layout.calculate_fragmentation();
648        let fragmentation_reduction = initial_fragmentation - final_fragmentation;
649        let time_taken = start_time.elapsed();
650
651        // Update statistics
652        self.stats.executions += 1;
653        self.stats.total_bytes_moved += bytes_moved as u64;
654        self.stats.total_objects_relocated += objects_relocated as u64;
655        self.stats.total_time += time_taken;
656
657        let efficiency = if bytes_moved > 0 {
658            fragmentation_reduction / (bytes_moved as f64 / 1024.0 / 1024.0)
659        } else {
660            0.0
661        };
662
663        self.stats.average_efficiency =
664            (self.stats.average_efficiency * (self.stats.executions - 1) as f64 + efficiency)
665                / self.stats.executions as f64;
666
667        Ok(CompactionResult {
668            bytes_moved,
669            objects_relocated,
670            fragmentation_reduction,
671            time_taken,
672            algorithm_used: CompactionAlgorithm::TwoPointer,
673            efficiency_score: efficiency,
674        })
675    }
676
677    fn get_statistics(&self) -> CompactionStats {
678        self.stats.clone()
679    }
680
681    fn reset(&mut self) {
682        self.stats = CompactionStats::default();
683    }
684}
685
686impl DefragmentationEngine {
687    pub fn new(config: DefragConfig) -> Self {
688        let strategies: Vec<Box<dyn CompactionStrategy>> = vec![
689            Box::new(SlidingCompactionStrategy::new()),
690            Box::new(TwoPointerCompactionStrategy::new()),
691        ];
692
693        Self {
694            config,
695            stats: DefragStats::default(),
696            active_tasks: Vec::new(),
697            memory_layout: MemoryLayoutTracker::new(),
698            strategies: strategies
699                .into_iter()
700                .map(|s| s as Box<dyn CompactionStrategy>)
701                .collect(),
702            performance_history: VecDeque::with_capacity(1000),
703        }
704    }
705
706    /// Check if defragmentation should be triggered
707    pub fn should_defragment(&mut self) -> bool {
708        if !self.config.auto_defrag {
709            return false;
710        }
711
712        let current_fragmentation = self.memory_layout.calculate_fragmentation();
713        self.stats.current_fragmentation = current_fragmentation;
714
715        current_fragmentation > self.config.fragmentation_threshold
716            && self.memory_layout.get_total_free_space() >= self.config.min_free_space
717    }
718
719    /// Trigger defragmentation
720    pub fn defragment(&mut self) -> Result<CompactionResult, DefragError> {
721        let start_time = Instant::now();
722
723        if self
724            .active_tasks
725            .iter()
726            .any(|t| t.status == TaskStatus::Running)
727        {
728            return Err(DefragError::DefragmentationInProgress);
729        }
730
731        // Select best compaction strategy
732        let strategy_index = self.select_best_strategy()?;
733        let strategy = &mut self.strategies[strategy_index];
734
735        // Execute compaction
736        let result = strategy.execute(&mut self.memory_layout)?;
737
738        // Update statistics
739        self.stats.total_cycles += 1;
740        self.stats.total_bytes_moved += result.bytes_moved as u64;
741        self.stats.total_time_spent += result.time_taken;
742        self.stats.successful_cycles += 1;
743        self.stats.objects_relocated += result.objects_relocated as u64;
744        self.stats.average_fragmentation_reduction = (self.stats.average_fragmentation_reduction
745            * (self.stats.total_cycles - 1) as f64
746            + result.fragmentation_reduction)
747            / self.stats.total_cycles as f64;
748
749        let cycle_time = start_time.elapsed();
750        self.stats.average_cycle_time = Duration::from_nanos(
751            (self.stats.average_cycle_time.as_nanos() as u64 * (self.stats.total_cycles - 1)
752                + cycle_time.as_nanos() as u64)
753                / self.stats.total_cycles,
754        );
755
756        // Record performance
757        let performance = DefragPerformance {
758            timestamp: start_time,
759            fragmentation_before: self.stats.current_fragmentation,
760            fragmentation_after: self.memory_layout.calculate_fragmentation(),
761            time_taken: cycle_time,
762            bytes_moved: result.bytes_moved,
763            algorithm_used: result.algorithm_used.clone(),
764            success: true,
765        };
766
767        self.performance_history.push_back(performance);
768        if self.performance_history.len() > 1000 {
769            self.performance_history.pop_front();
770        }
771
772        Ok(result)
773    }
774
775    /// Select the best compaction strategy based on current conditions
776    fn select_best_strategy(&mut self) -> Result<usize, DefragError> {
777        let mut best_index = 0;
778        let mut best_benefit = 0.0;
779
780        for (i, strategy) in self.strategies.iter().enumerate() {
781            if strategy.can_handle(&self.memory_layout) {
782                let benefit = strategy.estimate_benefit(&self.memory_layout);
783                if benefit > best_benefit {
784                    best_benefit = benefit;
785                    best_index = i;
786                }
787            }
788        }
789
790        if best_benefit == 0.0 {
791            return Err(DefragError::NoSuitableStrategy);
792        }
793
794        Ok(best_index)
795    }
796
797    /// Create a defragmentation task
798    pub fn create_task(
799        &mut self,
800        start_addr: usize,
801        size: usize,
802        algorithm: CompactionAlgorithm,
803        priority: TaskPriority,
804    ) -> u64 {
805        let task_id = self.active_tasks.len() as u64;
806        let task = DefragTask {
807            id: task_id,
808            start_addr,
809            size,
810            algorithm,
811            status: TaskStatus::Pending,
812            created_at: Instant::now(),
813            estimated_completion: None,
814            priority,
815        };
816
817        self.active_tasks.push(task);
818        task_id
819    }
820
821    /// Get current statistics
822    pub fn get_stats(&self) -> &DefragStats {
823        &self.stats
824    }
825
826    /// Get performance history
827    pub fn get_performance_history(&self) -> &VecDeque<DefragPerformance> {
828        &self.performance_history
829    }
830
831    /// Update memory layout
832    pub fn update_layout(
833        &mut self,
834        allocated_blocks: HashMap<usize, AllocatedBlock>,
835        free_regions: BTreeMap<usize, FreeRegion>,
836    ) {
837        self.memory_layout.allocated_blocks = allocated_blocks;
838        self.memory_layout.free_regions = free_regions;
839        self.memory_layout.invalidate_cache();
840    }
841
842    /// Get current memory layout
843    pub fn get_layout(&self) -> &MemoryLayoutTracker {
844        &self.memory_layout
845    }
846
847    /// Reset defragmentation engine
848    pub fn reset(&mut self) {
849        self.stats = DefragStats::default();
850        self.active_tasks.clear();
851        self.memory_layout = MemoryLayoutTracker::new();
852        self.performance_history.clear();
853
854        // Reset strategy statistics
855        for strategy in &mut self.strategies {
856            strategy.reset();
857        }
858    }
859}
860
861// Safety: DefragmentationEngine manages memory defragmentation state and compaction strategies.
862// While it contains NonNull pointers via MemoryLayoutTracker and trait objects,
863// it's safe to share across threads when protected by Arc<Mutex<>> because:
864// 1. All pointer operations are protected by the Mutex providing exclusive access
865// 2. CompactionStrategy trait requires Send + Sync
866// 3. No thread-local state is maintained
867unsafe impl Send for DefragmentationEngine {}
868unsafe impl Sync for DefragmentationEngine {}
869
870/// Defragmentation errors
871#[derive(Debug, Clone)]
872pub enum DefragError {
873    DefragmentationInProgress,
874    NoMovableBlocks,
875    InsufficientBlocks,
876    NoSuitableStrategy,
877    MemoryLayoutCorrupted,
878    TimeoutExceeded,
879    InternalError(String),
880}
881
882impl std::fmt::Display for DefragError {
883    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
884        match self {
885            DefragError::DefragmentationInProgress => {
886                write!(f, "Defragmentation already in progress")
887            }
888            DefragError::NoMovableBlocks => write!(f, "No movable blocks available for compaction"),
889            DefragError::InsufficientBlocks => {
890                write!(f, "Insufficient blocks for compaction strategy")
891            }
892            DefragError::NoSuitableStrategy => {
893                write!(f, "No suitable compaction strategy available")
894            }
895            DefragError::MemoryLayoutCorrupted => write!(f, "Memory layout is corrupted"),
896            DefragError::TimeoutExceeded => write!(f, "Defragmentation timeout exceeded"),
897            DefragError::InternalError(msg) => write!(f, "Internal error: {}", msg),
898        }
899    }
900}
901
902impl std::error::Error for DefragError {}
903
904/// Thread-safe defragmentation engine wrapper
905pub struct ThreadSafeDefragmentationEngine {
906    engine: Arc<Mutex<DefragmentationEngine>>,
907}
908
909impl ThreadSafeDefragmentationEngine {
910    pub fn new(config: DefragConfig) -> Self {
911        Self {
912            engine: Arc::new(Mutex::new(DefragmentationEngine::new(config))),
913        }
914    }
915
916    pub fn should_defragment(&self) -> bool {
917        let mut engine = self.engine.lock().unwrap_or_else(|e| e.into_inner());
918        engine.should_defragment()
919    }
920
921    pub fn defragment(&self) -> Result<CompactionResult, DefragError> {
922        let mut engine = self.engine.lock().unwrap_or_else(|e| e.into_inner());
923        engine.defragment()
924    }
925
926    pub fn get_stats(&self) -> DefragStats {
927        let engine = self.engine.lock().unwrap_or_else(|e| e.into_inner());
928        engine.get_stats().clone()
929    }
930
931    pub fn get_performance_history(&self) -> Vec<DefragPerformance> {
932        let engine = self.engine.lock().unwrap_or_else(|e| e.into_inner());
933        engine.get_performance_history().iter().cloned().collect()
934    }
935}
936
937#[cfg(test)]
938mod tests {
939    use super::*;
940
941    #[test]
942    fn test_memory_layout_tracker() {
943        let mut tracker = MemoryLayoutTracker::new();
944
945        // Add some allocated blocks and free regions
946        tracker.add_allocated_block(1000, 500, true);
947        tracker.add_allocated_block(2000, 300, false);
948        tracker.add_free_region(1500, 200);
949        tracker.add_free_region(2500, 800);
950
951        let fragmentation = tracker.calculate_fragmentation();
952        assert!((0.0..=1.0).contains(&fragmentation));
953
954        let total_free = tracker.get_total_free_space();
955        assert_eq!(total_free, 1000);
956
957        let largest_free = tracker.get_largest_free_block();
958        assert_eq!(largest_free, 800);
959    }
960
961    #[test]
962    fn test_sliding_compaction_strategy() {
963        let mut strategy = SlidingCompactionStrategy::new();
964        let mut layout = MemoryLayoutTracker::new();
965
966        // Set up a fragmented layout
967        layout.add_allocated_block(1000, 500, true);
968        layout.add_free_region(1500, 200);
969        layout.add_allocated_block(2000, 300, true);
970        layout.add_free_region(2300, 500);
971
972        assert!(strategy.can_handle(&layout));
973
974        let benefit = strategy.estimate_benefit(&layout);
975        assert!(benefit > 0.0);
976
977        let result = strategy.execute(&mut layout);
978        assert!(result.is_ok());
979
980        let compaction_result = result.expect("unwrap failed");
981        assert!(compaction_result.bytes_moved > 0);
982        assert!(compaction_result.objects_relocated > 0);
983
984        // `execute` above must have accumulated real statistics ...
985        assert!(strategy.get_statistics().executions > 0);
986        // ... and `reset` must genuinely clear them, not just be a no-op
987        // documented as "would need to add reset method to the trait".
988        strategy.reset();
989        assert_eq!(strategy.get_statistics().executions, 0);
990        assert_eq!(strategy.get_statistics().total_bytes_moved, 0);
991    }
992
993    #[test]
994    fn test_two_pointer_compaction_strategy_reset_clears_statistics() {
995        let mut strategy = TwoPointerCompactionStrategy::new();
996        let mut layout = MemoryLayoutTracker::new();
997        layout.add_allocated_block(1000, 500, true);
998        layout.add_allocated_block(2000, 300, true);
999        layout.add_free_region(1500, 200);
1000
1001        strategy
1002            .execute(&mut layout)
1003            .expect("two-pointer compaction should succeed with movable blocks");
1004        assert!(strategy.get_statistics().executions > 0);
1005
1006        strategy.reset();
1007        assert_eq!(strategy.get_statistics().executions, 0);
1008        assert_eq!(strategy.get_statistics().total_bytes_moved, 0);
1009    }
1010
1011    #[test]
1012    fn test_defragmentation_engine_reset_clears_strategy_statistics() {
1013        let config = DefragConfig::default();
1014        let mut engine = DefragmentationEngine::new(config);
1015
1016        let mut allocated_blocks = HashMap::new();
1017        allocated_blocks.insert(
1018            1000,
1019            AllocatedBlock {
1020                address: 1000,
1021                size: 500,
1022                allocation_time: Instant::now(),
1023                last_access: None,
1024                access_count: 0,
1025                is_movable: true,
1026                reference_count: 1,
1027            },
1028        );
1029        allocated_blocks.insert(
1030            2000,
1031            AllocatedBlock {
1032                address: 2000,
1033                size: 300,
1034                allocation_time: Instant::now(),
1035                last_access: None,
1036                access_count: 0,
1037                is_movable: true,
1038                reference_count: 1,
1039            },
1040        );
1041        let mut free_regions = BTreeMap::new();
1042        free_regions.insert(
1043            1500,
1044            FreeRegion {
1045                address: 1500,
1046                size: 300,
1047                age: Duration::from_secs(10),
1048                access_frequency: 0,
1049                adjacent_to_allocated: true,
1050            },
1051        );
1052        engine.update_layout(allocated_blocks, free_regions);
1053        engine
1054            .defragment()
1055            .expect("defragmentation should find a suitable strategy for this layout");
1056        assert!(
1057            engine
1058                .strategies
1059                .iter()
1060                .any(|s| s.get_statistics().executions > 0),
1061            "defragment() should have driven at least one strategy's statistics"
1062        );
1063
1064        engine.reset();
1065        assert!(
1066            engine
1067                .strategies
1068                .iter()
1069                .all(|s| s.get_statistics().executions == 0),
1070            "reset() must clear every strategy's accumulated statistics"
1071        );
1072    }
1073
1074    #[test]
1075    fn test_defragmentation_engine() {
1076        let config = DefragConfig::default();
1077        let mut engine = DefragmentationEngine::new(config);
1078
1079        // Set up memory layout
1080        let mut allocated_blocks = HashMap::new();
1081        allocated_blocks.insert(
1082            1000,
1083            AllocatedBlock {
1084                address: 1000,
1085                size: 500,
1086                allocation_time: Instant::now(),
1087                last_access: None,
1088                access_count: 0,
1089                is_movable: true,
1090                reference_count: 1,
1091            },
1092        );
1093
1094        let mut free_regions = BTreeMap::new();
1095        free_regions.insert(
1096            1500,
1097            FreeRegion {
1098                address: 1500,
1099                size: 300,
1100                age: Duration::from_secs(10),
1101                access_frequency: 0,
1102                adjacent_to_allocated: true,
1103            },
1104        );
1105
1106        engine.update_layout(allocated_blocks, free_regions);
1107
1108        // Test defragmentation trigger: a single free region has nothing to
1109        // be fragmented relative to (fragmentation = 1 - largest/total = 0),
1110        // which is below the default 0.3 threshold either way.
1111        let should_defrag = engine.should_defragment();
1112        assert!(!should_defrag);
1113
1114        let stats = engine.get_stats();
1115        assert_eq!(stats.total_cycles, 0); // No cycles yet
1116    }
1117
1118    #[test]
1119    fn test_coalescing() {
1120        let mut tracker = MemoryLayoutTracker::new();
1121
1122        // Add adjacent free regions
1123        tracker.add_free_region(1000, 500);
1124        tracker.add_free_region(1500, 300);
1125        tracker.add_free_region(2000, 200); // Non-adjacent
1126
1127        let coalesced = tracker.coalesce_free_regions();
1128        assert_eq!(coalesced, 1); // One coalescing operation
1129
1130        // Should now have two regions: one large (800 bytes) and one separate (200 bytes)
1131        assert_eq!(tracker.free_regions.len(), 2);
1132    }
1133
1134    #[test]
1135    fn test_thread_safe_engine() {
1136        let config = DefragConfig::default();
1137        let engine = ThreadSafeDefragmentationEngine::new(config);
1138
1139        let should_defrag = engine.should_defragment();
1140        assert!(!should_defrag, "should not trigger defrag on empty layout");
1141
1142        let stats = engine.get_stats();
1143        assert_eq!(stats.total_cycles, 0);
1144    }
1145}