docs(core): the shape-dependence is what pins the cause, not the number

The review re-ran fold_bench across two shapes at the SAME 18,050 cells: the
old code cost 51 ms at 25x361 and 244 ms at 95x95, and after the fix the two
are indistinguishable. A 4.8x spread from group count alone is what a
per-member scan predicts and a constant factor does not, so the spread
vanishing is better evidence for the diagnosis than the absolute number.

Also records the depth asymmetry between the two Agg arms, which bounds a
chain of folded formulas and must not be tidied into one rule.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Edward Langley
2026-09-20 12:30:25 -07:00
parent 22ea527837
commit ee8d4b3709
+19 -4
View File
@@ -527,10 +527,25 @@ first version, above — made every member's `Ref` miss the cache and fall into
`aggregate_raw`, one poorly-narrowed scan **per member**: 51.1–53.3 ms at the
same 18,050 cells and superlinear with it (45× the cells cost 85× the time).
So the divergence and the stall were **one defect**, and fixing the first fixed
the second. The fixed point runs exactly **2** passes (measured, not assumed),
so hoisting the member set out of the loop has a 2× ceiling on the fold's share
and was declined: a memo is state, and a memo keyed by anything less than the
whole `partial` is a second answer to "what does this cell depend on".
the second.
**And "18,050 cells" was not a sufficient description of that workload.** At the
same cell count the old code cost 51 ms at a 25×361 shape and 244 ms at 95×95 —
a 4.8× spread from group *count* alone, which is what a per-member scan predicts
and a constant factor does not. After the fix the two shapes are 16.9–17.2 ms
and 16.4–17.5 ms: indistinguishable. **The shape-dependence being gone is the
confirmation of the cause**, more than the absolute number is. Re-measuring this
means stating the shape, not just the cells.
The fixed point runs exactly **2** passes (measured, not assumed), so hoisting
the member set out of the loop has a 2× ceiling on the fold's share and was
declined: a memo is state, and a memo keyed by anything less than the whole
`partial` is a second answer to "what does this cell depend on".
One asymmetry not to tidy: the cached path hands `eval_expr` a fresh
`MAX_EVAL_DEPTH` at each `Agg`, while the uncached `Agg` threads its own `depth`
through. The second is what bounds a chain of folded formulas; making them agree
by resetting both would unbound it.
**`Expr::Is(Filter)` — a coordinate predicate as a number.** `Cat IS Item` is
`1.0` when the cell's own `Cat` coordinate is `Item` and `0.0` otherwise. It