Replace a claim measured through the software rasteriser

Two places said the history cushion's evidence was a swipe that moved
nothing for 689ms. That reading came from `ui-trace show` on a row taller
than the viewport, which reports clipped bounds and so prints "nothing
moved" for a list that is scrolling fine -- and it was taken on the software
renderer besides. The finding it supported is sound and has a better
witness: counted at the server, ten swipes asked for ten pages before the
change and three after, which does not depend on how anything renders.

Also records what the screen actually costs now, measured on the GPU
emulator: 5.2-5.9% janky frames and 0-2 slow UI-thread frames flinging fast,
against 3.3% for the stock Settings app on the same device. With the note
that matters for repeating it -- let the screen settle before resetting
gfxinfo, because the first seconds after opening a session are every row
being composed for the first time and read three times worse.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Opus 5 committed 2026-08-30 20:35:54 -04:00
1 parent 0dc248b9e4
commit 3da0f2e2f6
2 files changed
+23 -6

No files matched your search

+18 -3
View File
@@ -477,9 +477,24 @@ machine belongs in `~/.claude/TOOLCHAIN.md` (toolchain versions) or
rows has to measure a screen rather than name a number: the history rows has to measure a screen rather than name a number: the history
cushion was eight rows, which on a tool-heavy transcript is less than one cushion was eight rows, which on a tool-heavy transcript is less than one
screenful, and the reader hit the end of what was loaded on every swipe screenful, and the reader hit the end of what was loaded on every swipe
and stood there for a round trip. That was "scrolling is laggy" -- not a and stood there for a round trip. It is `HISTORY_SCREENS` viewports now,
slow frame. It is `HISTORY_SCREENS` viewports now, counted from what is counted from what is actually on screen. Measured at the server, which is
actually on screen. the one number here that does not depend on how the emulator renders:
against a 24,000-event transcript, ten swipes asked for ten pages before
and three after.
- **What the transcript screen costs to scroll, for whoever measures it
next.** Taken 2026-08-30 on the GPU emulator (`emu up` provides one; a
frame number from the software rasteriser means nothing -- see
`~/.claude/MACHINE.md`), against a real imported transcript with the debug
server at `--delay 120`. Settled and flinging fast, both into fresh
history and back through rows already drawn: **5.2-5.9% janky frames, 99th
percentile 29-32ms, 0-2 slow UI-thread frames.** The stock Settings app on
the same device is 3.3% and 38ms, so this is at the platform floor and
what is left is the emulator rather than the app. The number that is *not*
at the floor is the first few seconds after opening a session, where every
row on the way is being composed for the first time; that is inherent to a
lazy list and it is why a measurement taken before the screen settles
reads three times worse. **Settle first, then reset `gfxinfo`.**
- **Only `fetchTranscript` was off the main thread; the fold was not.** - **Only `fetchTranscript` was off the main thread; the fold was not.**
`foldEvent` returns a new list per event, so a page is that many copies of `foldEvent` returns a new list per event, so a page is that many copies of
a growing list -- fine at 80 events and about 300,000 element copies at a growing list -- fine at 80 events and about 300,000 element copies at
@@ -88,13 +88,15 @@ private val LOADING_SPINNER = 48.dp
* row is anything from one line to a page and a fixed count is therefore a distance only by * row is anything from one line to a page and a fixed count is therefore a distance only by
* accident. Eight rows was the number, and on a tool-heavy transcript eight rows is less than one * accident. Eight rows was the number, and on a tool-heavy transcript eight rows is less than one
* screen: the reader reached the end of what was loaded on *every* swipe and waited a round trip * screen: the reader reached the end of what was loaded on *every* swipe and waited a round trip
* standing there. That is what "scrolling is laggy" turned out to be -- not a slow frame, but the * standing there, which is a list running out of transcript rather than a slow frame.
* list running out of transcript, which the emulator showed as a swipe that moved nothing for 689ms
* and then jumped.
* *
* Three, so a fling lands on rows that are already there and the page after them is on its way. The * Three, so a fling lands on rows that are already there and the page after them is on its way. The
* cost of being generous is a page fetched that nobody reads; the cost of being mean is a list that * cost of being generous is a page fetched that nobody reads; the cost of being mean is a list that
* stops under a finger, and those are not the same size. * stops under a finger, and those are not the same size.
*
* Counted at the server rather than inferred from the screen, which is the only measurement here
* that does not depend on how the emulator renders: against a 24,000-event transcript at `--delay
* 120`, ten swipes asked for **ten** pages before this and **three** after.
*/ */
private const val HISTORY_SCREENS = 3 private const val HISTORY_SCREENS = 3