1use std::collections::BTreeMap;
4use std::fmt::Write;
5
6use super::{CacheStats, Lookup, Tally};
7
8fn line(tally: &Tally) -> String {
9 format!(
10 "hit={} miss={} prefix={} new={} reused={}",
11 tally.hit, tally.miss, tally.prefix, tally.new, tally.reused
12 )
13}
14
15fn percent(part: usize, whole: usize) -> f64 {
16 match whole {
17 0 => 0.0,
18 _ => 100.0 * part as f64 / whole as f64,
19 }
20}
21
22pub fn cache_log(stats: &CacheStats, rate: u32) -> String {
25 let (lookups, reached) = (&stats.lookups, &stats.reached);
26 let mut out = String::new();
27 let mut cuts: Vec<(String, usize)> = Vec::new();
28 let mut second = None;
29 for (k, &(at, made)) in reached.iter().enumerate() {
30 let whole = at.div_euclid(i64::from(rate));
31 match second {
32 None => cuts.push(("pass".to_string(), made)),
33 Some(last) if whole > last || k + 1 == reached.len() => {
34 cuts.push((format!("t={:.3}s", at as f64 / f64::from(rate)), made));
35 }
36 Some(_) => continue,
37 }
38 second = Some(whole);
39 }
40 if cuts.last().is_none_or(|(_, made)| *made < lookups.len()) {
41 cuts.push(("end".to_string(), lookups.len()));
42 }
43 let (mut from, mut hits, mut looked) = (0, 0, 0);
44 for (label, to) in cuts {
45 let tally = Tally::of(&lookups[from..to]);
46 (hits, looked) = (hits + tally.hit, looked + tally.looked());
47 let cum = percent(hits, looked);
48 let tally = line(&tally);
49 let _ = writeln!(out, "sva-cache {label} {tally} cum-hit={cum:.1}%");
50 from = to;
51 }
52 let mut nodes: BTreeMap<&str, Vec<&Lookup>> = BTreeMap::new();
53 for lookup in lookups {
54 nodes.entry(&lookup.node).or_default().push(lookup);
55 }
56 for (node, lookups) in &nodes {
57 let tally = line(&Tally::of(lookups.iter().copied()));
58 let _ = writeln!(out, "sva-cache node {tally} {node}");
59 }
60 let total = Tally::of(lookups);
61 let _ = writeln!(
62 out,
63 "sva-cache total {} hit-rate={:.1}% nodes={} entries={} bytes={}",
64 line(&total),
65 percent(total.hit, total.looked()),
66 nodes.len(),
67 stats.entries,
68 stats.bytes
69 );
70 out
71}