perf(chat): batch mounted row height reads before placement

Merged
TE
3d ago
+139-17

Rows entering a fast transcript fling read their heights between sibling content layout effects. Those siblings can write layout, making the next row force layout again. Queue only dirty row measurements and read them together after sibling effects, immediately before the existing pre-paint placement correction. Preserve visible content, measured-height correction, overscan and ResizeObserver behavior.

Leo: “i feel like the app is slow on scrolling thru stuff... claude desktop is 120hz or something and ours is wack + it has sometimes not even rendered stuff!?”

The existing transcript-fling fixture now checks sibling-write/read ordering, first-painted row geometry, changed versions and removed rows. The queue drains each commit; effect cleanup removes obsolete entries. No timer, observer, animation loop or Swift logic is added. Risk: the row measurement lifecycle; fleet streaming/fold and fling coverage will verify it.

Prior art: #33460 handles code paint and cached-turn preparation and is complementary; this preserves its files. #33543's reduced speculative range was withdrawn after weak timing improvement and unchanged blanking. The settle lane was notified of the shared App.tsx path. Design remains unchanged.

Performance

Same-worker source A/B at 494a4d2e49cd89eb77bac5c58e154f47e1ffe106 versus baseline App.tsx at 7cdc87323aa445eb1918d1576a316fac2dd29a69; both variants retain the same repaired test infrastructure and harness. Two rounds shown separately. Timing and painted coverage run separately.

MetricBeforeAfterDeltaHarness or run link
Fling interval p95 (ms)104 / 100165 / 10861 / 841627980053
Fling longest frame (ms)115 / 116165 / 11450 / -241627980053
Fling React p95 (ms)79 / 62127 / 7748 / 1541627980053
Fling dropped frames/s, nominal 120 Hz106.56 / 103.574111.293 / 109.1154.733 / 5.54141627980053
Long-scroll p95 (ms)42 / 2454 / 4912 / 2541627980053
Wheel p95 (ms)24 / 2330 / 316 / 841627980053
Fling row.measure p95 (ms)18 / 180 / 0-18 / -1841627980053
Fling painted blank frames1 / 11 / 10 / 041627980053
Fling painted blank time (ms)16 / 1616 / 160 / 041627980053
Long-scroll painted blank frames0 / 00 / 00 / 041627980053
Long-scroll painted blank time (ms)0 / 00 / 00 / 041627980053
Wheel painted blank frames0 / 00 / 00 / 041627980053
Wheel painted blank time (ms)0 / 00 / 00 / 041627980053

The initial sequential A/B run above had slower interval-p95 pairs despite a lower row-height-read span. The order-balanced timing follow-up below reverses its fling regression signal. Painted blanking is unchanged. Headless values do not establish native/nightly 120 Hz. The historical native 58.3 ms p95 is context only, not this A/B baseline.

Validation: run 41627980053 passes the full web check. Baseline fails exactly the named mount-order assertion (first read after 1 write, expected at least 12); candidate passes the new browser test, full fling suite, and existing streaming/fold regressions. Fixed harness/source hashes and source restoration are recorded. The only added base repair is the exact already-merged #33545 patch; batching product files are unchanged from 8086573. Opus 5.5 medium exact-head review carries to 494a4d2. No new review is needed for unchanged code.

Order-balanced timing follow-up

Run 41631832187 uses the same worker, exact candidate/base sources and fixed 3000-row WebKit harness in A/B/B/A order, two rounds per pass. All source, harness and log hashes and final source restoration were verified. Correctness and painted-coverage evidence above is reused from run41627980053; this run measures timing only.

PassSourceFling p95 (ms)Render-to-commit p95 (ms)Row measurement p95 (ms)Long p95 (ms)Wheel p95 (ms)
1A, baseline133 / 110100 / 8023 / 2142 / 3823 / 23
2B, candidate105 / 11277 / 761 / 043 / 4424 / 24
3B, candidate108 / 9881 / 681 / 143 / 4323 / 24
4A, baseline135 / 12694 / 8321 / 2044 / 4424 / 24
MetricBeforeAfterDeltaHarness or run link
Fling p95, passes 1→2 (ms)133 / 110105 / 112−28 / +241631832187
Fling p95, passes 4→3 (ms)135 / 126108 / 98−27 / −28same run
Long p95, passes 1→2 (ms)42 / 3843 / 44+1 / +6same run
Long p95, passes 4→3 (ms)44 / 4443 / 43−1 / −1same run
Wheel p95, passes 1→2 (ms)23 / 2324 / 24+1 / +1same run
Wheel p95, passes 4→3 (ms)24 / 2423 / 24−1 / 0same run

The fling improves in three of four adjacent order-balanced comparisons; the exception is +2 ms. Long and wheel results are mixed and overlap the ending baseline, so the earlier broad slowdown does not persist. This supports the narrow batching change, not a claim of uniform improvement or statistical certainty. Each value remains an individual round's p95; none are pooled. react in the raw output measures render-to-commit elapsed time (including child effects and forced layout), not isolated React CPU time. Geometry and measurement spans overlap and are not a percentage cost decomposition.

Gallery layout-shift failures are attributed to the shared flake tracked in #33516; the exact-source diagnostic rebuild matches base, and the isolated scenes do not mount this scroller. No thresholds were changed. The known blanking is unchanged and this does not demonstrate native or nightly 120 Hz.

Final red attribution: the phone-lane static check and both warm-source errors fail identically on replay base eb7026a16d448775ab4b1b0230d529ab4ebb4ec3. All eight webview verdicts pass.

🤖 Generated with Codex

Agent session: agent session