v0.6.1 flush: memcmp finds a row's first and last changed cells

The whole-row memcmp settled an unchanged row, but on a changed row the cell
loop then read again the prefix memcmp had already compared, and the trailing
trim walked unchanged cells one by one. Chunks of eight cells now find the
first and last changed cell, so no cell is compared twice and only the span
between them is scanned cell by cell. Output is byte-identical.

Measured on frames captured from vif at 60 fps (median of 15, one core):
vanguard 120x40 73.7 -> 66.6 us a frame (-10%), vanguard 240x66
158.8 -> 131.8 (-17%), main with the gun firing 40.0 -> 34.1 (-15%), main
idle 20.3 -> 14.0 (-31%). Dropping the row compare instead costs +15% to
+120% on the same frames.
This commit is contained in:
Lixen authored and GitHub committed 2026-10-28 22:03:41 -04:00
1 parent bc22a9bf00
commit df94556101
3 files changed
+59 -14

No files matched your search

+20
View File
@@ -3,6 +3,7 @@ package terminal
import (
"bytes"
"cmp"
"math/rand/v2"
"slices"
"strings"
"sync"
@@ -133,6 +134,25 @@ func TestUnchangedFrameWritesNothing(t *testing.T) {
}
}
// Every cell a frame changes is written, wherever in its row it lies and however
// wide the row: after a flush the screen the terminal holds is the frame
func TestEveryChangedCellIsWritten(t *testing.T) {
rng := rand.New(rand.NewPCG(1, 2))
for _, width := range []int{1, 7, 8, 9, 17, 120} {
o := newOutputBuffer(ColorModeTrueColor)
frame := make([]Cell, width*4)
for range 200 {
for range rng.IntN(len(frame)/2 + 1) {
frame[rng.IntN(len(frame))] = Cell{Rune: 'a' + rng.Int32N(4), Fg: color.RGB{G: uint8(rng.IntN(2))}}
}
o.flush(frame, width, 4)
if !slices.Equal(o.front, frame) {
t.Fatalf("width %d: a changed cell went unwritten", width)
}
}
}
}
// A paste arrives as one event, whatever reads split it into: an escape
// inside it is text even when a poll times out on it, and a paste the
// terminal never ends ends when input pauses, whether reads time out or