Summary
vt.Scrollback.Push allocates a fresh slice for every line pushed via slices.Clone(line[:lastNonEmpty+1]).
In a profile from an app embedding x/vt for multiple panes, that per-push cloning accounts for roughly 71% of heap usage: about 97 MB with 11 panes active. DefaultScrollbackSize is already capped at 10,000, so the dominant cost here is not unbounded retention, but allocation churn on every push.
Current code
In scrollback.go:
func (s *Scrollback) Push(line uv.Line) {
...
cloned := slices.Clone(line[:lastNonEmpty+1])
...
s.lines = append(s.lines, cloned)
}
This means every scrolled line allocates a new backing array, even when line widths and write patterns are highly repetitive.
Why this matters
- The allocation happens on every push, not just when the scrollback grows.
- With several live panes, the heap cost becomes significant even at the default scrollback size.
- The profile suggests this path is the dominant heap consumer in realistic multi-pane terminal workloads.
Possible improvements
- Reuse line buffers from a
sync.Pool keyed by approximate line capacity.
- Introduce a copy-on-write strategy so unchanged/stable lines avoid immediate full clone costs.
- Store scrollback lines in a reusable slab/ring representation instead of one fresh slice per push.
I realize Push likely clones to avoid aliasing the render buffer, so some copy is necessary unless ownership changes. The request is mainly to reduce the per-push allocation pressure, not to eliminate safety.
Expected outcome
Anything that reduces fresh allocations in Scrollback.Push would help. Even a pooled-buffer approach behind the existing API could materially lower heap churn for applications with many concurrent emulators.
If useful, I can follow up with a benchmark or heap profile excerpt from the embedding app.
Summary
vt.Scrollback.Pushallocates a fresh slice for every line pushed viaslices.Clone(line[:lastNonEmpty+1]).In a profile from an app embedding
x/vtfor multiple panes, that per-push cloning accounts for roughly 71% of heap usage: about 97 MB with 11 panes active.DefaultScrollbackSizeis already capped at 10,000, so the dominant cost here is not unbounded retention, but allocation churn on every push.Current code
In
scrollback.go:This means every scrolled line allocates a new backing array, even when line widths and write patterns are highly repetitive.
Why this matters
Possible improvements
sync.Poolkeyed by approximate line capacity.I realize
Pushlikely clones to avoid aliasing the render buffer, so some copy is necessary unless ownership changes. The request is mainly to reduce the per-push allocation pressure, not to eliminate safety.Expected outcome
Anything that reduces fresh allocations in
Scrollback.Pushwould help. Even a pooled-buffer approach behind the existing API could materially lower heap churn for applications with many concurrent emulators.If useful, I can follow up with a benchmark or heap profile excerpt from the embedding app.