Measure a cost on a tree that does not move when layout does

`Branch` picks which of two subtrees to draw by comparing a measured pixel
length with a threshold. That is exactly what the oracle wants -- it is how a
widget believing a measurement a cold start would not have given it becomes a
different tree -- and exactly what a rig measuring cost must not have: the
fixture's shape moves with the thing being measured.

It has been moving. Seed 1 at depth 8 draws 88 widgets and writes 2,298
primitives a frame at `5ed9e87`, and 115 and 8,209 at `bd6de71` -- three and
a half times the work -- so the handoff's "fixed point cost 3x" compared two
different workloads and is withdrawn. Measured on one tree instead, with
`Edits::fixed_branches`, `5ed9e87` is 1,761M instructions and ~699M cycles
against this head's 2,093M and ~819M, while drawing 100 widgets against 97
and writing 4,272 primitives against 3,951. Fixed point costs something like
a fifth to a quarter, not three times.

The oracle keeps measured branches: `fixed_branches` is false by default and
only the rig sets it. A branch consumes its randomness either way, so both
grow the same ids.

**Check the work counters before comparing two commits' times.** The rig
prints drawn widgets, widget draws and primitive writes for this reason;
an undrawn `leftover` child still moves them, which no flag can remove.

Checked: fmt, clippy, 105 tests, the 100-seed generated oracle.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
iris-aiandClaude Opus 5 committed 2026-09-16 14:06:44 -04:00
1 parent 4cbb242a5d
commit 394d5149a5
2 files changed
+30 -3

No files matched your search

+19 -1
View File
@@ -29,6 +29,17 @@ pub struct Edits {
/// one. Region nodes change what a move writes and how deep a primitive's /// one. Region nodes change what a move writes and how deep a primitive's
/// chain is, so a tree that never grows one leaves both untested. /// chain is, so a tree that never grows one leaves both untested.
pub nodes: HashMap<usize, bool>, pub nodes: HashMap<usize, bool>,
/// Whether a [`Branch`] takes the side it would take at any measurement,
/// rather than the side the one it made says. The oracle wants the
/// measured side -- that is the whole point of a branch, and how a widget
/// believing a measurement a cold start would not have given it becomes a
/// different tree. A rig measuring cost wants this instead: a fixture
/// whose shape moves with the thing being measured cannot be compared
/// with itself across a change to it, and seed 1 at depth 8 went from 88
/// drawn widgets and 2,298 primitive writes a frame to 115 and 8,209
/// across fixed point, which is three and a half times the work behind a
/// number read as three and a half times the cost.
pub fixed_branches: bool,
} }
#[derive(Default, Clone)] #[derive(Default, Clone)]
@@ -286,7 +297,14 @@ impl<Rsc: UiRsc + 'static> Grow<'_, Rsc> {
let probe = self.node(depth - 1); let probe = self.node(depth - 1);
let wide = self.node(depth - 1); let wide = self.node(depth - 1);
let narrow = self.node(depth - 1); let narrow = self.node(depth - 1);
let threshold = self.rng.below(500) as f32; // Drawn either way, so the side a fixed branch takes is still a
// side the generator chose -- and it consumes the same randomness
// as a measured one, so the two grow the same ids.
let measured = self.rng.below(500) as f32;
let threshold = match self.edits.fixed_branches {
true => f32::MIN,
false => measured,
};
let id = Branch { let id = Branch {
probe, probe,
wide, wide,
+11 -2
View File
@@ -92,9 +92,18 @@ fn trace_selected(tree: &Tree) {
#[cfg(not(feature = "layout-diagnostics"))] #[cfg(not(feature = "layout-diagnostics"))]
fn trace_selected(_: &Tree) {} fn trace_selected(_: &Tree) {}
/// The shape a cost is measured on must not depend on what layout measured,
/// or two commits are compared on two different trees. See `Edits`.
fn rig_edits() -> Edits {
Edits {
fixed_branches: true,
..Default::default()
}
}
fn warm(seed: u64, depth: usize) -> (Harness, Tree) { fn warm(seed: u64, depth: usize) -> (Harness, Tree) {
let mut harness = Harness::new(OUTPUT); let mut harness = Harness::new(OUTPUT);
let (root, tree) = grow(&mut harness.rsc, seed, depth, &Edits::default()); let (root, tree) = grow(&mut harness.rsc, seed, depth, &rig_edits());
harness.state.root = Some(root); harness.state.root = Some(root);
harness.frame(); harness.frame();
println!( println!(
@@ -173,7 +182,7 @@ fn layout_cost() {
if selected("cold") { if selected("cold") {
let mut harness = Harness::new(OUTPUT); let mut harness = Harness::new(OUTPUT);
let (root, tree) = grow(&mut harness.rsc, seed, depth, &Edits::default()); let (root, tree) = grow(&mut harness.rsc, seed, depth, &rig_edits());
harness.state.root = Some(root); harness.state.root = Some(root);
println!( println!(
"fixture: seed {seed}, depth {depth}, {} widgets", "fixture: seed {seed}, depth {depth}, {} widgets",