Dirty many widgets at once, which nothing was checking
Every generated case changes one thing: four declared sizes, or one span's children, or the output. A frame settling one dependency path says nothing about a frame settling a set of them that overlap, which is the case the settle order exists for. So two more: every declared size in the tree changing at once, and a spread of widgets marked for redraw together. The second changes nothing, which is the point -- no box may move, and the order the dirty set is taken in is all that can make one. The hundred-seed sweep is 1,000 comparisons now rather than 800, and passes. `IRIS_PHASE=many` is the same load for the diagnostics rig, with `IRIS_DIRTY` widgets marked per frame. It says what one repainting leaf cannot: at 130 of 260 widgets, choosing which dirty widget to settle next is 24.5% of the frame, because the dirty set is scanned once per widget settled and a hash set is walked by capacity rather than by length. Memoizing the depth walk inside one scan does not pay -- it trades parent lookups for memo lookups and costs 4% more instructions -- so the fix is to stop rescanning, which changes the order widgets settle in and wants agreeing first. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2525637e26
commit
77bb75e5de
2 files changed
+99
-11
No files matched your search
@@ -11,8 +11,9 @@
|
||||
//! -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.
|
||||
//! `IRIS_PHASE` is `cold`, `repaint`, `many`, `size`, `scroll`, `resize`, or
|
||||
//! `all`. `IRIS_SEED`, `IRIS_DEPTH`, and `IRIS_FRAMES` select the load, and
|
||||
//! `IRIS_DIRTY` how many widgets `many` marks at once.
|
||||
|
||||
use iris::harness::Harness;
|
||||
use iris::prelude::*;
|
||||
@@ -165,7 +166,7 @@ fn layout_cost() {
|
||||
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()),
|
||||
["all", "cold", "repaint", "many", "size", "scroll", "resize"].contains(&phase.as_str()),
|
||||
"unknown IRIS_PHASE {phase:?}"
|
||||
);
|
||||
let selected = |name| phase == "all" || phase == name;
|
||||
@@ -194,6 +195,22 @@ fn layout_cost() {
|
||||
});
|
||||
}
|
||||
|
||||
if selected("many") {
|
||||
let (mut harness, tree) = warm(seed, depth);
|
||||
trace_selected(&tree);
|
||||
// Spread through the tree rather than taken from one subtree, so the
|
||||
// dependency paths the frame settles overlap.
|
||||
let wanted = env("IRIS_DIRTY", 32_usize).max(1);
|
||||
let step = (tree.ids.len() / wanted).max(1);
|
||||
let dirty: Vec<_> = tree.ids.iter().copied().step_by(step).collect();
|
||||
println!("marking {} of {} widgets", dirty.len(), tree.ids.len());
|
||||
run("many", frames, &mut harness, move |harness, _| {
|
||||
for &id in &dirty {
|
||||
harness.rsc.widgets_mut().get_dyn_mut(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if selected("size") {
|
||||
let (mut harness, tree) = warm(seed, depth);
|
||||
trace_selected(&tree);
|
||||
|
||||
Reference in new issue
Block a user