test(daemon): guard live paint allocation budget
- commit
- f9d041e35b12d128a383811fbe17f9f5b17709ce
- author
- brice · · 2026-07-12 05:52 UTC
- parent
- c0fdb61
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· 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 | |||
| 220 | 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 | 246 | func TestPerformanceFixturePaintLiveUsesPrecomputedAlternatingWrites(t *testing.T) { | |
| 224 | 247 | fixture := newPerformanceFixture(t, performanceConfig{}) | |
| 225 | 248 | ||
| @@ -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 | ||||