From 394d5149a5e6118e6059b25bf809cccc4989ab7d Mon Sep 17 00:00:00 2001 From: iris-ai <4+iris-ai@noreply.localhost> Date: Wed, 16 Sep 2026 14:06:44 -0400 Subject: [PATCH] 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 --- src/random.rs | 20 +++++++++++++++++++- tests/layout_diagnostics.rs | 13 +++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/random.rs b/src/random.rs index daf137f..98bcc91 100644 --- a/src/random.rs +++ b/src/random.rs @@ -29,6 +29,17 @@ pub struct Edits { /// 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. pub nodes: HashMap, + /// 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)] @@ -286,7 +297,14 @@ impl Grow<'_, Rsc> { let probe = self.node(depth - 1); let wide = 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 { probe, wide, diff --git a/tests/layout_diagnostics.rs b/tests/layout_diagnostics.rs index e7a9548..db4e618 100644 --- a/tests/layout_diagnostics.rs +++ b/tests/layout_diagnostics.rs @@ -92,9 +92,18 @@ fn trace_selected(tree: &Tree) { #[cfg(not(feature = "layout-diagnostics"))] 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) { 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.frame(); println!( @@ -173,7 +182,7 @@ fn layout_cost() { if selected("cold") { 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); println!( "fixture: seed {seed}, depth {depth}, {} widgets",