metrale_circuit/hardware/
render_matrix.rs1use std::collections::BTreeMap;
10use std::fmt::Write as _;
11
12use super::HwReport;
13use super::render::{FIT_CONTEXTS, FIT_SEQS, pct, projection};
14use crate::venn::Class;
15
16pub type PortList = BTreeMap<String, (String, usize)>;
19
20pub fn port_lists(per_device: &[(String, PortList)]) -> String {
24 let mut s = String::from(
25 "## Port lists\n\nRule kernels a matrix model's policy could select that the device cannot run (the class does not compile them, its build compiles them out, or the device lacks the instruction), with the number of matrix models whose policy names them.\n",
26 );
27 for (device, kernels) in per_device {
28 let _ = writeln!(s, "\n### {device}\n");
29 if kernels.is_empty() {
30 let _ = writeln!(s, "None.");
31 continue;
32 }
33 let _ = writeln!(s, "| kernel | why | models |\n|---|---|---:|");
34 for (k, (why, n)) in kernels {
35 let _ = writeln!(s, "| {k} | {} | {n} |", why.replace('|', "\\|"));
36 }
37 }
38 s
39}
40
41pub fn summary_row(r: &HwReport) -> String {
44 let usable = r.resolved.device.memory_bytes * r.resolved.device.usable_fraction;
45 let fits = |c: u64| r.footprint.bytes(c, FIT_CONTEXTS[0]) <= usable;
46 let max_c = FIT_SEQS.iter().copied().filter(|&c| fits(c)).max();
47 let tok = |i: usize| {
48 r.tables.get(i).map_or_else(
49 || "-".into(),
50 |t| format!("{:.0}", t.planned.plan.rows as f64 / (t.total_us / 1e6)),
51 )
52 };
53 let wide = r.tables.get(1);
54 let mut gaps: Vec<(f64, String)> = wide
55 .map(|t| {
56 t.rows
57 .iter()
58 .filter(|x| !matches!(x.class, Class::Shared | Class::SharedUnmeasured))
59 .map(|x| {
60 (
61 x.share,
62 format!("{} ({}, {})", x.site, x.class.name(), pct(x.share)),
63 )
64 })
65 .collect()
66 })
67 .unwrap_or_default();
68 if let Some(t) = wide {
69 for f in &t.flags {
70 let kind = f.layer_kind.map_or("prologue/epilogue", |k| k.name());
71 gaps.push((f.share, format!("per-row {kind} ({})", pct(f.share))));
72 }
73 }
74 gaps.sort_by(|a, b| b.0.total_cmp(&a.0).then_with(|| a.1.cmp(&b.1)));
75 let top: Vec<String> = gaps.into_iter().take(5).map(|(_, g)| g).collect();
76 format!(
77 "| {} | {} | {} / {} | {} | {} / {} / {}{} | {} | {} | {} |",
78 r.model.label,
79 r.resolved.device.id,
80 pct(r.covered(0, true)),
81 pct(r.covered(1, true)),
82 pct(r.covered(1, false)),
83 tok(0),
84 tok(1),
85 tok(2),
86 projection(r),
87 if fits(1) {
88 "yes".to_string()
89 } else {
90 let need = r.footprint.bytes(1, FIT_CONTEXTS[0]);
91 format!("no: TP={}", (need / usable).ceil() as u64)
92 },
93 max_c.map_or_else(|| "none".into(), |c| c.to_string()),
94 if top.is_empty() {
95 "none".into()
96 } else {
97 top.join("; ")
98 }
99 )
100}