detached f9d041e

test(daemon): guard live paint allocation budget

commit
f9d041e35b12d128a383811fbe17f9f5b17709ce
author
brice · · 2026-07-12 05:52 UTC
parent
c0fdb61
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
1 file · +23 -0
M internal/usecase/daemon/performance_benchmark_test.go +23 -0
@@ -220,6 +220,29 @@ func benchmarkReportMetrics(b *testing.B, metrics performanceMetrics, operations
220220 b.ReportMetric(float64(historyRows), "historyrows/pane")
221221 }
222222
223+// Root-cause hypothesis (verified by this guard before the fix): S2 allocates
224+// one visible VT-frame copy during capture plus a new composed frame and its
225+// base-frame clone for every live paint. Those client-sized copies, rather
226+// than history, account for the ~1 MiB/op regression in the pinned benchmark.
227+func TestLivePaintAllocationBudget(t *testing.T) {
228+ for _, tt := range []struct {
229+ name string
230+ panes int
231+ }{
232+ {name: "1tab-1pane", panes: 1},
233+ {name: "1tab-4panes", panes: 4},
234+ } {
235+ t.Run(tt.name, func(t *testing.T) {
236+ fixture := newPerformanceFixture(t, performanceConfig{size: domain.Size{Cols: 120, Rows: 40}, panes: tt.panes, historyRows: 10_000})
237+ allocs := testing.AllocsPerRun(20, func() {
238+ fixture.paintLive()
239+ fixture.ac.ackOutputState(fixture.ac.output.next)
240+ })
241+ require.LessOrEqual(t, allocs, float64(60), "live paint must reuse bounded attachment-owned visible-frame buffers")
242+ })
243+ }
244+}
245+
223246 func TestPerformanceFixturePaintLiveUsesPrecomputedAlternatingWrites(t *testing.T) {
224247 fixture := newPerformanceFixture(t, performanceConfig{})
225248
@@ -220,6 +220,29 @@ func benchmarkReportMetrics(b *testing.B, metrics performanceMetrics, operations
220 b.ReportMetric(float64(historyRows), "historyrows/pane")220 b.ReportMetric(float64(historyRows), "historyrows/pane")
221 }221 }
222 222
223+// Root-cause hypothesis (verified by this guard before the fix): S2 allocates
224+// one visible VT-frame copy during capture plus a new composed frame and its
225+// base-frame clone for every live paint. Those client-sized copies, rather
226+// than history, account for the ~1 MiB/op regression in the pinned benchmark.
227+func TestLivePaintAllocationBudget(t *testing.T) {
228+ for _, tt := range []struct {
229+ name string
230+ panes int
231+ }{
232+ {name: "1tab-1pane", panes: 1},
233+ {name: "1tab-4panes", panes: 4},
234+ } {
235+ t.Run(tt.name, func(t *testing.T) {
236+ fixture := newPerformanceFixture(t, performanceConfig{size: domain.Size{Cols: 120, Rows: 40}, panes: tt.panes, historyRows: 10_000})
237+ allocs := testing.AllocsPerRun(20, func() {
238+ fixture.paintLive()
239+ fixture.ac.ackOutputState(fixture.ac.output.next)
240+ })
241+ require.LessOrEqual(t, allocs, float64(60), "live paint must reuse bounded attachment-owned visible-frame buffers")
242+ })
243+ }
244+}
245+
223 func TestPerformanceFixturePaintLiveUsesPrecomputedAlternatingWrites(t *testing.T) {246 func TestPerformanceFixturePaintLiveUsesPrecomputedAlternatingWrites(t *testing.T) {
224 fixture := newPerformanceFixture(t, performanceConfig{})247 fixture := newPerformanceFixture(t, performanceConfig{})
225 248

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close