1use std::{collections::BTreeMap, time::Duration};
4
5pub const CACHE_RESIDENCY_LAYER_REPORT_LIMIT: usize = 128;
11
12#[derive(Debug, Clone, Default, Eq, PartialEq)]
15pub struct CacheLayerResidencyStats {
16 pub logical_cached_tokens: u64,
18 pub key_value_blocks: u64,
20 pub compressed_latent_blocks: u64,
22 pub device_blocks: u64,
24 pub host_blocks: u64,
26 pub disk_blocks: u64,
28 pub current_device_bytes: u64,
30 pub current_host_bytes: u64,
32 pub current_disk_bytes: u64,
34 pub mutable_tail_bytes: u64,
36 pub in_flight_write_blocks: u64,
38 pub in_flight_write_bytes: u64,
40 pub in_flight_host_demotion_blocks: u64,
42 pub in_flight_host_demotion_bytes: u64,
44 pub protected_recent_blocks: u64,
46 pub protected_prefix_blocks: u64,
48 pub host_promotions: u64,
50 pub disk_promotions: u64,
52 pub host_demotions: u64,
54 pub disk_demotions: u64,
56 pub transfer_bytes: u64,
58 pub transfer_wait: Duration,
60 pub demand_hits: u64,
62 pub demand_misses: u64,
64 pub in_flight_waits: u64,
66 pub failures: u64,
68 pub prefill_full_attention_blocks: u64,
70 pub prefill_full_attention_bytes: u64,
72 pub decode_full_attention_blocks: u64,
74 pub decode_full_attention_bytes: u64,
76 pub attention_scratch_peak_bytes: u64,
78}
79
80impl CacheLayerResidencyStats {
81 pub fn accumulate(&mut self, other: &Self) {
83 self.logical_cached_tokens += other.logical_cached_tokens;
84 self.key_value_blocks += other.key_value_blocks;
85 self.compressed_latent_blocks += other.compressed_latent_blocks;
86 self.device_blocks += other.device_blocks;
87 self.host_blocks += other.host_blocks;
88 self.disk_blocks += other.disk_blocks;
89 self.current_device_bytes += other.current_device_bytes;
90 self.current_host_bytes += other.current_host_bytes;
91 self.current_disk_bytes += other.current_disk_bytes;
92 self.mutable_tail_bytes += other.mutable_tail_bytes;
93 self.in_flight_write_blocks += other.in_flight_write_blocks;
94 self.in_flight_write_bytes += other.in_flight_write_bytes;
95 self.in_flight_host_demotion_blocks += other.in_flight_host_demotion_blocks;
96 self.in_flight_host_demotion_bytes += other.in_flight_host_demotion_bytes;
97 self.protected_recent_blocks += other.protected_recent_blocks;
98 self.protected_prefix_blocks += other.protected_prefix_blocks;
99 self.host_promotions += other.host_promotions;
100 self.disk_promotions += other.disk_promotions;
101 self.host_demotions += other.host_demotions;
102 self.disk_demotions += other.disk_demotions;
103 self.transfer_bytes += other.transfer_bytes;
104 self.transfer_wait += other.transfer_wait;
105 self.demand_hits += other.demand_hits;
106 self.demand_misses += other.demand_misses;
107 self.in_flight_waits += other.in_flight_waits;
108 self.failures += other.failures;
109 self.prefill_full_attention_blocks += other.prefill_full_attention_blocks;
110 self.prefill_full_attention_bytes += other.prefill_full_attention_bytes;
111 self.decode_full_attention_blocks += other.decode_full_attention_blocks;
112 self.decode_full_attention_bytes += other.decode_full_attention_bytes;
113 self.attention_scratch_peak_bytes = self
114 .attention_scratch_peak_bytes
115 .max(other.attention_scratch_peak_bytes);
116 }
117}
118
119#[derive(Debug, Clone, Default, Eq, PartialEq)]
121pub struct CacheLayerResidencyReport {
122 pub global_layer: usize,
124 pub stats: CacheLayerResidencyStats,
126}
127
128#[derive(Debug, Clone, Default, Eq, PartialEq)]
130pub struct CacheResidencyReport {
131 pub logical_cached_tokens: u64,
133 pub key_value_blocks: u64,
135 pub compressed_latent_blocks: u64,
137 pub device_blocks: u64,
139 pub host_blocks: u64,
141 pub disk_blocks: u64,
143 pub current_device_bytes: u64,
145 pub peak_device_bytes: u64,
147 pub current_host_bytes: u64,
149 pub peak_host_bytes: u64,
151 pub current_disk_bytes: u64,
153 pub peak_disk_bytes: u64,
155 pub in_flight_write_blocks: u64,
157 pub in_flight_write_bytes: u64,
159 pub peak_in_flight_write_bytes: u64,
161 pub in_flight_host_demotion_blocks: u64,
163 pub in_flight_host_demotion_bytes: u64,
165 pub peak_in_flight_host_demotion_bytes: u64,
167 pub mutable_tail_bytes: u64,
169 pub protected_recent_blocks: u64,
171 pub protected_prefix_blocks: u64,
173 pub per_layer: Vec<CacheLayerResidencyReport>,
175 pub per_layer_overflow_layers: u64,
177 pub per_layer_overflow: CacheLayerResidencyStats,
179 pub host_promotions: u64,
181 pub disk_promotions: u64,
183 pub host_demotions: u64,
185 pub disk_demotions: u64,
187 pub transfer_bytes: u64,
189 pub transfer_wait: Duration,
191 pub evictions: u64,
193 pub discarded_sliding_blocks: u64,
195 pub block_seals: u64,
197 pub tail_allocations: u64,
199 pub demand_hits: u64,
201 pub demand_misses: u64,
203 pub in_flight_waits: u64,
205 pub queue_capacity: usize,
207 pub queue_peak_occupancy: usize,
209 pub queue_backpressure: u64,
211 pub cancellations: u64,
213 pub failures: u64,
215 pub prefill_full_attention_blocks: u64,
217 pub prefill_full_attention_bytes: u64,
219 pub decode_full_attention_blocks: u64,
221 pub decode_full_attention_bytes: u64,
223 pub attention_scratch_peak_bytes: u64,
225 pub prompt_cache_saves: u64,
227 pub prompt_cache_loads: u64,
229 pub prompt_cache_bytes: u64,
231 pub imported_buffered_shards: u64,
233 pub process_rss_bytes: Option<u64>,
235 pub process_minor_page_faults: Option<u64>,
237 pub process_major_page_faults: Option<u64>,
239}
240
241#[derive(Debug, Default)]
247pub struct CacheResidencyTelemetry {
248 pub report: CacheResidencyReport,
250 layer_activity: BTreeMap<usize, CacheLayerResidencyStats>,
251 layer_activity_overflow: CacheLayerResidencyStats,
252}
253
254impl CacheResidencyTelemetry {
255 pub fn new(queue_capacity: usize) -> Self {
257 let mut telemetry = Self::default();
258 telemetry.report.queue_capacity = queue_capacity;
259 telemetry
260 }
261
262 pub fn layer_activity_mut(&mut self, global_layer: usize) -> &mut CacheLayerResidencyStats {
267 if self.layer_activity.contains_key(&global_layer)
268 || self.layer_activity.len() < CACHE_RESIDENCY_LAYER_REPORT_LIMIT
269 {
270 self.layer_activity.entry(global_layer).or_default()
271 } else {
272 &mut self.layer_activity_overflow
273 }
274 }
275
276 pub fn unassigned_activity_mut(&mut self) -> &mut CacheLayerResidencyStats {
278 &mut self.layer_activity_overflow
279 }
280
281 pub fn finalize_snapshot(
286 &mut self,
287 mut current: BTreeMap<usize, CacheLayerResidencyStats>,
288 device_budget_bytes: u64,
289 host_budget_bytes: u64,
290 disk_budget_bytes: Option<u64>,
291 ) {
292 self.report.per_layer.clear();
293 self.report.per_layer_overflow_layers = 0;
294 self.report.per_layer_overflow = CacheLayerResidencyStats::default();
295
296 let mut selected_layers = self.layer_activity.keys().copied().collect::<Vec<_>>();
297 for global_layer in current.keys().copied() {
298 if selected_layers.len() == CACHE_RESIDENCY_LAYER_REPORT_LIMIT {
299 break;
300 }
301 if !self.layer_activity.contains_key(&global_layer) {
302 selected_layers.push(global_layer);
303 }
304 }
305 selected_layers.sort_unstable();
306 for global_layer in selected_layers {
307 let mut stats = current.remove(&global_layer).unwrap_or_default();
308 if let Some(activity) = self.layer_activity.get(&global_layer) {
309 apply_activity(activity, &mut stats);
310 }
311 self.report.per_layer.push(CacheLayerResidencyReport {
312 global_layer,
313 stats,
314 });
315 }
316 for (_, stats) in current {
317 self.report.per_layer_overflow_layers += 1;
318 self.report.per_layer_overflow.accumulate(&stats);
319 }
320 apply_activity(
321 &self.layer_activity_overflow,
322 &mut self.report.per_layer_overflow,
323 );
324
325 if self.report.current_device_bytes <= device_budget_bytes {
326 self.report.peak_device_bytes = self
327 .report
328 .peak_device_bytes
329 .max(self.report.current_device_bytes);
330 }
331 if self.report.current_host_bytes <= host_budget_bytes {
332 self.report.peak_host_bytes = self
333 .report
334 .peak_host_bytes
335 .max(self.report.current_host_bytes);
336 }
337 if disk_budget_bytes.is_none_or(|budget| self.report.current_disk_bytes <= budget) {
338 self.report.peak_disk_bytes = self
339 .report
340 .peak_disk_bytes
341 .max(self.report.current_disk_bytes);
342 }
343 self.report.peak_in_flight_write_bytes = self
344 .report
345 .peak_in_flight_write_bytes
346 .max(self.report.in_flight_write_bytes);
347 self.report.peak_in_flight_host_demotion_bytes = self
348 .report
349 .peak_in_flight_host_demotion_bytes
350 .max(self.report.in_flight_host_demotion_bytes);
351 }
352}
353
354fn apply_activity(activity: &CacheLayerResidencyStats, stats: &mut CacheLayerResidencyStats) {
355 stats.host_promotions += activity.host_promotions;
356 stats.disk_promotions += activity.disk_promotions;
357 stats.host_demotions += activity.host_demotions;
358 stats.disk_demotions += activity.disk_demotions;
359 stats.transfer_bytes += activity.transfer_bytes;
360 stats.transfer_wait += activity.transfer_wait;
361 stats.demand_hits += activity.demand_hits;
362 stats.demand_misses += activity.demand_misses;
363 stats.in_flight_waits += activity.in_flight_waits;
364 stats.failures += activity.failures;
365 stats.prefill_full_attention_blocks += activity.prefill_full_attention_blocks;
366 stats.prefill_full_attention_bytes += activity.prefill_full_attention_bytes;
367 stats.decode_full_attention_blocks += activity.decode_full_attention_blocks;
368 stats.decode_full_attention_bytes += activity.decode_full_attention_bytes;
369 stats.attention_scratch_peak_bytes = stats
370 .attention_scratch_peak_bytes
371 .max(activity.attention_scratch_peak_bytes);
372}
373
374#[cfg(test)]
375mod tests {
376 use super::*;
377
378 #[test]
379 fn aggregation_sums_current_and_cumulative_fields_but_preserves_peaks() {
380 let mut aggregate = CacheLayerResidencyStats {
381 current_device_bytes: 4,
382 transfer_bytes: 8,
383 attention_scratch_peak_bytes: 16,
384 ..Default::default()
385 };
386 aggregate.accumulate(&CacheLayerResidencyStats {
387 current_device_bytes: 5,
388 transfer_bytes: 9,
389 attention_scratch_peak_bytes: 12,
390 ..Default::default()
391 });
392 assert_eq!(aggregate.current_device_bytes, 9);
393 assert_eq!(aggregate.transfer_bytes, 17);
394 assert_eq!(aggregate.attention_scratch_peak_bytes, 16);
395 }
396
397 #[test]
398 fn telemetry_keeps_historical_layers_stable_and_folds_current_overflow() {
399 let mut telemetry = CacheResidencyTelemetry::new(3);
400 for layer in 0..CACHE_RESIDENCY_LAYER_REPORT_LIMIT {
401 telemetry.layer_activity_mut(layer).demand_hits = 1;
402 }
403 telemetry.unassigned_activity_mut().failures = 2;
404 telemetry.report.current_device_bytes = 8;
405 telemetry.report.current_host_bytes = 12;
406 telemetry.report.current_disk_bytes = 16;
407 telemetry.finalize_snapshot(
408 BTreeMap::from([(
409 CACHE_RESIDENCY_LAYER_REPORT_LIMIT,
410 CacheLayerResidencyStats {
411 device_blocks: 1,
412 ..Default::default()
413 },
414 )]),
415 8,
416 12,
417 Some(16),
418 );
419
420 assert_eq!(telemetry.report.queue_capacity, 3);
421 assert_eq!(
422 telemetry.report.per_layer.len(),
423 CACHE_RESIDENCY_LAYER_REPORT_LIMIT
424 );
425 assert_eq!(telemetry.report.per_layer_overflow_layers, 1);
426 assert_eq!(telemetry.report.per_layer_overflow.device_blocks, 1);
427 assert_eq!(telemetry.report.per_layer_overflow.failures, 2);
428 assert_eq!(telemetry.report.peak_device_bytes, 8);
429 assert_eq!(telemetry.report.peak_host_bytes, 12);
430 assert_eq!(telemetry.report.peak_disk_bytes, 16);
431 }
432}