Record frame-extent performance gains and alignment verification

This commit is contained in:
iris-ai committed 2026-09-17 15:54:23 -04:00
1 parent 8f1a5e0a46
commit 264dc4f0c2
2 files changed
+90 -42

No files matched your search

+86 -41
View File
@@ -422,8 +422,9 @@ back.
The original proposal was implemented by Claude as `5fcace1` on The original proposal was implemented by Claude as `5fcace1` on
`wip/region-and-placement` in `/home/bob/repos/iris-pr18`. It kept the fraction `wip/region-and-placement` in `/home/bob/repos/iris-pr18`. It kept the fraction
reference stable but failed five layout cases and three draw-count cases. reference stable but failed five layout cases and three draw-count cases.
The first correction is `efb416b`; the exact-recomposition continuation is The first correction is `efb416b`, exact recomposition is `2ed5503`, and the
`2ed5503`, pushed to origin's `wip/region-and-placement`, in the isolated checkout current performance/alignment continuation is **`7601aa2`**, pushed to origin's
`wip/region-and-placement`, in the isolated checkout
`/home/bob/repos/iris-layout-experiment`; **it is not on PR #18**. The original `/home/bob/repos/iris-layout-experiment`; **it is not on PR #18**. The original
checkout is unchanged. Keep the experiment's build directory separate: Cargo checkout is unchanged. Keep the experiment's build directory separate: Cargo
can accept a different worktree's artifacts as fresh when a target directory can accept a different worktree's artifacts as fresh when a target directory
@@ -436,7 +437,7 @@ report must not replace the reference against which that report was obtained.
Text wraps at the available extent, not the whole fraction reference. Text wraps at the available extent, not the whole fraction reference.
**The offer includes availability.** A span can keep the same reference frame **The offer includes availability.** A span can keep the same reference frame
while offering a text less room after an earlier sibling. `known_len` therefore while offering a text less room after an earlier sibling. `measure_len` therefore
accepts the placement as well as the frame. The first measurement's accepts the placement as well as the frame. The first measurement's
`offer_placement` survives later placing evaluations, and local redraw asks `offer_placement` survives later placing evaluations, and local redraw asks
that original question before restoring the assigned slot. Frame-length that original question before restoring the assigned slot. Frame-length
@@ -484,52 +485,96 @@ at arbitrarily wider widths. New tests compare actual primitive and mask
geometry and assert reuse for fixed-frame resizing and widening unwrapped geometry and assert reuse for fixed-frame resizing and widening unwrapped
text (including explicit newlines and empty content). text (including explicit newlines and empty content).
**Measure the offer without placing an intermediate answer.** `Painter::measure_len`
replaces the hint/cache query and its duplicated drawing fallback. It still runs
`Widget::draw` when needed, but leaves that drawing in the offer until the parent
assigns the child's slot. Placing the child's own answer first was wasted work:
the parent immediately replaced that placement. The regression counts three
rather than four evaluations for a numeric-size leaf in a span, checks its actual
primitive bounds, and repeats after resize. There is no second layout body on
`Widget`. `Holds::ANY.through(...)` now returns `ANY` directly: an unrestricted
range needs no inverse division, including for a negative fraction.
**A chosen slot does not cancel a child's alignment.** Bryan caught this in the
`tabs` image: a fixed 100 px square under a flexible wrapper sat at the slot's
near edge. `ask_box` now aligns a declared frame inside the chosen slot, just as
it does inside an unassigned offer. A regression asserts the actual coordinates
before and after resize. Warm/cold equality could not find this because both
were wrong. The corrected 900 px image still has 88 px between blue and red and
78 px between red and orange: the enclosing `.pad(10)` adds 10 px before the
red slot. Red is centered in that slot, 5 px right of the visible black gap's
center. Do not describe those two centers as the same thing or compensate with
an arbitrary offset. The parked padding API change is still separate.
Ordinary verification passes: formatting, workspace clippy with Ordinary verification passes: formatting, workspace clippy with
`layout-diagnostics`, workspace tests, and the fast generated cases. The `layout-diagnostics`, workspace tests (100 suite tests and 21 core tests), and
suite contains 98 tests. A `tabs` resize from 1920x1200 the fast generated cases. The corrected `tabs` resize from 1920x1200 to
to 900x1200 matches a cold 900x1200 screenshot with zero differing pixels. 900x1200 matches a cold image with zero differing pixels. The release oracle
With exact equality, the final release oracle passes 2000 seeds at depth 4 passes 2000 seeds at depth 4 and 1000 at depth 6; the shrinker passes 400 at
and 1000 seeds at depth 6, the shrinker passes 400 seeds at depth 5, and the depth 5, and the debug oracle passes 120 at depth 4 with assertions enabled.
debug oracle passes 120 seeds at depth 4 with assertions enabled. Every scan Every scan runs all fifteen scenarios with exact equality. The measurement
runs all fifteen scenarios. The fixed-frame reuse regression was also run regression fails with four draws instead of three when the optimization is
against `efb416b`: it fails there with two child draws instead of one. disabled.
**Do not land this as a performance improvement.** The old stretching and **Still not ready to replace PR #18.** The prototype does substantially more
wrapper reuse tests pass, and one numeric-size test improves from three draws nested layout work than `e44dea3`, even after removing intermediate placements
to two. The continuation reduces widget draws from `efb416b` by 6.9% cold, (seed 1, depth 8):
9.0% with many dirty widgets, 7.6% for a size change, and 8.7% for resize.
Against the last working `e44dea3`, it still does substantially more nested
layout work (seed 1, depth 8):
| phase | widget draws before | prototype | primitive writes before | prototype | | phase | widget draws at e44dea3 | current | primitive writes at e44dea3 | current |
| --- | ---: | ---: | ---: | ---: | | --- | ---: | ---: | ---: | ---: |
| cold | 369 | 498 | 9635 | 9704 | | cold | 369 | 484 | 9635 | 9179 |
| repaint | 1 | 1 | 1 | 1 | | repaint | 1 | 1 | 1 | 1 |
| many | 157 | 282 | 4721 | 6398 | | many | 157 | 274 | 4721 | 5873 |
| size | 16 | 254 | 106 | 5725 | | size | 16 | 247 | 106 | 5200 |
| scroll | 2 | 2 | 0 | 0 | | scroll | 2 | 2 | 0 | 0 |
| resize | 13 | 300 | 0 | 6255 | | resize | 13 | 292 | 0 | 5730 |
These are instrumented work counts, not elapsed-time speedups (50 frames per These are instrumented work counts, not speedups (50 frames per phase except
phase except cold, which is one; 578 generated widgets, 191 finally active). cold, which is one; 578 generated widgets, 191 finally active). Against
Uninstrumented release binaries, run directly with `perf stat -r 3 -e `2ed5503`, the changes remove 7 widget draws and 525 primitive writes per size
instructions:u`, also improve over `efb416b`: 2000 size-change frames fall frame, and 8 draws and 525 writes per resize frame.
from 14.3923B to 13.8914B instructions (3.5%); 2000 resize frames fall from
16.9756B to 16.1331B (5.0%). These whole-process totals include fixture setup;
reported run-to-run variation is at most 0.24%. The many-dirty workload falls
from 16.7962B to 16.0030B instructions (4.7%, 2000 frames). Repaint and scroll
keep their draw counts but incur small CPU increases: 0.8849B to 0.8913B
(+0.7%) and 2.4332B to 2.4479B (+0.6%) over 100,000 frames each. These are
CPU-work measurements, not phone frame times. Keeping measurement validity separate from final drawing validity
and deferring to an already dirty parent were also tried; neither improved
this rig's draw counts, so those changes were removed.
Raw placement reads in containers and repeated offer/placement evaluations **Measure cycles as well as instructions** (Bryan, 2026-09-17). The following
remain conservative. Retaining local coordinates also costs memory per are whole-process medians from seven alternating before/after runs of direct,
primitive. The experiment demonstrates selective reuse with one `Widget::draw` uninstrumented release test executables under `perf stat -e
body, not a finished performance replacement. Optimize the container protocol cycles:u,instructions:u`. The baseline is `39f7b08`: `2ed5503` with the same
under the same correctness tests before adopting it. The parked `Inset`/`Outset` centering fix, so both sides draw the corrected layout. CPU-heavy scans were stopped
API changes are not included in this experiment. during measurement. Counts include fixture setup; size, resize and many run
5000 frames, repaint and scroll run 1,000,000. These are VM CPU measurements,
not phone frame times.
| phase | cycles before → after (billions) | instructions before → after (billions) |
| --- | ---: | ---: |
| size | 12.8008 → 11.0344 (-13.8%) | 34.3660 → 29.9450 (-12.9%) |
| resize | 14.9368 → 13.0376 (-12.7%) | 39.8487 → 35.0032 (-12.2%) |
| many | 14.8875 → 12.9579 (-13.0%) | 39.6021 → 34.7499 (-12.3%) |
| repaint | 2.7416 → 2.6626 (-2.9%) | 6.0266 → 6.0197 (-0.1%) |
| scroll | 9.7848 → 8.4829 (-13.3%) | 21.7203 → 20.9527 (-3.5%) |
The size/resize/many cycle samples have nonoverlapping before/after ranges,
with each range less than 1.3% of its median. Additional depth-6 fixtures,
10,000 frames and seven alternating pairs each: seed 3 improves cycles by
7.0% for size and 8.4% for resize; seed 13 improves resize by 9.2%, while its
size samples overlap and establish no cycle improvement.
**The remaining gap has not been established as an unavoidable correctness
cost.** A temporary failure trace of one frame on seed 1, depth 8 counted
39/108 failed reuse attempts in size, 65/151 in resize, and 45/109 in many
where only the raw placement snapshot differed: both numeric frame and extent
ranges still held. Containers read that snapshot to construct child positions,
so discarding the check would be wrong; a way to retain those child positions
relative to the extent is the next protocol question. There is also necessary
width-dependent work: the hot text at widget 199 visits 68 px and 89.53613 px
and reports different heights (316.80078 and 228.80078). It is evaluated
repeatedly at those widths, so that fact alone does not justify all the repeats.
Keeping measurement validity separate from final drawing validity and deferring
to an already dirty parent were previously tried without improving draw counts.
Measuring first in local redraw and then restoring its slot saved only one draw
in `many` and added another request on simple repaints; that trial was removed.
Retaining local coordinates still costs memory per primitive. Optimize under
the exact oracle before adopting the prototype; the `Inset`/`Outset` changes
remain parked.
## How layout is decided ## How layout is decided
+4 -1
View File
@@ -876,7 +876,10 @@ so reuse composes positions in the same order as a fresh draw instead of
recovering fractions by division. The experimental oracle requires exact recovering fractions by division. The experimental oracle requires exact
pixel-region equality. `Widget::draw` remains the only layout body. The pixel-region equality. `Widget::draw` remains the only layout body. The
prototype has significant container-update costs and is not yet adopted into prototype has significant container-update costs and is not yet adopted into
the pinned framework. the pinned framework. A container's length query can evaluate the offered extent
without first placing the child's reported size; the parent's subsequent draw
assigns the final slot using the same `Widget::draw` body. A parent-selected slot
still honors a fixed child's alignment within that slot.
**A `Span` leaves its children in the offered orthogonal region unless **A `Span` leaves its children in the offered orthogonal region unless
explicitly compacted** (2026-09-12). It still reports the widest orthogonal explicitly compacted** (2026-09-12). It still reports the widest orthogonal