metrale_circuit/hardware/
render_routes.rs1use std::collections::BTreeSet;
12use std::fmt::Write as _;
13
14use super::gaps::{GapTable, gap_table};
15use super::plan::Resolved;
16use super::{HwError, HwReport, OnePlan};
17use crate::fuser::{FusionPlan, Policy};
18use crate::ir::Circuit;
19use crate::runtime::RuntimeRoute;
20
21#[derive(Debug, Clone)]
23pub struct RouteRow {
24 pub route: RuntimeRoute,
26 pub primary_us: f64,
28 pub primary_launches: u64,
30 pub table: GapTable,
32}
33
34pub fn route_rows(
36 r: &Resolved,
37 c: &Circuit,
38 policy: &Policy,
39 label: &str,
40 tables: &[GapTable],
41) -> Result<Vec<RouteRow>, HwError> {
42 let mut out = Vec::new();
43 for t in tables {
44 for rp in &t.planned.routes {
45 let settings = rp.route.policy(policy).settings;
46 out.push(RouteRow {
47 route: rp.route.clone(),
48 primary_us: t.total_us,
49 primary_launches: t.planned.plan.launches(),
50 table: gap_table(r, c, &settings, label, rp.planned.clone())?,
51 });
52 }
53 }
54 Ok(out)
55}
56
57fn rules_of(p: &FusionPlan) -> BTreeSet<&str> {
58 p.groups.iter().map(|g| g.rule.as_str()).collect()
59}
60
61fn swapped(primary: &FusionPlan, route: &FusionPlan) -> (Vec<String>, Vec<String>) {
63 let (a, b) = (rules_of(primary), rules_of(route));
64 (
65 b.difference(&a).map(|s| format!("`{s}`")).collect(),
66 a.difference(&b).map(|s| format!("`{s}`")).collect(),
67 )
68}
69
70pub(super) fn routes_section(s: &mut String, r: &HwReport) {
72 if r.routes.is_empty() {
73 return;
74 }
75 let _ = writeln!(
76 s,
77 "## Runtime routes\n\nConditions the engine checks at run time, under which a step runs another arm than the plan's (FUSIONS.toml `[[runtime]]`). The estimates above are the primary arm's; each route is planned and estimated beside it{}.\n",
78 super::render::projection(r)
79 );
80 let mut described = BTreeSet::new();
81 for row in &r.routes {
82 let x = &row.route;
83 if described.insert(x.id.as_str()) {
84 let _ = writeln!(
85 s,
86 "- `{}`: when {}; planned as `{}` ({}).",
87 x.id,
88 x.why,
89 x.plans_as_text(),
90 x.cite
91 );
92 }
93 }
94 let _ = writeln!(
95 s,
96 "\n| case | route | time (ms) | route time (ms) | launches | route launches | route runs | instead of |\n|---|---|---:|---:|---:|---:|---|---|"
97 );
98 for row in &r.routes {
99 let p = &row.table.planned.plan;
100 let primary = r
101 .tables
102 .iter()
103 .find(|t| t.planned.run == row.table.planned.run)
104 .map(|t| &t.planned.plan);
105 let (runs, instead) = primary.map(|q| swapped(q, p)).unwrap_or_default();
106 let _ = writeln!(
107 s,
108 "| decode C={} ({} n={}) | `{}` | {:.3} | {:.3} | {} | {} | {} | {} |",
109 p.rows,
110 p.mode.name(),
111 p.rows,
112 row.route.id,
113 row.primary_us / 1e3,
114 row.table.total_us / 1e3,
115 row.primary_launches,
116 p.launches(),
117 runs.join(", "),
118 instead.join(", ")
119 );
120 }
121 let _ = writeln!(s);
122}
123
124pub fn plan_text(circuit: &Circuit, one: &OnePlan) -> String {
128 let families = &one.resolved.families;
129 let group = |g: &crate::fuser::Group| super::tc_policy::unit_tag(families, &g.kernels);
130 let node = |n: crate::ir::NodeIdx| one.planned.pipelines.line(n);
131 let notes = crate::render::Notes {
132 group: &group,
133 node: &node,
134 };
135 let mut s = crate::render::render_noted(circuit, &one.planned.plan, &one.header, ¬es);
136 for rp in &one.planned.routes {
137 let header = crate::render::with_settings(&one.header, &rp.route.policy(&one.policy));
138 let node = |n: crate::ir::NodeIdx| rp.planned.pipelines.line(n);
139 let notes = crate::render::Notes {
140 group: &group,
141 node: &node,
142 };
143 s.push_str(&crate::render::route_section(
144 circuit,
145 &rp.route,
146 &rp.planned.plan,
147 &header,
148 ¬es,
149 ));
150 }
151 s
152}