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