perf(text): size the line numbers in one layout, not one per line - #696
Merged
Conversation
`updateLineNumberHeight` set a gutter cell's height and then read the next line's `getBoundingClientRect()`, so every read forced the layout the write before it had invalidated - one full layout of the whole document per line. Read every height first, then write them. Measured in a WebView on a Pixel 9 Pro emulator, a 1 MB text file (7.8k lines): | | ready | in this function | |---|---|---| | before | 38.2s | 37.6s | | after | 0.26s | 6ms | The cost is linear in the number of lines from here: 10 MB takes 3.0s and 25 MB 6.7s, both of which are the browser parsing and laying out two elements per line rather than anything this function does. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TouAQNfktsp9THcceennEX
andiwand
force-pushed
the
perf/text-line-number-layout
branch
from
August 18, 2026 05:49
84ba844 to
5ff01ce
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A megabyte of plain text took 38 seconds to appear in a WebView, of which 37.6 seconds was spent inside one function.
The bug
text.js'supdateLineNumberHeightinterleaves a write and a read:Setting
style.heightinvalidates layout; thegetBoundingClientRect()on the next iteration forces it again. With one gutter cell per line that is one full layout of the entire document per line.Every height is read first, then written. Same output, one layout.
Numbers
Measured in a WebView on a Pixel 9 Pro emulator, timing wall clock to a stable DOM and accumulating the time spent inside this function. 1 MB of plain text, 7,823 lines:
updateLineNumberHeightBatching is indistinguishable from not doing the work at all. Repeated back to back to rule out warm-up: 29.1s / 0.19s / 30.2s.
Cost is linear in line count from here, and what remains is the browser, not this function:
Not in this PR
Two elements per line, half of them the gutter, costs roughly 4.6–5.4 KB of renderer memory each. At 50 MB and 100 MB the renderer is killed by the low-memory killer (2.7 GB RSS) and takes the app process with it — the engine itself is fine there, writing 121 MB of html in 2.5s. Replacing the parallel gutter column with a CSS counter would halve both the element count and the memory and remove this script; virtualising the line list is what would actually lift the ceiling. Neither is here.
Test
No test: this is a rendering performance fix with identical output, and the reference-output suite compares rendered pages. Verified by the measurements above.
🤖 Generated with Claude Code