cache_rs/metrics/
gdsf.rs

1//! GDSF Cache Metrics
2//!
3//! Metrics specific to the GDSF (Greedy Dual-Size Frequency) cache algorithm.
4
5extern crate alloc;
6
7use super::{CacheMetrics, CoreCacheMetrics};
8use alloc::collections::BTreeMap;
9use alloc::string::{String, ToString};
10
11/// GDSF-specific metrics (extends CoreCacheMetrics)
12///
13/// This struct contains metrics specific to the GDSF (Greedy Dual-Size Frequency)
14/// cache algorithm. GDSF combines frequency, size, and aging using the formula:
15/// Priority = (Frequency / Size) + Global_Age
16#[derive(Debug, Clone)]
17pub struct GdsfCacheMetrics {
18    /// Core metrics common to all cache algorithms
19    pub core: CoreCacheMetrics,
20
21    /// Current global age value
22    pub global_age: f64,
23
24    /// Total number of aging events (when global age is increased)
25    pub total_aging_events: u64,
26
27    /// Current minimum priority in the cache
28    pub min_priority: f64,
29
30    /// Current maximum priority in the cache
31    pub max_priority: f64,
32
33    /// Total frequency of all items (cumulative)
34    pub total_frequency: u64,
35
36    /// Total size of all items ever processed (for size-frequency analysis)
37    pub total_item_size_processed: u64,
38
39    /// Number of small items (below average size) that were cached
40    pub small_items_cached: u64,
41
42    /// Number of large items (above average size) that were cached
43    pub large_items_cached: u64,
44
45    /// Total number of size-based evictions (items evicted due to poor size/frequency ratio)
46    pub size_based_evictions: u64,
47
48    /// Sum of all frequency/size ratios for efficiency analysis
49    pub total_frequency_size_ratio: f64,
50}
51
52impl GdsfCacheMetrics {
53    /// Creates a new GdsfCacheMetrics instance with the specified maximum cache size
54    ///
55    /// # Arguments
56    /// * `max_cache_size_bytes` - The maximum allowed cache size in bytes
57    pub fn new(max_cache_size_bytes: u64) -> Self {
58        Self {
59            core: CoreCacheMetrics::new(max_cache_size_bytes),
60            global_age: 0.0,
61            total_aging_events: 0,
62            min_priority: 0.0,
63            max_priority: 0.0,
64            total_frequency: 0,
65            total_item_size_processed: 0,
66            small_items_cached: 0,
67            large_items_cached: 0,
68            size_based_evictions: 0,
69            total_frequency_size_ratio: 0.0,
70        }
71    }
72
73    /// Records an aging event (when global age is increased due to eviction)
74    ///
75    /// # Arguments
76    /// * `new_global_age` - The new global age value
77    pub fn record_aging_event(&mut self, new_global_age: f64) {
78        self.total_aging_events += 1;
79        self.global_age = new_global_age;
80    }
81
82    /// Records processing of an item (frequency increment and size tracking)
83    ///
84    /// # Arguments
85    /// * `frequency` - Current frequency of the item
86    /// * `size` - Size of the item in bytes
87    /// * `priority` - Calculated priority of the item
88    pub fn record_item_access(&mut self, frequency: u64, size: u64, priority: f64) {
89        self.total_frequency += frequency;
90        self.total_item_size_processed += size;
91
92        if size > 0 {
93            let freq_size_ratio = frequency as f64 / size as f64;
94            self.total_frequency_size_ratio += freq_size_ratio;
95        }
96
97        // Update min/max priority tracking
98        if self.min_priority == 0.0 || priority < self.min_priority {
99            self.min_priority = priority;
100        }
101        if priority > self.max_priority {
102            self.max_priority = priority;
103        }
104    }
105
106    /// Records caching of an item with size classification
107    ///
108    /// # Arguments
109    /// * `size` - Size of the cached item in bytes
110    /// * `average_size` - Current average item size for classification
111    pub fn record_item_cached(&mut self, size: u64, average_size: f64) {
112        if size as f64 <= average_size {
113            self.small_items_cached += 1;
114        } else {
115            self.large_items_cached += 1;
116        }
117    }
118
119    /// Records a size-based eviction
120    pub fn record_size_based_eviction(&mut self) {
121        self.size_based_evictions += 1;
122    }
123
124    /// Calculates the average frequency across all processed items
125    ///
126    /// # Returns
127    /// Average frequency per item access, or 0.0 if no items processed
128    pub fn average_frequency(&self) -> f64 {
129        if self.core.requests > 0 {
130            self.total_frequency as f64 / self.core.requests as f64
131        } else {
132            0.0
133        }
134    }
135
136    /// Calculates the average size of processed items
137    ///
138    /// # Returns
139    /// Average item size in bytes, or 0.0 if no items processed
140    pub fn average_item_size(&self) -> f64 {
141        if self.core.requests > 0 {
142            self.total_item_size_processed as f64 / self.core.requests as f64
143        } else {
144            0.0
145        }
146    }
147
148    /// Calculates the average frequency-to-size ratio
149    ///
150    /// # Returns
151    /// Average frequency/size ratio, or 0.0 if no items processed
152    pub fn average_frequency_size_ratio(&self) -> f64 {
153        if self.core.requests > 0 {
154            self.total_frequency_size_ratio / self.core.requests as f64
155        } else {
156            0.0
157        }
158    }
159
160    /// Calculates the priority range (max - min)
161    ///
162    /// # Returns
163    /// The range of priorities currently in the cache
164    pub fn priority_range(&self) -> f64 {
165        if self.max_priority >= self.min_priority {
166            self.max_priority - self.min_priority
167        } else {
168            0.0
169        }
170    }
171
172    /// Calculates size distribution balance (small vs large items)
173    ///
174    /// # Returns
175    /// Ratio of small items to total cached items, or 0.0 if no items cached
176    pub fn size_distribution_balance(&self) -> f64 {
177        let total_cached = self.small_items_cached + self.large_items_cached;
178        if total_cached > 0 {
179            self.small_items_cached as f64 / total_cached as f64
180        } else {
181            0.0
182        }
183    }
184
185    /// Calculates size-based eviction efficiency
186    ///
187    /// # Returns
188    /// Ratio of size-based evictions to total evictions
189    pub fn size_eviction_efficiency(&self) -> f64 {
190        if self.core.evictions > 0 {
191            self.size_based_evictions as f64 / self.core.evictions as f64
192        } else {
193            0.0
194        }
195    }
196
197    /// Converts GDSF metrics to a BTreeMap for reporting
198    ///
199    /// This method returns all metrics relevant to the GDSF cache algorithm,
200    /// including both core metrics and GDSF-specific size-frequency metrics.
201    ///
202    /// Uses BTreeMap to ensure consistent, deterministic ordering of metrics.
203    ///
204    /// # Returns
205    /// A BTreeMap containing all GDSF cache metrics as key-value pairs
206    pub fn to_btreemap(&self) -> BTreeMap<String, f64> {
207        let mut metrics = self.core.to_btreemap();
208
209        // GDSF-specific aging metrics
210        metrics.insert("global_age".to_string(), self.global_age);
211        metrics.insert(
212            "total_aging_events".to_string(),
213            self.total_aging_events as f64,
214        );
215
216        // Priority metrics
217        metrics.insert("min_priority".to_string(), self.min_priority);
218        metrics.insert("max_priority".to_string(), self.max_priority);
219        metrics.insert("priority_range".to_string(), self.priority_range());
220
221        // Frequency metrics
222        metrics.insert("total_frequency".to_string(), self.total_frequency as f64);
223        metrics.insert("average_frequency".to_string(), self.average_frequency());
224
225        // Size metrics
226        metrics.insert(
227            "total_item_size_processed".to_string(),
228            self.total_item_size_processed as f64,
229        );
230        metrics.insert("average_item_size".to_string(), self.average_item_size());
231        metrics.insert(
232            "small_items_cached".to_string(),
233            self.small_items_cached as f64,
234        );
235        metrics.insert(
236            "large_items_cached".to_string(),
237            self.large_items_cached as f64,
238        );
239        metrics.insert(
240            "size_distribution_balance".to_string(),
241            self.size_distribution_balance(),
242        );
243
244        // Size-frequency efficiency metrics
245        metrics.insert(
246            "total_frequency_size_ratio".to_string(),
247            self.total_frequency_size_ratio,
248        );
249        metrics.insert(
250            "average_frequency_size_ratio".to_string(),
251            self.average_frequency_size_ratio(),
252        );
253        metrics.insert(
254            "size_based_evictions".to_string(),
255            self.size_based_evictions as f64,
256        );
257        metrics.insert(
258            "size_eviction_efficiency".to_string(),
259            self.size_eviction_efficiency(),
260        );
261
262        // Rate metrics
263        if self.core.requests > 0 {
264            metrics.insert(
265                "aging_event_rate".to_string(),
266                self.total_aging_events as f64 / self.core.requests as f64,
267            );
268            metrics.insert(
269                "size_based_eviction_rate".to_string(),
270                self.size_based_evictions as f64 / self.core.requests as f64,
271            );
272        }
273
274        metrics
275    }
276}
277
278impl CacheMetrics for GdsfCacheMetrics {
279    /// Returns all GDSF cache metrics as key-value pairs in deterministic order
280    ///
281    /// # Returns
282    /// A BTreeMap containing all metrics tracked by this GDSF cache instance
283    fn metrics(&self) -> BTreeMap<String, f64> {
284        self.to_btreemap()
285    }
286
287    /// Returns the algorithm name for this cache implementation
288    ///
289    /// # Returns
290    /// "GDSF" - identifying this as a Greedy Dual-Size Frequency cache
291    fn algorithm_name(&self) -> &'static str {
292        "GDSF"
293    }
294}