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}