Record the request arena, which replaces the second expression shape

This commit is contained in:
iris-ai committed 2026-09-20 21:07:56 -04:00
1 parent 5f01dc65c8
commit a7da12a8ff
2 files changed
+82 -4

No files matched your search

+61
View File
@@ -6,6 +6,67 @@ nothing here is rediscovered. Each entry says who found it and when.
it (settled design, the measurement method) belongs in `docs/LAYOUT.md`, and
the current plan is in `docs/HANDOFF.md`.
## One node type for a request, and no refcount (2026-09-20)
`05e6ced`. `SizeRequest` was a second expression shape beside the one the
layout pass already had, and the `Arc` in it was how that showed. Bryan asked
the questions in this order: it is not exposed outside widget code, so it must
not be an `Arc`; why is it not a `Box`; where are the clones and can they be
references; is the tree duplicating one that exists; and ideally an arena,
because it is a single type.
**What was duplicated.** `SizeRequest` held `Sum`/`Min`/`Max` over
`Arc<(Self, Self)>`. `RequestArena` held the same three operators as
`Op` plus `Node { op, a, b }` in a `Vec`, reached through
`RequestedLen::Deferred { index, epoch, leftover }`. The constant fold was
written twice -- `SizeRequest::min`/`max`/`Add` and `RequestArena::combine`
both summed two linears, both resolved a comparison through
`independent_order`, and both collapsed equal operands -- and
`RequestArena::import` existed only to walk the first and rebuild it as the
second, recursively, at every ask.
**What it is now.** One `Node`, one `Op`, one fold in `Nodes::combine`, and
two kinds of owner: a rule's expression holds a small arena for as long as the
rule lasts, and `RequestArena` holds the pass's. `import` is
`Nodes::graft`, which copies nodes between two arenas with each length passed
through a `resolve` closure -- `within_len(base)` when importing, the identity
when the builder joins two expressions. A node's operand is an `Operand`, a
number within its own arena; `RequestedLen` is that plus the epoch saying
which pass numbered it, and is the only form that leaves an arena. The epoch
is therefore checked once where a handle comes back in (`RequestArena::operand`)
rather than at every level of the walk it starts, which also catches a stale
handle at `combine` rather than waiting for `segment`.
**No refcount replaces the `Arc`.** `SizeRequest::Linear(LayoutLen)` stays
inline and only `Expr(Box<Expr>)` allocates, so `size_of::<SizeRule>()` is 40
before and after -- the arena costs a widget nothing, because nearly every
rule holds a plain length. The only clones of a request in the framework were
`SizeRule::at_least` and `at_most`, and both read a rule out of a slot, moved
one end of its bound, and wrote it back into the same slot;
`Widgets::edit_bound` does that where it sits, and the two are gone. Every
read was already a reference (`size_rules` borrows, `deferred` returns
`Option<&SizeRequest>`, `import` takes `&SizeRequest`), and `WidgetData` is
not `Clone`. What is left is `src/random.rs`'s shrinker, where a deep copy of
a two-node expression at shrink time is nothing.
`SizeRequest` grew a `Display`, because the shrinker prints a rule and a
derived `Debug` of an arena is not something a tree can be rebuilt from. An
expression reads `min(30 px;1 leftover;, 2 leftover;)<0.5 rel;`.
**Measured**, medians of three release runs under `perf stat -e
instructions:u`, each set within 0.005% of its median: `bounds_cost`
`MODE=cap FRAMES=2000` is 5.665B against 5.743B (**-1.34%**), and
`revision_cost` resize `ROWS=40 FRAMES=500` is 4.855B against 4.893B
(**-0.79%**). Small, but consistent and well past the noise floor here.
Verified: format, workspace clippy under `-D warnings` with and without
`layout-diagnostics`, 206 ordinary and 210 diagnostic tests, the cold dump
byte-identical to `2ac0843` across all **34,986** boxes, all three seed scans
(400 at depth 5 in 64.24s, 1,000 at depth 6 in 160.35s, 2,000 at depth 4 in
298.82s), and 400 depth-5 trees in each of the three deferred-request corpora
in 205.42s. The scans were run because this replaces the allocator's data
structure, which is the one thing they exist to check.
## Eleventh sweep: the built-in bounds work (2026-09-20)
Over `2ac0843`, which no earlier round reviewed -- `SizeRule` from an enum to