Skip to main content

optirs_gpu/memory/management/
eviction_policies.rs

1// Memory eviction policies for GPU memory management
2//
3// This module provides sophisticated eviction strategies to manage limited
4// GPU memory efficiently by determining which data should be removed when
5// memory pressure occurs.
6
7use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
8use std::sync::{Arc, RwLock};
9use std::time::{Duration, Instant};
10
11/// Main eviction engine that manages multiple eviction policies
12pub struct EvictionEngine {
13    /// Configuration
14    config: EvictionConfig,
15    /// Statistics
16    stats: EvictionStats,
17    /// Available eviction policies
18    policies: HashMap<String, Box<dyn EvictionPolicy>>,
19    /// Currently active policy
20    active_policy: String,
21    /// Memory regions under management
22    memory_regions: HashMap<usize, MemoryRegion>,
23    /// Performance monitor
24    performance_monitor: EvictionPerformanceMonitor,
25    /// Policy selection history
26    policy_history: VecDeque<PolicySelection>,
27}
28
29/// Eviction configuration
30#[derive(Debug, Clone)]
31pub struct EvictionConfig {
32    /// Enable automatic eviction
33    pub auto_eviction: bool,
34    /// Memory pressure threshold to trigger eviction
35    pub pressure_threshold: f64,
36    /// Enable adaptive policy selection
37    pub enable_adaptive: bool,
38    /// Enable performance monitoring
39    pub enable_monitoring: bool,
40    /// Default eviction policy
41    pub default_policy: String,
42    /// Policy switching threshold
43    pub policy_switch_threshold: f64,
44    /// Minimum eviction batch size
45    pub min_batch_size: usize,
46    /// Maximum eviction batch size
47    pub max_batch_size: usize,
48    /// Enable workload-aware eviction
49    pub workload_aware: bool,
50    /// GPU kernel context consideration
51    pub kernel_context_weight: f64,
52}
53
54impl Default for EvictionConfig {
55    fn default() -> Self {
56        Self {
57            auto_eviction: true,
58            pressure_threshold: 0.85,
59            enable_adaptive: true,
60            enable_monitoring: true,
61            default_policy: "LRU".to_string(),
62            policy_switch_threshold: 0.1,
63            min_batch_size: 1,
64            max_batch_size: 64,
65            workload_aware: true,
66            kernel_context_weight: 0.3,
67        }
68    }
69}
70
71/// Eviction statistics
72#[derive(Debug, Clone, Default)]
73pub struct EvictionStats {
74    /// Total evictions performed
75    pub total_evictions: u64,
76    /// Total bytes evicted
77    pub total_bytes_evicted: u64,
78    /// Total objects evicted
79    pub total_objects_evicted: u64,
80    /// Average eviction time
81    pub average_eviction_time: Duration,
82    /// Eviction accuracy (correctly evicted items)
83    pub eviction_accuracy: f64,
84    /// Policy performance scores
85    pub policy_scores: HashMap<String, f64>,
86    /// Memory pressure events
87    pub pressure_events: u64,
88    /// Adaptive policy switches
89    pub policy_switches: u64,
90}
91
92/// Memory region for eviction management
93#[derive(Debug, Clone)]
94pub struct MemoryRegion {
95    /// Base address
96    pub base_addr: usize,
97    /// Region size
98    pub size: usize,
99    /// Cached objects in this region
100    pub objects: HashMap<usize, CacheObject>,
101    /// Region type (cache, buffer, etc.)
102    pub region_type: RegionType,
103    /// Current memory pressure
104    pub pressure: f64,
105    /// Last eviction time
106    pub last_eviction: Option<Instant>,
107}
108
109/// Types of memory regions
110#[derive(Debug, Clone, PartialEq)]
111pub enum RegionType {
112    Cache,
113    Buffer,
114    Texture,
115    Constant,
116    Shared,
117    Global,
118}
119
120/// Cached object representation
121#[derive(Debug, Clone)]
122pub struct CacheObject {
123    /// Object address
124    pub address: usize,
125    /// Object size
126    pub size: usize,
127    /// Creation time
128    pub created_at: Instant,
129    /// Last access time
130    pub last_access: Instant,
131    /// Access count
132    pub access_count: u32,
133    /// Access frequency (accesses per second)
134    pub access_frequency: f64,
135    /// Object priority
136    pub priority: ObjectPriority,
137    /// GPU kernel context
138    pub kernel_context: Option<u32>,
139    /// Object type
140    pub object_type: ObjectType,
141    /// Eviction cost (higher = more expensive to evict)
142    pub eviction_cost: f64,
143    /// Replacement cost (higher = more expensive to reload)
144    pub replacement_cost: f64,
145}
146
147/// Object priority levels
148#[derive(Debug, Clone, PartialEq, Ord, PartialOrd, Eq)]
149pub enum ObjectPriority {
150    Low,
151    Normal,
152    High,
153    Critical,
154}
155
156/// Object type classification
157#[derive(Debug, Clone, PartialEq, Eq, Hash)]
158pub enum ObjectType {
159    Data,
160    Texture,
161    Constant,
162    Instruction,
163    Temporary,
164    Persistent,
165    Critical,
166}
167
168impl CacheObject {
169    /// Update access information
170    pub fn update_access(&mut self) {
171        self.access_count += 1;
172        let now = Instant::now();
173        let time_since_creation = now.duration_since(self.created_at).as_secs_f64();
174
175        if time_since_creation > 0.0 {
176            self.access_frequency = self.access_count as f64 / time_since_creation;
177        }
178
179        self.last_access = now;
180    }
181
182    /// Calculate object utility score for eviction decisions
183    pub fn calculate_utility(&self) -> f64 {
184        let age_factor = self.last_access.elapsed().as_secs_f64();
185        let frequency_factor = self.access_frequency;
186        let priority_factor = match self.priority {
187            ObjectPriority::Critical => 10.0,
188            ObjectPriority::High => 5.0,
189            ObjectPriority::Normal => 1.0,
190            ObjectPriority::Low => 0.5,
191        };
192
193        let size_factor = 1.0 / (self.size as f64).sqrt();
194
195        // Higher utility = less likely to be evicted
196        (frequency_factor * priority_factor * size_factor) / (age_factor + 1.0)
197    }
198}
199
200/// Eviction policy trait
201pub trait EvictionPolicy: Send + Sync {
202    fn name(&self) -> &str;
203    fn select_victims(&mut self, region: &MemoryRegion, target_bytes: usize) -> Vec<usize>;
204    fn update_access(&mut self, address: usize, object: &CacheObject);
205    fn add_object(&mut self, address: usize, object: &CacheObject);
206    fn remove_object(&mut self, address: usize);
207    fn get_statistics(&self) -> PolicyStats;
208    fn configure(&mut self, config: &EvictionConfig);
209    fn reset(&mut self);
210}
211
212/// Policy statistics
213#[derive(Debug, Clone, Default)]
214pub struct PolicyStats {
215    pub evictions: u64,
216    pub bytes_evicted: u64,
217    pub average_latency: Duration,
218    pub accuracy_score: f64,
219    pub hit_rate: f64,
220}
221
222/// LRU (Least Recently Used) eviction policy
223pub struct LRUPolicy {
224    /// LRU order tracking
225    lru_order: VecDeque<usize>,
226    /// Address to position mapping
227    address_map: HashMap<usize, usize>,
228    /// Statistics
229    stats: PolicyStats,
230}
231
232impl Default for LRUPolicy {
233    fn default() -> Self {
234        Self::new()
235    }
236}
237
238impl LRUPolicy {
239    pub fn new() -> Self {
240        Self {
241            lru_order: VecDeque::new(),
242            address_map: HashMap::new(),
243            stats: PolicyStats::default(),
244        }
245    }
246
247    fn move_to_end(&mut self, address: usize) {
248        if let Some(&pos) = self.address_map.get(&address) {
249            if pos < self.lru_order.len() {
250                self.lru_order.remove(pos);
251                self.lru_order.push_back(address);
252                self.update_positions();
253            }
254        }
255    }
256
257    fn update_positions(&mut self) {
258        self.address_map.clear();
259        for (pos, &addr) in self.lru_order.iter().enumerate() {
260            self.address_map.insert(addr, pos);
261        }
262    }
263}
264
265impl EvictionPolicy for LRUPolicy {
266    fn name(&self) -> &str {
267        "LRU"
268    }
269
270    fn select_victims(&mut self, region: &MemoryRegion, target_bytes: usize) -> Vec<usize> {
271        let mut victims = Vec::new();
272        let mut bytes_selected = 0;
273
274        // Start from least recently used
275        for &address in &self.lru_order {
276            if let Some(object) = region.objects.get(&address) {
277                victims.push(address);
278                bytes_selected += object.size;
279
280                if bytes_selected >= target_bytes {
281                    break;
282                }
283            }
284        }
285
286        self.stats.evictions += victims.len() as u64;
287        self.stats.bytes_evicted += bytes_selected as u64;
288
289        victims
290    }
291
292    fn update_access(&mut self, address: usize, _object: &CacheObject) {
293        self.move_to_end(address);
294    }
295
296    fn add_object(&mut self, address: usize, _object: &CacheObject) {
297        if !self.address_map.contains_key(&address) {
298            self.lru_order.push_back(address);
299            self.address_map.insert(address, self.lru_order.len() - 1);
300        }
301    }
302
303    fn remove_object(&mut self, address: usize) {
304        if let Some(&pos) = self.address_map.get(&address) {
305            if pos < self.lru_order.len() {
306                self.lru_order.remove(pos);
307                self.address_map.remove(&address);
308                self.update_positions();
309            }
310        }
311    }
312
313    fn get_statistics(&self) -> PolicyStats {
314        self.stats.clone()
315    }
316
317    fn configure(&mut self, _config: &EvictionConfig) {
318        // LRU typically doesn't need configuration
319    }
320
321    fn reset(&mut self) {
322        self.lru_order.clear();
323        self.address_map.clear();
324        self.stats = PolicyStats::default();
325    }
326}
327
328/// LFU (Least Frequently Used) eviction policy
329pub struct LFUPolicy {
330    /// Frequency tracking
331    frequency_map: HashMap<usize, u32>,
332    /// Frequency buckets for efficient selection
333    frequency_buckets: BTreeMap<u32, HashSet<usize>>,
334    /// Statistics
335    stats: PolicyStats,
336}
337
338impl Default for LFUPolicy {
339    fn default() -> Self {
340        Self::new()
341    }
342}
343
344impl LFUPolicy {
345    pub fn new() -> Self {
346        Self {
347            frequency_map: HashMap::new(),
348            frequency_buckets: BTreeMap::new(),
349            stats: PolicyStats::default(),
350        }
351    }
352
353    fn update_frequency(&mut self, address: usize) {
354        let old_freq = self.frequency_map.get(&address).copied().unwrap_or(0);
355        let new_freq = old_freq + 1;
356
357        // Remove from old bucket
358        if old_freq > 0 {
359            if let Some(bucket) = self.frequency_buckets.get_mut(&old_freq) {
360                bucket.remove(&address);
361                if bucket.is_empty() {
362                    self.frequency_buckets.remove(&old_freq);
363                }
364            }
365        }
366
367        // Add to new bucket
368        self.frequency_buckets
369            .entry(new_freq)
370            .or_default()
371            .insert(address);
372        self.frequency_map.insert(address, new_freq);
373    }
374}
375
376impl EvictionPolicy for LFUPolicy {
377    fn name(&self) -> &str {
378        "LFU"
379    }
380
381    fn select_victims(&mut self, region: &MemoryRegion, target_bytes: usize) -> Vec<usize> {
382        let mut victims = Vec::new();
383        let mut bytes_selected = 0;
384
385        // Select from lowest frequency buckets first
386        for addresses in self.frequency_buckets.values() {
387            for &address in addresses {
388                if let Some(object) = region.objects.get(&address) {
389                    victims.push(address);
390                    bytes_selected += object.size;
391
392                    if bytes_selected >= target_bytes {
393                        break;
394                    }
395                }
396            }
397
398            if bytes_selected >= target_bytes {
399                break;
400            }
401        }
402
403        self.stats.evictions += victims.len() as u64;
404        self.stats.bytes_evicted += bytes_selected as u64;
405
406        victims
407    }
408
409    fn update_access(&mut self, address: usize, _object: &CacheObject) {
410        self.update_frequency(address);
411    }
412
413    fn add_object(&mut self, address: usize, _object: &CacheObject) {
414        self.update_frequency(address);
415    }
416
417    fn remove_object(&mut self, address: usize) {
418        if let Some(freq) = self.frequency_map.remove(&address) {
419            if let Some(bucket) = self.frequency_buckets.get_mut(&freq) {
420                bucket.remove(&address);
421                if bucket.is_empty() {
422                    self.frequency_buckets.remove(&freq);
423                }
424            }
425        }
426    }
427
428    fn get_statistics(&self) -> PolicyStats {
429        self.stats.clone()
430    }
431
432    fn configure(&mut self, _config: &EvictionConfig) {
433        // LFU typically doesn't need configuration
434    }
435
436    fn reset(&mut self) {
437        self.frequency_map.clear();
438        self.frequency_buckets.clear();
439        self.stats = PolicyStats::default();
440    }
441}
442
443/// FIFO (First In, First Out) eviction policy
444pub struct FIFOPolicy {
445    /// Insertion order tracking
446    insertion_order: VecDeque<usize>,
447    /// Statistics
448    stats: PolicyStats,
449}
450
451impl Default for FIFOPolicy {
452    fn default() -> Self {
453        Self::new()
454    }
455}
456
457impl FIFOPolicy {
458    pub fn new() -> Self {
459        Self {
460            insertion_order: VecDeque::new(),
461            stats: PolicyStats::default(),
462        }
463    }
464}
465
466impl EvictionPolicy for FIFOPolicy {
467    fn name(&self) -> &str {
468        "FIFO"
469    }
470
471    fn select_victims(&mut self, region: &MemoryRegion, target_bytes: usize) -> Vec<usize> {
472        let mut victims = Vec::new();
473        let mut bytes_selected = 0;
474
475        // Select oldest insertions first
476        for &address in &self.insertion_order {
477            if let Some(object) = region.objects.get(&address) {
478                victims.push(address);
479                bytes_selected += object.size;
480
481                if bytes_selected >= target_bytes {
482                    break;
483                }
484            }
485        }
486
487        self.stats.evictions += victims.len() as u64;
488        self.stats.bytes_evicted += bytes_selected as u64;
489
490        victims
491    }
492
493    fn update_access(&mut self, _address: usize, _object: &CacheObject) {
494        // FIFO doesn't consider access patterns
495    }
496
497    fn add_object(&mut self, address: usize, _object: &CacheObject) {
498        self.insertion_order.push_back(address);
499    }
500
501    fn remove_object(&mut self, address: usize) {
502        if let Some(pos) = self
503            .insertion_order
504            .iter()
505            .position(|&addr| addr == address)
506        {
507            self.insertion_order.remove(pos);
508        }
509    }
510
511    fn get_statistics(&self) -> PolicyStats {
512        self.stats.clone()
513    }
514
515    fn configure(&mut self, _config: &EvictionConfig) {
516        // FIFO typically doesn't need configuration
517    }
518
519    fn reset(&mut self) {
520        self.insertion_order.clear();
521        self.stats = PolicyStats::default();
522    }
523}
524
525/// Clock (Second Chance) eviction policy
526pub struct ClockPolicy {
527    /// Circular list of objects
528    clock_list: Vec<ClockEntry>,
529    /// Address to index mapping
530    address_map: HashMap<usize, usize>,
531    /// Clock hand position
532    clock_hand: usize,
533    /// Statistics
534    stats: PolicyStats,
535}
536
537/// Clock entry
538#[derive(Debug, Clone)]
539struct ClockEntry {
540    address: usize,
541    reference_bit: bool,
542}
543
544impl Default for ClockPolicy {
545    fn default() -> Self {
546        Self::new()
547    }
548}
549
550impl ClockPolicy {
551    pub fn new() -> Self {
552        Self {
553            clock_list: Vec::new(),
554            address_map: HashMap::new(),
555            clock_hand: 0,
556            stats: PolicyStats::default(),
557        }
558    }
559
560    fn advance_clock(&mut self) -> Option<usize> {
561        if self.clock_list.is_empty() {
562            return None;
563        }
564
565        let start_pos = self.clock_hand;
566
567        loop {
568            let entry = &mut self.clock_list[self.clock_hand];
569
570            if entry.reference_bit {
571                // Give second chance
572                entry.reference_bit = false;
573            } else {
574                // Victim found
575                let victim = entry.address;
576                self.clock_hand = (self.clock_hand + 1) % self.clock_list.len();
577                return Some(victim);
578            }
579
580            self.clock_hand = (self.clock_hand + 1) % self.clock_list.len();
581
582            if self.clock_hand == start_pos {
583                // Full cycle completed, all had reference bits set
584                break;
585            }
586        }
587
588        // If all had reference bits, just return first one
589        if !self.clock_list.is_empty() {
590            Some(self.clock_list[0].address)
591        } else {
592            None
593        }
594    }
595}
596
597impl EvictionPolicy for ClockPolicy {
598    fn name(&self) -> &str {
599        "Clock"
600    }
601
602    fn select_victims(&mut self, region: &MemoryRegion, target_bytes: usize) -> Vec<usize> {
603        let mut victims = Vec::new();
604        let mut bytes_selected = 0;
605
606        while bytes_selected < target_bytes {
607            if let Some(victim_addr) = self.advance_clock() {
608                if let Some(object) = region.objects.get(&victim_addr) {
609                    victims.push(victim_addr);
610                    bytes_selected += object.size;
611                }
612            } else {
613                break;
614            }
615        }
616
617        self.stats.evictions += victims.len() as u64;
618        self.stats.bytes_evicted += bytes_selected as u64;
619
620        victims
621    }
622
623    fn update_access(&mut self, address: usize, _object: &CacheObject) {
624        if let Some(&index) = self.address_map.get(&address) {
625            if index < self.clock_list.len() {
626                self.clock_list[index].reference_bit = true;
627            }
628        }
629    }
630
631    fn add_object(&mut self, address: usize, _object: &CacheObject) {
632        let entry = ClockEntry {
633            address,
634            reference_bit: true,
635        };
636
637        self.clock_list.push(entry);
638        self.address_map.insert(address, self.clock_list.len() - 1);
639    }
640
641    fn remove_object(&mut self, address: usize) {
642        if let Some(&index) = self.address_map.get(&address) {
643            if index < self.clock_list.len() {
644                self.clock_list.remove(index);
645                self.address_map.remove(&address);
646
647                // Update all subsequent indices
648                for i in index..self.clock_list.len() {
649                    let addr = self.clock_list[i].address;
650                    self.address_map.insert(addr, i);
651                }
652
653                // Adjust clock hand
654                if self.clock_hand > index {
655                    self.clock_hand -= 1;
656                } else if self.clock_hand >= self.clock_list.len() && !self.clock_list.is_empty() {
657                    self.clock_hand = 0;
658                }
659            }
660        }
661    }
662
663    fn get_statistics(&self) -> PolicyStats {
664        self.stats.clone()
665    }
666
667    fn configure(&mut self, _config: &EvictionConfig) {
668        // Clock typically doesn't need configuration
669    }
670
671    fn reset(&mut self) {
672        self.clock_list.clear();
673        self.address_map.clear();
674        self.clock_hand = 0;
675        self.stats = PolicyStats::default();
676    }
677}
678
679/// Adaptive Replacement Cache (ARC) policy
680pub struct ARCPolicy {
681    /// T1: Recent cache misses
682    t1: VecDeque<usize>,
683    /// T2: Recent cache hits
684    t2: VecDeque<usize>,
685    /// B1: Ghost entries for T1
686    b1: VecDeque<usize>,
687    /// B2: Ghost entries for T2
688    b2: VecDeque<usize>,
689    /// Adaptation parameter
690    p: usize,
691    /// Cache capacity
692    capacity: usize,
693    /// Statistics
694    stats: PolicyStats,
695}
696
697impl ARCPolicy {
698    pub fn new(capacity: usize) -> Self {
699        Self {
700            t1: VecDeque::new(),
701            t2: VecDeque::new(),
702            b1: VecDeque::new(),
703            b2: VecDeque::new(),
704            p: 0,
705            capacity,
706            stats: PolicyStats::default(),
707        }
708    }
709
710    fn replace(&mut self, address: usize) -> Option<usize> {
711        let t1_len = self.t1.len();
712
713        if t1_len > 0 && (t1_len > self.p || (self.b2.contains(&address) && t1_len == self.p)) {
714            // Remove from T1
715            if let Some(victim) = self.t1.pop_front() {
716                self.b1.push_back(victim);
717                if self.b1.len() > self.capacity {
718                    self.b1.pop_front();
719                }
720                return Some(victim);
721            }
722        } else {
723            // Remove from T2
724            if let Some(victim) = self.t2.pop_front() {
725                self.b2.push_back(victim);
726                if self.b2.len() > self.capacity {
727                    self.b2.pop_front();
728                }
729                return Some(victim);
730            }
731        }
732
733        None
734    }
735
736    fn adapt(&mut self, address: usize) {
737        let delta = if self.b1.len() >= self.b2.len() {
738            1
739        } else {
740            self.b2.len() / self.b1.len().max(1)
741        };
742
743        if self.b1.contains(&address) {
744            self.p = (self.p + delta).min(self.capacity);
745        } else if self.b2.contains(&address) {
746            self.p = self.p.saturating_sub(delta);
747        }
748    }
749}
750
751impl EvictionPolicy for ARCPolicy {
752    fn name(&self) -> &str {
753        "ARC"
754    }
755
756    fn select_victims(&mut self, region: &MemoryRegion, target_bytes: usize) -> Vec<usize> {
757        let mut victims = Vec::new();
758        let mut bytes_selected = 0;
759
760        while bytes_selected < target_bytes {
761            if let Some(victim_addr) = self.replace(0) {
762                // Simplified
763                if let Some(object) = region.objects.get(&victim_addr) {
764                    victims.push(victim_addr);
765                    bytes_selected += object.size;
766                }
767            } else {
768                break;
769            }
770        }
771
772        self.stats.evictions += victims.len() as u64;
773        self.stats.bytes_evicted += bytes_selected as u64;
774
775        victims
776    }
777
778    fn update_access(&mut self, address: usize, _object: &CacheObject) {
779        // Simplified ARC access handling
780        if self.t1.contains(&address) {
781            // Move from T1 to T2
782            if let Some(pos) = self.t1.iter().position(|&addr| addr == address) {
783                self.t1.remove(pos);
784                self.t2.push_back(address);
785            }
786        } else if self.t2.contains(&address) {
787            // Move to end of T2
788            if let Some(pos) = self.t2.iter().position(|&addr| addr == address) {
789                self.t2.remove(pos);
790                self.t2.push_back(address);
791            }
792        }
793    }
794
795    fn add_object(&mut self, address: usize, _object: &CacheObject) {
796        if self.b1.contains(&address) {
797            self.adapt(address);
798            self.b1.retain(|&addr| addr != address);
799            self.t2.push_back(address);
800        } else if self.b2.contains(&address) {
801            self.adapt(address);
802            self.b2.retain(|&addr| addr != address);
803            self.t2.push_back(address);
804        } else {
805            self.t1.push_back(address);
806        }
807    }
808
809    fn remove_object(&mut self, address: usize) {
810        self.t1.retain(|&addr| addr != address);
811        self.t2.retain(|&addr| addr != address);
812        self.b1.retain(|&addr| addr != address);
813        self.b2.retain(|&addr| addr != address);
814    }
815
816    fn get_statistics(&self) -> PolicyStats {
817        self.stats.clone()
818    }
819
820    fn configure(&mut self, _config: &EvictionConfig) {
821        // ARC adapts automatically
822    }
823
824    fn reset(&mut self) {
825        self.t1.clear();
826        self.t2.clear();
827        self.b1.clear();
828        self.b2.clear();
829        self.p = 0;
830        self.stats = PolicyStats::default();
831    }
832}
833
834/// Workload-aware eviction policy
835pub struct WorkloadAwarePolicy {
836    /// Base policy to extend
837    base_policy: Box<dyn EvictionPolicy>,
838    /// Kernel context weights
839    kernel_weights: HashMap<u32, f64>,
840    /// Object type priorities
841    type_priorities: HashMap<ObjectType, f64>,
842    /// Statistics
843    stats: PolicyStats,
844}
845
846impl WorkloadAwarePolicy {
847    pub fn new(base_policy: Box<dyn EvictionPolicy>) -> Self {
848        let mut type_priorities = HashMap::new();
849        type_priorities.insert(ObjectType::Critical, 10.0);
850        type_priorities.insert(ObjectType::Persistent, 5.0);
851        type_priorities.insert(ObjectType::Data, 2.0);
852        type_priorities.insert(ObjectType::Texture, 1.5);
853        type_priorities.insert(ObjectType::Constant, 1.0);
854        type_priorities.insert(ObjectType::Temporary, 0.5);
855
856        Self {
857            base_policy,
858            kernel_weights: HashMap::new(),
859            type_priorities,
860            stats: PolicyStats::default(),
861        }
862    }
863
864    fn calculate_eviction_priority(&self, object: &CacheObject) -> f64 {
865        let mut priority = object.calculate_utility();
866
867        // Apply object type priority
868        if let Some(&type_priority) = self.type_priorities.get(&object.object_type) {
869            priority *= type_priority;
870        }
871
872        // Apply kernel context weight
873        if let Some(kernel_id) = object.kernel_context {
874            if let Some(&weight) = self.kernel_weights.get(&kernel_id) {
875                priority *= weight;
876            }
877        }
878
879        // Apply object priority
880        let priority_multiplier = match object.priority {
881            ObjectPriority::Critical => 100.0,
882            ObjectPriority::High => 10.0,
883            ObjectPriority::Normal => 1.0,
884            ObjectPriority::Low => 0.1,
885        };
886
887        priority * priority_multiplier
888    }
889}
890
891impl EvictionPolicy for WorkloadAwarePolicy {
892    fn name(&self) -> &str {
893        "WorkloadAware"
894    }
895
896    fn select_victims(&mut self, region: &MemoryRegion, target_bytes: usize) -> Vec<usize> {
897        // Calculate priorities for all objects
898        let mut object_priorities: Vec<(usize, f64)> = region
899            .objects
900            .iter()
901            .map(|(&addr, obj)| (addr, self.calculate_eviction_priority(obj)))
902            .collect();
903
904        // Sort by priority (lowest first = best eviction candidates)
905        object_priorities
906            .sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
907
908        let mut victims = Vec::new();
909        let mut bytes_selected = 0;
910
911        for (address, _priority) in object_priorities {
912            if let Some(object) = region.objects.get(&address) {
913                victims.push(address);
914                bytes_selected += object.size;
915
916                if bytes_selected >= target_bytes {
917                    break;
918                }
919            }
920        }
921
922        self.stats.evictions += victims.len() as u64;
923        self.stats.bytes_evicted += bytes_selected as u64;
924
925        victims
926    }
927
928    fn update_access(&mut self, address: usize, object: &CacheObject) {
929        self.base_policy.update_access(address, object);
930
931        // Update kernel weights based on access patterns
932        if let Some(kernel_id) = object.kernel_context {
933            let weight = self.kernel_weights.entry(kernel_id).or_insert(1.0);
934            *weight = (*weight * 0.9 + 1.1).min(10.0); // Increase weight for active kernels
935        }
936    }
937
938    fn add_object(&mut self, address: usize, object: &CacheObject) {
939        self.base_policy.add_object(address, object);
940    }
941
942    fn remove_object(&mut self, address: usize) {
943        self.base_policy.remove_object(address);
944    }
945
946    fn get_statistics(&self) -> PolicyStats {
947        let mut stats = self.stats.clone();
948        let base_stats = self.base_policy.get_statistics();
949
950        // Combine statistics
951        stats.evictions += base_stats.evictions;
952        stats.bytes_evicted += base_stats.bytes_evicted;
953
954        stats
955    }
956
957    fn configure(&mut self, config: &EvictionConfig) {
958        self.base_policy.configure(config);
959    }
960
961    fn reset(&mut self) {
962        self.base_policy.reset();
963        self.kernel_weights.clear();
964        self.stats = PolicyStats::default();
965    }
966}
967
968/// Performance monitoring for eviction policies
969pub struct EvictionPerformanceMonitor {
970    /// Performance history
971    history: VecDeque<EvictionPerformance>,
972    /// Policy performance tracking
973    policy_performance: HashMap<String, Vec<f64>>,
974    /// Configuration
975    config: MonitorConfig,
976}
977
978/// Eviction performance sample
979#[derive(Debug, Clone)]
980pub struct EvictionPerformance {
981    pub timestamp: Instant,
982    pub policy_name: String,
983    pub eviction_time: Duration,
984    pub bytes_evicted: usize,
985    pub objects_evicted: usize,
986    pub accuracy_score: f64,
987}
988
989/// Monitor configuration
990#[derive(Debug, Clone)]
991pub struct MonitorConfig {
992    pub history_size: usize,
993    pub performance_window: usize,
994    pub enable_adaptive: bool,
995}
996
997impl Default for MonitorConfig {
998    fn default() -> Self {
999        Self {
1000            history_size: 1000,
1001            performance_window: 100,
1002            enable_adaptive: true,
1003        }
1004    }
1005}
1006
1007impl EvictionPerformanceMonitor {
1008    pub fn new(config: MonitorConfig) -> Self {
1009        Self {
1010            history: VecDeque::with_capacity(config.history_size),
1011            policy_performance: HashMap::new(),
1012            config,
1013        }
1014    }
1015
1016    /// Record eviction performance
1017    pub fn record_performance(&mut self, performance: EvictionPerformance) {
1018        self.history.push_back(performance.clone());
1019        if self.history.len() > self.config.history_size {
1020            self.history.pop_front();
1021        }
1022
1023        // Update policy performance tracking
1024        let scores = self
1025            .policy_performance
1026            .entry(performance.policy_name.clone())
1027            .or_default();
1028
1029        scores.push(performance.accuracy_score);
1030        if scores.len() > self.config.performance_window {
1031            scores.remove(0);
1032        }
1033    }
1034
1035    /// Get best performing policy
1036    pub fn get_best_policy(&self) -> Option<String> {
1037        if !self.config.enable_adaptive {
1038            return None;
1039        }
1040
1041        let mut best_policy = None;
1042        let mut best_score = 0.0;
1043
1044        for (policy_name, scores) in &self.policy_performance {
1045            if scores.len() >= 5 {
1046                // Minimum samples required
1047                let avg_score = scores.iter().sum::<f64>() / scores.len() as f64;
1048                if avg_score > best_score {
1049                    best_score = avg_score;
1050                    best_policy = Some(policy_name.clone());
1051                }
1052            }
1053        }
1054
1055        best_policy
1056    }
1057}
1058
1059/// Policy selection record
1060#[derive(Debug, Clone)]
1061pub struct PolicySelection {
1062    pub timestamp: Instant,
1063    pub policy_name: String,
1064    pub reason: String,
1065    pub performance_score: f64,
1066}
1067
1068impl EvictionEngine {
1069    pub fn new(config: EvictionConfig) -> Self {
1070        let mut policies: HashMap<String, Box<dyn EvictionPolicy>> = HashMap::new();
1071
1072        // Add built-in policies
1073        policies.insert("LRU".to_string(), Box::new(LRUPolicy::new()));
1074        policies.insert("LFU".to_string(), Box::new(LFUPolicy::new()));
1075        policies.insert("FIFO".to_string(), Box::new(FIFOPolicy::new()));
1076        policies.insert("Clock".to_string(), Box::new(ClockPolicy::new()));
1077        policies.insert("ARC".to_string(), Box::new(ARCPolicy::new(1000)));
1078
1079        if config.workload_aware {
1080            let base_policy = Box::new(LRUPolicy::new());
1081            policies.insert(
1082                "WorkloadAware".to_string(),
1083                Box::new(WorkloadAwarePolicy::new(base_policy)),
1084            );
1085        }
1086
1087        let active_policy = config.default_policy.clone();
1088        let performance_monitor = EvictionPerformanceMonitor::new(MonitorConfig::default());
1089
1090        Self {
1091            config,
1092            stats: EvictionStats::default(),
1093            policies,
1094            active_policy,
1095            memory_regions: HashMap::new(),
1096            performance_monitor,
1097            policy_history: VecDeque::with_capacity(100),
1098        }
1099    }
1100
1101    /// Register a memory region
1102    pub fn register_region(&mut self, base_addr: usize, size: usize, region_type: RegionType) {
1103        let region = MemoryRegion {
1104            base_addr,
1105            size,
1106            objects: HashMap::new(),
1107            region_type,
1108            pressure: 0.0,
1109            last_eviction: None,
1110        };
1111
1112        self.memory_regions.insert(base_addr, region);
1113    }
1114
1115    /// Add object to tracking
1116    pub fn add_object(
1117        &mut self,
1118        region_addr: usize,
1119        object: CacheObject,
1120    ) -> Result<(), EvictionError> {
1121        let region = self
1122            .memory_regions
1123            .get_mut(&region_addr)
1124            .ok_or_else(|| EvictionError::RegionNotFound("Region not registered".to_string()))?;
1125
1126        // Add to all policies
1127        for policy in self.policies.values_mut() {
1128            policy.add_object(object.address, &object);
1129        }
1130
1131        region.objects.insert(object.address, object);
1132        Ok(())
1133    }
1134
1135    /// Update object access
1136    pub fn update_access(
1137        &mut self,
1138        region_addr: usize,
1139        object_addr: usize,
1140    ) -> Result<(), EvictionError> {
1141        let region = self
1142            .memory_regions
1143            .get_mut(&region_addr)
1144            .ok_or_else(|| EvictionError::RegionNotFound("Region not found".to_string()))?;
1145
1146        if let Some(object) = region.objects.get_mut(&object_addr) {
1147            object.update_access();
1148
1149            // Update all policies
1150            for policy in self.policies.values_mut() {
1151                policy.update_access(object_addr, object);
1152            }
1153        }
1154
1155        Ok(())
1156    }
1157
1158    /// Check if eviction is needed
1159    pub fn should_evict(&self, region_addr: usize) -> bool {
1160        if let Some(region) = self.memory_regions.get(&region_addr) {
1161            region.pressure > self.config.pressure_threshold
1162        } else {
1163            false
1164        }
1165    }
1166
1167    /// Perform eviction
1168    pub fn evict(
1169        &mut self,
1170        region_addr: usize,
1171        target_bytes: usize,
1172    ) -> Result<Vec<usize>, EvictionError> {
1173        let region = self
1174            .memory_regions
1175            .get(&region_addr)
1176            .ok_or_else(|| EvictionError::RegionNotFound("Region not found".to_string()))?;
1177
1178        let start_time = Instant::now();
1179
1180        // Select policy (adaptive if enabled)
1181        let policy_name = if self.config.enable_adaptive {
1182            self.performance_monitor
1183                .get_best_policy()
1184                .unwrap_or_else(|| self.active_policy.clone())
1185        } else {
1186            self.active_policy.clone()
1187        };
1188
1189        let victims = if let Some(policy) = self.policies.get_mut(&policy_name) {
1190            policy.select_victims(region, target_bytes)
1191        } else {
1192            return Err(EvictionError::PolicyNotFound(
1193                "Policy not available".to_string(),
1194            ));
1195        };
1196
1197        let eviction_time = start_time.elapsed();
1198
1199        // Remove evicted objects
1200        if let Some(region) = self.memory_regions.get_mut(&region_addr) {
1201            for &victim_addr in &victims {
1202                region.objects.remove(&victim_addr);
1203
1204                // Remove from all policies
1205                for policy in self.policies.values_mut() {
1206                    policy.remove_object(victim_addr);
1207                }
1208            }
1209
1210            region.last_eviction = Some(Instant::now());
1211        }
1212
1213        // Update statistics
1214        self.stats.total_evictions += 1;
1215        self.stats.total_objects_evicted += victims.len() as u64;
1216
1217        let total_eviction_time = self.stats.average_eviction_time.as_nanos() as u64
1218            * (self.stats.total_evictions - 1)
1219            + eviction_time.as_nanos() as u64;
1220        self.stats.average_eviction_time =
1221            Duration::from_nanos(total_eviction_time / self.stats.total_evictions);
1222
1223        // Record performance
1224        let performance = EvictionPerformance {
1225            timestamp: start_time,
1226            policy_name: policy_name.clone(),
1227            eviction_time,
1228            bytes_evicted: target_bytes,
1229            objects_evicted: victims.len(),
1230            accuracy_score: 0.8, // Would be calculated based on future access patterns
1231        };
1232
1233        self.performance_monitor.record_performance(performance);
1234
1235        Ok(victims)
1236    }
1237
1238    /// Switch active policy
1239    pub fn switch_policy(&mut self, policy_name: String) -> Result<(), EvictionError> {
1240        if !self.policies.contains_key(&policy_name) {
1241            return Err(EvictionError::PolicyNotFound(
1242                "Policy not available".to_string(),
1243            ));
1244        }
1245
1246        let selection = PolicySelection {
1247            timestamp: Instant::now(),
1248            policy_name: policy_name.clone(),
1249            reason: "Manual switch".to_string(),
1250            performance_score: 0.0,
1251        };
1252
1253        self.policy_history.push_back(selection);
1254        if self.policy_history.len() > 100 {
1255            self.policy_history.pop_front();
1256        }
1257
1258        self.active_policy = policy_name;
1259        self.stats.policy_switches += 1;
1260
1261        Ok(())
1262    }
1263
1264    /// Get statistics
1265    pub fn get_stats(&self) -> &EvictionStats {
1266        &self.stats
1267    }
1268
1269    /// Get policy statistics
1270    pub fn get_policy_stats(&self) -> HashMap<String, PolicyStats> {
1271        self.policies
1272            .iter()
1273            .map(|(name, policy)| (name.clone(), policy.get_statistics()))
1274            .collect()
1275    }
1276}
1277
1278/// Eviction errors
1279#[derive(Debug, Clone)]
1280pub enum EvictionError {
1281    RegionNotFound(String),
1282    PolicyNotFound(String),
1283    EvictionFailed(String),
1284    InvalidConfiguration(String),
1285}
1286
1287impl std::fmt::Display for EvictionError {
1288    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1289        match self {
1290            EvictionError::RegionNotFound(msg) => write!(f, "Region not found: {}", msg),
1291            EvictionError::PolicyNotFound(msg) => write!(f, "Policy not found: {}", msg),
1292            EvictionError::EvictionFailed(msg) => write!(f, "Eviction failed: {}", msg),
1293            EvictionError::InvalidConfiguration(msg) => write!(f, "Invalid configuration: {}", msg),
1294        }
1295    }
1296}
1297
1298impl std::error::Error for EvictionError {}
1299
1300/// Thread-safe eviction engine wrapper
1301pub struct ThreadSafeEvictionEngine {
1302    engine: Arc<RwLock<EvictionEngine>>,
1303}
1304
1305impl ThreadSafeEvictionEngine {
1306    pub fn new(config: EvictionConfig) -> Self {
1307        Self {
1308            engine: Arc::new(RwLock::new(EvictionEngine::new(config))),
1309        }
1310    }
1311
1312    pub fn should_evict(&self, region_addr: usize) -> bool {
1313        let engine = self.engine.read().unwrap_or_else(|e| e.into_inner());
1314        engine.should_evict(region_addr)
1315    }
1316
1317    pub fn evict(
1318        &self,
1319        region_addr: usize,
1320        target_bytes: usize,
1321    ) -> Result<Vec<usize>, EvictionError> {
1322        let mut engine = self.engine.write().unwrap_or_else(|e| e.into_inner());
1323        engine.evict(region_addr, target_bytes)
1324    }
1325
1326    pub fn add_object(&self, region_addr: usize, object: CacheObject) -> Result<(), EvictionError> {
1327        let mut engine = self.engine.write().unwrap_or_else(|e| e.into_inner());
1328        engine.add_object(region_addr, object)
1329    }
1330
1331    pub fn get_stats(&self) -> EvictionStats {
1332        let engine = self.engine.read().unwrap_or_else(|e| e.into_inner());
1333        engine.get_stats().clone()
1334    }
1335}
1336
1337#[cfg(test)]
1338mod tests {
1339    use super::*;
1340
1341    #[test]
1342    fn test_eviction_engine_creation() {
1343        let config = EvictionConfig::default();
1344        let engine = EvictionEngine::new(config);
1345        assert!(!engine.policies.is_empty());
1346    }
1347
1348    #[test]
1349    fn test_lru_policy() {
1350        let mut policy = LRUPolicy::new();
1351        assert_eq!(policy.name(), "LRU");
1352
1353        let object = CacheObject {
1354            address: 0x1000,
1355            size: 64,
1356            created_at: Instant::now(),
1357            last_access: Instant::now(),
1358            access_count: 1,
1359            access_frequency: 1.0,
1360            priority: ObjectPriority::Normal,
1361            kernel_context: None,
1362            object_type: ObjectType::Data,
1363            eviction_cost: 1.0,
1364            replacement_cost: 1.0,
1365        };
1366
1367        policy.add_object(0x1000, &object);
1368        assert_eq!(policy.lru_order.len(), 1);
1369    }
1370
1371    #[test]
1372    fn test_cache_object_utility() {
1373        let object = CacheObject {
1374            address: 0x1000,
1375            size: 64,
1376            created_at: Instant::now() - Duration::from_secs(10),
1377            last_access: Instant::now() - Duration::from_secs(1),
1378            access_count: 5,
1379            access_frequency: 0.5,
1380            priority: ObjectPriority::High,
1381            kernel_context: Some(100),
1382            object_type: ObjectType::Data,
1383            eviction_cost: 1.0,
1384            replacement_cost: 2.0,
1385        };
1386
1387        let utility = object.calculate_utility();
1388        assert!(utility > 0.0);
1389    }
1390
1391    #[test]
1392    fn test_thread_safe_engine() {
1393        let config = EvictionConfig::default();
1394        let engine = ThreadSafeEvictionEngine::new(config);
1395
1396        let stats = engine.get_stats();
1397        assert_eq!(stats.total_evictions, 0);
1398    }
1399}