cache_rs/metrics/
lfu.rs

1//! LFU Cache Metrics
2//!
3//! Metrics specific to the LFU (Least Frequently Used) cache algorithm.
4
5extern crate alloc;
6
7use super::{CacheMetrics, CoreCacheMetrics};
8use alloc::collections::BTreeMap;
9use alloc::string::{String, ToString};
10
11/// LFU-specific metrics (extends CoreCacheMetrics)
12///
13/// This struct contains metrics specific to the LFU (Least Frequently Used) cache algorithm.
14/// LFU tracks frequency of access for each item, so these metrics focus on frequency
15/// distribution and access patterns.
16#[derive(Debug, Clone)]
17pub struct LfuCacheMetrics {
18    /// Core metrics common to all cache algorithms
19    pub core: CoreCacheMetrics,
20
21    /// Current minimum frequency in the cache
22    pub min_frequency: u64,
23
24    /// Current maximum frequency in the cache
25    pub max_frequency: u64,
26
27    /// Total number of frequency increments (every cache hit increases frequency)
28    pub total_frequency_increments: u64,
29
30    /// Number of unique frequency levels currently in use
31    pub active_frequency_levels: u64,
32}
33
34impl LfuCacheMetrics {
35    /// Creates a new LfuCacheMetrics instance with the specified maximum cache size
36    ///
37    /// # Arguments
38    /// * `max_cache_size_bytes` - The maximum allowed cache size in bytes
39    pub fn new(max_cache_size_bytes: u64) -> Self {
40        Self {
41            core: CoreCacheMetrics::new(max_cache_size_bytes),
42            min_frequency: 0,
43            max_frequency: 0,
44            total_frequency_increments: 0,
45            active_frequency_levels: 0,
46        }
47    }
48
49    /// Records a frequency increment (when an item is accessed and its frequency increases)
50    ///
51    /// # Arguments
52    /// * `old_frequency` - The previous frequency value
53    /// * `new_frequency` - The new frequency value for the accessed item
54    pub fn record_frequency_increment(&mut self, _old_frequency: usize, new_frequency: usize) {
55        self.total_frequency_increments += 1;
56
57        // Update min/max frequency tracking
58        let new_freq_u64 = new_frequency as u64;
59        if self.min_frequency == 0 || new_freq_u64 < self.min_frequency {
60            self.min_frequency = new_freq_u64;
61        }
62        if new_freq_u64 > self.max_frequency {
63            self.max_frequency = new_freq_u64;
64        }
65    }
66
67    /// Records a cache hit with frequency information
68    ///
69    /// # Arguments
70    /// * `object_size` - Size of the object that was served from cache (in bytes)
71    /// * `frequency` - Current frequency of the accessed item
72    pub fn record_frequency_hit(&mut self, object_size: u64, frequency: usize) {
73        self.core.record_hit(object_size);
74
75        // Update frequency bounds
76        let freq_u64 = frequency as u64;
77        if self.min_frequency == 0 || freq_u64 < self.min_frequency {
78            self.min_frequency = freq_u64;
79        }
80        if freq_u64 > self.max_frequency {
81            self.max_frequency = freq_u64;
82        }
83    }
84
85    /// Updates the frequency levels based on current frequency lists
86    ///
87    /// # Arguments
88    /// * `frequency_lists` - Map of frequency to list of items at that frequency
89    pub fn update_frequency_levels<T>(&mut self, frequency_lists: &BTreeMap<usize, T>) {
90        self.active_frequency_levels = frequency_lists.len() as u64;
91
92        // Update min/max from the actual frequency keys
93        if let (Some(&min_freq), Some(&max_freq)) =
94            (frequency_lists.keys().min(), frequency_lists.keys().max())
95        {
96            self.min_frequency = min_freq as u64;
97            self.max_frequency = max_freq as u64;
98        }
99    }
100
101    /// Records a cache miss for LFU metrics
102    ///
103    /// # Arguments
104    /// * `object_size` - Size of the object that was requested (in bytes)
105    pub fn record_miss(&mut self, object_size: u64) {
106        self.core.record_miss(object_size);
107    }
108
109    /// Updates the count of active frequency levels
110    ///
111    /// # Arguments
112    /// * `levels` - The number of different frequency levels currently in use
113    pub fn update_active_frequency_levels(&mut self, levels: u64) {
114        self.active_frequency_levels = levels;
115    }
116
117    /// Calculates the average frequency of accesses
118    ///
119    /// # Returns
120    /// Average frequency per item access, or 0.0 if no hits have occurred
121    pub fn average_frequency(&self) -> f64 {
122        if self.core.cache_hits > 0 {
123            self.total_frequency_increments as f64 / self.core.cache_hits as f64
124        } else {
125            0.0
126        }
127    }
128
129    /// Calculates the frequency range (max - min)
130    ///
131    /// # Returns
132    /// The range of frequencies currently in the cache
133    pub fn frequency_range(&self) -> u64 {
134        self.max_frequency.saturating_sub(self.min_frequency)
135    }
136
137    /// Converts LFU metrics to a BTreeMap for reporting
138    ///
139    /// This method returns all metrics relevant to the LFU cache algorithm,
140    /// including both core metrics and LFU-specific frequency metrics.
141    ///
142    /// Uses BTreeMap to ensure consistent, deterministic ordering of metrics.
143    ///
144    /// # Returns
145    /// A BTreeMap containing all LFU cache metrics as key-value pairs
146    pub fn to_btreemap(&self) -> BTreeMap<String, f64> {
147        let mut metrics = self.core.to_btreemap();
148
149        // LFU-specific metrics
150        metrics.insert("min_frequency".to_string(), self.min_frequency as f64);
151        metrics.insert("max_frequency".to_string(), self.max_frequency as f64);
152        metrics.insert("frequency_range".to_string(), self.frequency_range() as f64);
153        metrics.insert(
154            "total_frequency_increments".to_string(),
155            self.total_frequency_increments as f64,
156        );
157        metrics.insert(
158            "active_frequency_levels".to_string(),
159            self.active_frequency_levels as f64,
160        );
161        metrics.insert("average_frequency".to_string(), self.average_frequency());
162
163        // Frequency efficiency metrics
164        if self.core.requests > 0 {
165            metrics.insert(
166                "frequency_increment_rate".to_string(),
167                self.total_frequency_increments as f64 / self.core.requests as f64,
168            );
169        }
170
171        if self.active_frequency_levels > 0 && self.core.cache_hits > 0 {
172            metrics.insert(
173                "frequency_distribution_efficiency".to_string(),
174                self.active_frequency_levels as f64 / self.core.cache_hits as f64,
175            );
176        }
177
178        metrics
179    }
180}
181
182impl CacheMetrics for LfuCacheMetrics {
183    /// Returns all LFU cache metrics as key-value pairs in deterministic order
184    ///
185    /// # Returns
186    /// A BTreeMap containing all metrics tracked by this LFU cache instance
187    fn metrics(&self) -> BTreeMap<String, f64> {
188        self.to_btreemap()
189    }
190
191    /// Returns the algorithm name for this cache implementation
192    ///
193    /// # Returns
194    /// "LFU" - identifying this as a Least Frequently Used cache
195    fn algorithm_name(&self) -> &'static str {
196        "LFU"
197    }
198}