perf(buffer): rotate lines instead of copying cells when scrolling - #142
Open
YLzone wants to merge 2 commits into
Open
perf(buffer): rotate lines instead of copying cells when scrolling#142YLzone wants to merge 2 commits into
YLzone wants to merge 2 commits into
Conversation
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.
Problem
Buffer.DeleteLineArea/Buffer.InsertLineAreacopy every cell of the area one by one. For the common case — a terminal scrolling by one line — that isO(width × height)Cellstruct copies per scrolled line, even though the content of the lines does not change; only their order does.I ran into this while profiling a
charmbracelet/x/vtemulator fed with a plain-text stream:DeleteLineAreawas the top hotspot by a wide margin, because every\non the bottom row copies the whole buffer.Change
Add a fast path to both methods: when the area spans the full width of every affected line and the fill cell is a plain single-width cell, rotate the line slices instead of copying cells. Per scrolled line this turns
O(width × height)cell copies intoO(height)slice-header moves plus oneO(width)blank fill of the recycled line.The
canRotateLinesguard keeps the existing cell-by-cell path wherever rotation would not be equivalent:Width != 1), which needs the zero-width placeholder fix-ups inLine.Set;Buffer.Width()reports, since it readslen(b.Lines[0]).Benchmarks
One line scrolled off the top of a full buffer (
go test -bench . -count 6, Go 1.25):0 allocs/op before and after. The win grows with the buffer size because the old cost is
O(width × height)and the new one isO(height + width).Tests
InsertLineArea/DeleteLineAreahad no test coverage, so this PR adds some: full-buffer scroll, multi-line insert/delete, clamping whennexceeds the area, scroll regions, a styled fill cell, a narrow sub-width area (which must keep taking the slow path), and a wide fill cell whose placeholders must still be written throughLine.Set.Those tests pass unchanged on
mainbefore the fast path is applied, so they characterise existing behaviour rather than new behaviour.go test ./...passes with the change.