Files
ARCADIA-Studio/harness/experiments/architecture-perf-20260925/architecture_perf_probe.rs
T

167 lines
6.1 KiB
Rust

//! Local-only CPU diagnosis. Does not measure native FPS, canvas redraw, or GPU.
//! Usage: architecture_perf_probe.exe PATH_TO_RELEASE1_MODEL_JSON [SAMPLES=40]
use oa_core::{persistence, projection, routing, view_options::ViewIndex, *};
use std::{collections::BTreeMap, hint::black_box, time::Instant};
fn measure<T>(label: &str, samples: usize, mut f: impl FnMut(usize) -> T) {
for i in 0..3 {
black_box(f(i));
}
let mut ms = Vec::with_capacity(samples);
for i in 0..samples {
let start = Instant::now();
let output = black_box(f(i));
ms.push(start.elapsed().as_secs_f64() * 1000.0);
drop(output); // Exclude destruction consistently from each timed phase.
}
ms.sort_by(f64::total_cmp);
let percentile = |p: f64| ms[((samples - 1) as f64 * p).ceil() as usize];
println!(
"{label}: n={samples} p50_ms={:.4} p95_ms={:.4} max_ms={:.4}",
percentile(0.5),
percentile(0.95),
ms[samples - 1]
);
}
fn probe(p: &Project, d: &Diagram, samples: usize) {
let index = ViewIndex::new(p);
let view = projection::project_diagram_with_index(p, d, &index);
println!(
"\nview={} projection={:?} nodes={} ports={} edges={}",
d.name,
d.projection,
view.nodes.len(),
view.ports.len(),
view.edges.len()
);
measure("index_new", samples, |_| ViewIndex::new(black_box(p)));
measure("projection_reused_index", samples, |_| {
projection::project_diagram_with_index(black_box(p), black_box(d), black_box(&index))
});
measure("projection_fresh_index", samples, |_| {
projection::project_diagram(black_box(p), black_box(d))
});
measure("routing_settled", samples, |_| {
routing::route_diagram(black_box(p), black_box(d), black_box(&view))
});
// Deterministic slight motion of the first root card. Includes the same
// descendant/route translation as legacy single-card canvas drag. This is
// presentation-only, not the allocation transaction pipeline.
let Some(root) = view.nodes.iter().find(|n| n.visual_parent.is_none()) else {
return;
};
println!("moving_root={} element={}", root.id, p.named(root.element));
let mut moved = projection::descendant_nodes(p, d.id, root.id);
moved.push(root.id);
let make_moved = |i: usize| {
let delta = Position {
x: (i % 21) as f32 * 3.0,
y: (i % 11) as f32 * 2.0,
};
let mut preview = d.clone();
let deltas = moved
.iter()
.map(|id| (*id, delta))
.collect::<BTreeMap<_, _>>();
for route in projection::translated_routes(p, d, &deltas) {
if let Some(stored) = preview.routes.iter_mut().find(|r| r.edge == route.edge) {
*stored = route;
} else {
preview.routes.push(route);
}
}
for id in &moved {
if let Some(n) = preview.nodes.get_mut(id) {
n.position.x += delta.x;
n.position.y += delta.y;
}
}
preview
};
let previews = (0..samples.max(3)).map(make_moved).collect::<Vec<_>>();
let projected = previews
.iter()
.map(|d| projection::project_diagram_with_index(p, d, &index))
.collect::<Vec<_>>();
measure("moved_projection", samples, |i| {
projection::project_diagram_with_index(p, black_box(&previews[i]), &index)
});
measure("moved_routing", samples, |i| {
routing::route_diagram(p, black_box(&previews[i]), black_box(&projected[i]))
});
measure("moved_prepare_projection_routing", samples, |i| {
let preview = make_moved(i);
let view = projection::project_diagram_with_index(p, &preview, &index);
routing::route_diagram(p, &preview, &view)
});
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let args = std::env::args().collect::<Vec<_>>();
let path = args
.get(1)
.ok_or("usage: architecture_perf_probe PATH [SAMPLES]")?;
let samples = args
.get(2)
.map(|s| s.parse::<usize>())
.transpose()?
.unwrap_or(40)
.max(1);
let p = persistence::decode(&std::fs::read(path)?)?;
println!(
"CPU-only probe; samples={samples}; fixture={path}; elements={} relations={}",
p.elements.len(),
p.relations.len()
);
for d in p.diagrams.values() {
probe(&p, d, samples);
}
// Approximation of the reported eight-card screenshot, not an exact repro:
// fixture semantics, Overview projection, synthetic positions/card sizes.
let mut approx = p.clone();
let id: Id = "f0440000-0000-4000-8000-000000000001".parse()?;
approx.diagrams.insert(
id,
Diagram {
id,
alias: "fps44_approx_overview".into(),
name: "APPROXIMATION eight-card SA Overview".into(),
description: "Synthetic positions, not user geometry".into(),
layer: Layer::Sa,
projection: ProjectionKind::Overview,
view_options: Default::default(),
nodes: BTreeMap::new(),
routes: vec![],
layout: None,
},
);
for (ordinal, (alias, x, y)) in [
("sa_assessment_procedure", 30., 240.),
("sa_triage", 510., 240.),
("sa_control", 990., 240.),
("sa_technician", 530., 440.),
("sa_assess", 1490., 240.),
("sa_repair", 1030., 440.),
("sa_confirm", 1530., 440.),
("sa_reset", 30., 440.),
]
.into_iter()
.enumerate()
{
let element = approx
.elements
.values()
.find(|e| e.alias == alias)
.ok_or_else(|| format!("missing alias {alias}"))?
.id;
let generated = approx.place(id, element, Position { x, y });
let diagram = approx.diagrams.get_mut(&id).unwrap();
let mut node = diagram.nodes.remove(&generated).unwrap();
node.id = format!("f0440000-0000-4000-8000-{:012}", ordinal + 2).parse()?;
diagram.nodes.insert(node.id, node);
}
probe(&approx, &approx.diagrams[&id], samples);
Ok(())
}