Four commits now apply the off-screen row treatment; only the first was A/B'd. Records
which is which, and warns how to read a result for the three widgets that do not overdraw
on the test backup - a measurement there will correctly show no change, because the benefit
only exists once an integration returns more rows than the tile can show.
Also records two things that look like gaps and are not:
- content-visibility was applied to five of the 26 ScrollArea widgets on purpose. It
costs containment work and can be wrong on self-measuring content such as charts and
the notebook, so the rule was: apply where list length is decided by remote data.
- React cache() is server-only. It already wraps getBoardByNameAsync, getHomeBoardAsync,
getRscServerSettingsAsync and getQueryClient; it does nothing for client widget
rendering, where react-query does the caching and its gcTime was already verified at
5 minutes rather than the 24 hours the original fix catalogue assumed.
Records where this stopped so it can be picked up cleanly:
1. c526dd2b7 (media-requests content-visibility) is typechecked but NOT measured - the
only performance claim on this branch not A/B'd before commit. Verify with
measure-board-paint.mts, three repeats; revert if image requests do not fall.
2. The one lever with headroom left is the tRPC router's eager integration imports - a
16.10 MiB subtree of 460 files reached by every API route, which pays twice because
both the ~41 MiB of retained source and the ~38 MiB of V8 compiler zones scale with
how much JavaScript the server compiles. Every allocator and V8-flag avenue is closed.
3. Deferred with prizes attached: ASCII-escaping bundles (9.45 MiB, needs its own PR),
lazy mysql2/pg (21.4 MiB, structurally blocked), beszelSystemStats chart SVG.
4. The proposed set for the clean PR, six changes producing -26% peak anon and -52% at
idle for +18% CPU - with the gRPC patch flagged as hygiene rather than a measured win,
and the +30% board-load figure flagged as not established.
PHASE2-FINDINGS.md had grown past 1,200 lines mixing server memory, client rendering,
methodology and a long list of rejected experiments, which made it unusable as a
reference. Split under docs/perf/:
README.md headline results and the change-by-change ratio table
SERVER-MEMORY.md what the container's RAM is made of, layer by layer
CLIENT-PERFORMANCE.md dashboard cost in Chrome: DOM, fibers, images, paint
REJECTED.md ideas implemented, measured and abandoned, with numbers
METHODOLOGY.md the tools, and the measurement traps that gave wrong answers
PHASE2-FINDINGS.md is now an index so existing links keep working.
METHODOLOGY.md is the part worth keeping: every wrong number in this investigation came
from an instrument rather than the application, and each trap is written down - snapshots
cannot explain peaks, V8 truncates snapshot strings to ~1 KiB, snapshot objects are not
live handles, measuring without a forced GC measures V8's growth, taking a CDP snapshot
added 134 MiB to the process being measured, per-component React attribution silently
returns nothing on a production build, a fixed port can measure a leftover container, and
a server-side change that moves a client-side metric means the workload changed.