From 663a95f4a25bd17768379512e3142a84a4c20dea Mon Sep 17 00:00:00 2001 From: iris-ai <4+iris-ai@noreply.localhost> Date: Wed, 16 Sep 2026 14:23:22 -0400 Subject: [PATCH] Record what wrapping saved, and the rounding question left open Co-Authored-By: Claude Fable 5.1 --- docs/HANDOFF.md | 46 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index b54ebe0..a57ca66 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -8,7 +8,7 @@ log. Canonical Iris `main` is **`ca2b4b2`** (#17, the headless rig). **#18 `split/18-position-chain`** is open in `/home/bob/repos/iris-pr18`; its local -head is **`394d514`**, seventy commits. Built-in alignment is complete there; +head is **`4febabf`**, seventy-one commits. Built-in alignment is complete there; see "Built-in alignment" below for the retained-layout details. No PR reviews were present when checked on 2026-09-15. @@ -91,6 +91,11 @@ pin those rules. Seeds 10 and 86 are now in the ordinary generated set. ## Verification at `394d514` +`4febabf` on top of it -- `Fixed` wrapping rather than saturating -- passed +the workspace tests with all features (105), the release 100-seed oracle in +9.5 s, and rendered `random`, `tabs` and `text` byte-identical at 1920x1200 +against `394d514`. The measurements are under "Performance". + - `cargo fmt --all --check` - `cargo build --workspace --all-features` - `cargo clippy --workspace --all-targets --all-features -- -D warnings` @@ -160,9 +165,14 @@ Measured on the fixed-shape fixture, seed 1, depth 8, 500 frames of `many`: | --- | ---: | ---: | ---: | ---: | | `5ed9e87`, before fixed point | 100 | 4,272 | 1,761M | ~699M | | `4cbb242` | 97 | 3,951 | 2,093M | ~819M | +| `4febabf`, wrapping | 97 | 3,951 | 1,918M | ~771M | -So the grid costs something like a fifth to a quarter, on a head that draws -slightly less. Normalised by primitive writes it is about 1.27x. +Normalised by primitive writes the grid is now about 1.18x the float head +on instructions and 1.19x on cycles; per drawn widget it is 1.12x. What is +left is the shape of an integer multiply with a rounding against a float +fused multiply-add, spread across `UiSpan::within` (4.7% of cycles), +`remap_subtree` and `Holds::through`; nothing in the profile above those +is the grid. Text line breaking is 10% and `InstanceList::push` 9%. **Cycles are measurable on this machine and instructions alone are not enough.** Three runs of one binary varied 0.23%. It is *wall time* that @@ -200,12 +210,22 @@ Tried and rejected, with numbers, so they are not tried again: - Short-circuiting `apply_scalar` where the fraction is nought or one: +17%. (From the previous session.) -**The one lever left that is worth a decision:** `Fixed`'s `saturating_add` -and `saturating_sub`, measured at **8.1% of instructions and 2.7% of -cycles**. Wrapping instead is one instruction where saturating is five. That -trades away "a clamped coordinate keeps the ordering a wrapped one inverts", -which is a posture rather than a micro-optimisation, so it needs Bryan. -Everything else is spread thin. +- `#[inline]` on `UiSpan::within`, whose prologue and epilogue were a sixth + of its own cycles: -0.2% instructions, **+1.5% cycles**. + +**`4febabf` makes `Fixed` wrap rather than saturate** (Bryan, 2026-09-16: a +coordinate past the range is not going to draw reasonably anyway, so wrap +and break clearly): -8.6% instructions, -6.6% cycles, more on cycles than +the 2.7% the instruction share predicted. `from_f32` still clamps and +`Holds` keeps its saturating `narrow`, since a range of box lengths past +`i32` really is unbounded. + +**One posture question is still open, with its number.** `shift_round` +rounds halves away from zero so a value and its negation round to the same +distance, which is a sign branch on every multiply. Rounding halves up +instead measured **-1.6% instructions, -2% cycles** (1,888M, ~757M), and +would let a flipped span land a step from its mirror image at exact halves. +Not applied; Bryan's call. Earlier, against #18's own history: the retained rewrite took `many` from 25.17M instructions a frame at `691e3eb` to 6.14M at `29c7881`, and `resize` @@ -417,8 +437,12 @@ pixels and `Holds`. Decided with Bryan on 2026-09-15. - A weight is not a fraction: a list divides its room by the total of its weights, so `Weight` trades precision for the range to hold a whole list, and `Rel::ratio` turns two weights into a share on the finer grid. -- **Arithmetic saturates rather than wrapping**, because a clamped coordinate - keeps the ordering a wrapped one inverts. +- **Arithmetic wraps, as of `4febabf`.** It saturated first, so that a + clamped coordinate kept the ordering a wrapped one inverts, and that cost + a twelfth of layout's instructions to keep order two million pixels out + where nothing draws. `MIN` and `MAX` still stand in for an unbounded end, + which is safe only because every use compares against them and none adds + to them; `from_f32` is the one operation that clamps. - `Px` was `1/64` first. The residue of a length reached two ways is one rounding, so it scales with the step: at `1/64` that was 0.016 px, enough to move a box, and at `1/1024` it is a thousandth of a pixel. Range is