Retain opt-in layout performance diagnostics
This commit is contained in:
1 parent
84f589e364
commit
480f0bc99f
8 files changed
+613
-22
No files matched your search
@@ -0,0 +1,150 @@
|
||||
//! Retained CPU-layout diagnostics on one reproducible random tree.
|
||||
//!
|
||||
//! Counters and phase timers:
|
||||
//!
|
||||
//! cargo test --release --features layout-diagnostics \
|
||||
//! --test layout_diagnostics -- --ignored --nocapture
|
||||
//!
|
||||
//! Uninstrumented hardware totals for one phase:
|
||||
//!
|
||||
//! IRIS_PHASE=resize IRIS_FRAMES=1000 perf stat \
|
||||
//! -e cycles:u,instructions:u cargo test --release \
|
||||
//! --test layout_diagnostics -- --ignored --nocapture
|
||||
//!
|
||||
//! `IRIS_PHASE` is `cold`, `repaint`, `size`, `scroll`, `resize`, or `all`.
|
||||
//! `IRIS_SEED`, `IRIS_DEPTH`, and `IRIS_FRAMES` select the load.
|
||||
|
||||
use iris::harness::Harness;
|
||||
use iris::prelude::*;
|
||||
use iris::random::{Edits, Tree, grow};
|
||||
use std::time::Instant;
|
||||
|
||||
const OUTPUT: (f32, f32) = (1920.0, 1200.0);
|
||||
|
||||
fn env<T: std::str::FromStr>(name: &str, fallback: T) -> T {
|
||||
std::env::var(name)
|
||||
.ok()
|
||||
.and_then(|value| value.parse().ok())
|
||||
.unwrap_or(fallback)
|
||||
}
|
||||
|
||||
fn warm(seed: u64, depth: usize) -> (Harness, Tree) {
|
||||
let mut harness = Harness::new(OUTPUT);
|
||||
let (root, tree) = grow(&mut harness.rsc, seed, depth, &Edits::default());
|
||||
harness.state.root = Some(root);
|
||||
harness.frame();
|
||||
println!(
|
||||
"fixture: seed {seed}, depth {depth}, {} widgets, {} active",
|
||||
tree.ids.len(),
|
||||
harness.render.active_widgets()
|
||||
);
|
||||
#[cfg(feature = "layout-diagnostics")]
|
||||
let _ = iris::core::layout_diagnostics::take();
|
||||
(harness, tree)
|
||||
}
|
||||
|
||||
fn report(label: &str, mut elapsed: Vec<f64>, _harness: &Harness) {
|
||||
elapsed.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
||||
let frames = elapsed.len();
|
||||
println!(
|
||||
"{label}: {frames} frame(s), min {:.3} ms, median {:.3} ms, total {:.1} ms",
|
||||
elapsed[0],
|
||||
elapsed[frames / 2],
|
||||
elapsed.iter().sum::<f64>(),
|
||||
);
|
||||
#[cfg(feature = "layout-diagnostics")]
|
||||
{
|
||||
let diagnostics = iris::core::layout_diagnostics::take();
|
||||
print!("{}", diagnostics.per_frame(frames));
|
||||
for callsite in diagnostics.hot_text().iter().take(3) {
|
||||
let mut ancestry = Vec::new();
|
||||
let mut id = Some(callsite.id);
|
||||
while let Some(widget) = id {
|
||||
ancestry.push(_harness.rsc.widgets().label(widget).as_str());
|
||||
id = _harness
|
||||
.render
|
||||
.active
|
||||
.get(&widget)
|
||||
.and_then(|active| active.parent);
|
||||
}
|
||||
println!(" text ancestry: {}", ancestry.join(" < "));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run(
|
||||
label: &str,
|
||||
frames: usize,
|
||||
harness: &mut Harness,
|
||||
mut change: impl FnMut(&mut Harness, usize),
|
||||
) {
|
||||
let mut elapsed = Vec::with_capacity(frames);
|
||||
for frame in 0..frames {
|
||||
change(harness, frame);
|
||||
let start = Instant::now();
|
||||
harness.frame();
|
||||
elapsed.push(start.elapsed().as_secs_f64() * 1_000.0);
|
||||
}
|
||||
report(label, elapsed, harness);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "measurement, not a check"]
|
||||
fn layout_cost() {
|
||||
let seed = env("IRIS_SEED", 1_u64);
|
||||
let depth = env("IRIS_DEPTH", 7_usize);
|
||||
let frames = env("IRIS_FRAMES", 100_usize);
|
||||
assert!(frames > 0, "IRIS_FRAMES must be greater than zero");
|
||||
let phase = env("IRIS_PHASE", String::from("all"));
|
||||
assert!(
|
||||
["all", "cold", "repaint", "size", "scroll", "resize"].contains(&phase.as_str()),
|
||||
"unknown IRIS_PHASE {phase:?}"
|
||||
);
|
||||
let selected = |name| phase == "all" || phase == name;
|
||||
|
||||
if selected("cold") {
|
||||
let mut harness = Harness::new(OUTPUT);
|
||||
let (root, tree) = grow(&mut harness.rsc, seed, depth, &Edits::default());
|
||||
harness.state.root = Some(root);
|
||||
println!(
|
||||
"fixture: seed {seed}, depth {depth}, {} widgets",
|
||||
tree.ids.len()
|
||||
);
|
||||
#[cfg(feature = "layout-diagnostics")]
|
||||
let _ = iris::core::layout_diagnostics::take();
|
||||
run("cold", 1, &mut harness, |_, _| {});
|
||||
drop(tree);
|
||||
}
|
||||
|
||||
if selected("repaint") {
|
||||
let (mut harness, tree) = warm(seed, depth);
|
||||
let leaf = tree.ids[0];
|
||||
run("repaint", frames, &mut harness, move |harness, _| {
|
||||
let _ = harness.rsc.widgets_mut().get_dyn_mut(leaf);
|
||||
});
|
||||
}
|
||||
|
||||
if selected("size") {
|
||||
let (mut harness, tree) = warm(seed, depth);
|
||||
let sized = tree.sized[0];
|
||||
run("size", frames, &mut harness, move |harness, frame| {
|
||||
harness.rsc[sized].x = Some(Len::abs(100.0 + (frame % 2) as f32 * 40.0));
|
||||
});
|
||||
}
|
||||
|
||||
if selected("scroll") {
|
||||
let (mut harness, tree) = warm(seed, depth);
|
||||
let scroll = tree.scrolls[0];
|
||||
run("scroll", frames, &mut harness, move |harness, frame| {
|
||||
harness.rsc[scroll].scroll(if frame % 2 == 0 { 12.0 } else { -12.0 });
|
||||
});
|
||||
}
|
||||
|
||||
if selected("resize") {
|
||||
let (mut harness, tree) = warm(seed, depth);
|
||||
run("resize", frames, &mut harness, |harness, frame| {
|
||||
harness.resize((OUTPUT.0 - (frame % 2) as f32 * 8.0, OUTPUT.1));
|
||||
});
|
||||
drop(tree);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user