perf(screen): write each printable cell once
putPrintable blanked every target cell before writing it, interning the default style twice per character. Only the orphaned wide-pair halves outside the written span are blanked now, continuation checks read the stored flag without materializing a Cell, and RuneWidth returns early for printable ASCII. The differential oracle stays byte-identical. ScreenPrintableASCII 15.8 -> 44.2 MiB/s, ScrollbackFlood/history-0 15.7 -> 36.4 MiB/s, history-10000 4.9 -> 13.0 MiB/s
- commit
- 61dc24930674f861fd4db8642d43ad23e397a8ef
- author
- brice · · 2026-10-07 16:26 UTC
- parent
- 92e0c36
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
3 files · +16 -6
M core/frame.go +6 -0
| @@ -267,6 +267,12 @@ func (f Frame) Cell(x, y int) Cell { | |||
| 267 | 267 | } | |
| 268 | 268 | func (f Frame) At(x, y int) Cell { return f.Cell(x, y) } | |
| 269 | 269 | ||
| 270 | + | // IsContinuation reports whether (x,y) is the right half of a wide pair | |
| 271 | + | // without materializing the cell. | |
| 272 | + | func (f Frame) IsContinuation(x, y int) bool { | |
| 273 | + | return f.page.cells[f.offset(x, y)].flags&continuationFlag != 0 | |
| 274 | + | } | |
| 275 | + | ||
| 270 | 276 | func (f Frame) Set(x, y int, cell Cell) { | |
| 271 | 277 | index := f.offset(x, y) | |
| 272 | 278 | payloadID := f.internPayload(cell.Payload) | |
| @@ -267,6 +267,12 @@ func (f Frame) Cell(x, y int) Cell { | |||||
| 267 | } | 267 | } | ||
| 268 | func (f Frame) At(x, y int) Cell { return f.Cell(x, y) } | 268 | func (f Frame) At(x, y int) Cell { return f.Cell(x, y) } | ||
| 269 | 269 | ||||
| 270 | + | // IsContinuation reports whether (x,y) is the right half of a wide pair | |||
| 271 | + | // without materializing the cell. | |||
| 272 | + | func (f Frame) IsContinuation(x, y int) bool { | |||
| 273 | + | return f.page.cells[f.offset(x, y)].flags&continuationFlag != 0 | |||
| 274 | + | } | |||
| 275 | + | ||||
| 270 | func (f Frame) Set(x, y int, cell Cell) { | 276 | func (f Frame) Set(x, y int, cell Cell) { | ||
| 271 | index := f.offset(x, y) | 277 | index := f.offset(x, y) | ||
| 272 | payloadID := f.internPayload(cell.Payload) | 278 | payloadID := f.internPayload(cell.Payload) | ||
M core/rune_width.go +3 -0
| @@ -13,6 +13,9 @@ import "unicode" | |||
| 13 | 13 | // sequences are out of scope: combining marks report width 0 (current | |
| 14 | 14 | // behavior), ZWJ sequences are not coalesced (each rune is measured on its own). | |
| 15 | 15 | func RuneWidth(r rune) int { | |
| 16 | + | if r >= 0x20 && r < 0x7F { // printable ASCII, the common case | |
| 17 | + | return 1 | |
| 18 | + | } | |
| 16 | 19 | switch { | |
| 17 | 20 | case r < 0x20 || r == 0x7F || (r >= 0x80 && r <= 0x9F): | |
| 18 | 21 | return 0 | |
| @@ -13,6 +13,9 @@ import "unicode" | |||||
| 13 | // sequences are out of scope: combining marks report width 0 (current | 13 | // sequences are out of scope: combining marks report width 0 (current | ||
| 14 | // behavior), ZWJ sequences are not coalesced (each rune is measured on its own). | 14 | // behavior), ZWJ sequences are not coalesced (each rune is measured on its own). | ||
| 15 | func RuneWidth(r rune) int { | 15 | func RuneWidth(r rune) int { | ||
| 16 | + | if r >= 0x20 && r < 0x7F { // printable ASCII, the common case | |||
| 17 | + | return 1 | |||
| 18 | + | } | |||
| 16 | switch { | 19 | switch { | ||
| 17 | case r < 0x20 || r == 0x7F || (r >= 0x80 && r <= 0x9F): | 20 | case r < 0x20 || r == 0x7F || (r >= 0x80 && r <= 0x9F): | ||
| 18 | return 0 | 21 | return 0 | ||
M screen_write.go +7 -6
| @@ -107,7 +107,7 @@ func (s *Screen) putPrintable(r rune) { | |||
| 107 | 107 | insertDamageX := s.Col | |
| 108 | 108 | insertDamageWidth := 0 | |
| 109 | 109 | if s.insertMode { | |
| 110 | - | leftSplit := s.Col > 0 && s.frame.At(s.Col, s.Row).Continuation | |
| 110 | + | leftSplit := s.Col > 0 && s.frame.IsContinuation(s.Col, s.Row) | |
| 111 | 111 | s.frame.CopyRow(s.Row, s.Col+w, s.Col, s.frame.Width-s.Col-w) | |
| 112 | 112 | s.frame.FillRow(s.Row, s.Col, s.Col+w, renderer.BlankCell()) | |
| 113 | 113 | s.repairRow(s.Row) | |
| @@ -120,15 +120,16 @@ func (s *Screen) putPrintable(r rune) { | |||
| 120 | 120 | ||
| 121 | 121 | // Determine the range of cells actually modified, extending over any wide | |
| 122 | 122 | // pair the write lands on so no orphaned half is left behind. | |
| 123 | + | // Cells inside [Col, Col+w) are overwritten below, so only the orphaned | |
| 124 | + | // halves just outside it need blanking. | |
| 123 | 125 | lo, hi := s.Col, s.Col+w-1 | |
| 124 | - | if s.frame.At(s.Col, s.Row).Continuation { | |
| 126 | + | if s.frame.IsContinuation(s.Col, s.Row) { | |
| 125 | 127 | lo = s.Col - 1 | |
| 128 | + | s.frame.Set(lo, s.Row, renderer.BlankCell()) | |
| 126 | 129 | } | |
| 127 | - | if right := s.Col + w; right < s.frame.Width && s.frame.At(right, s.Row).Continuation { | |
| 130 | + | if right := s.Col + w; right < s.frame.Width && s.frame.IsContinuation(right, s.Row) { | |
| 128 | 131 | hi = right | |
| 129 | - | } | |
| 130 | - | for x := lo; x <= hi; x++ { | |
| 131 | - | s.frame.Set(x, s.Row, renderer.BlankCell()) | |
| 132 | + | s.frame.Set(right, s.Row, renderer.BlankCell()) | |
| 132 | 133 | } | |
| 133 | 134 | s.frame.Set(s.Col, s.Row, renderer.Cell{Rune: r, Style: s.Style}) | |
| 134 | 135 | s.buffer.content(s.Row, s.Col+w) | |
| @@ -107,7 +107,7 @@ func (s *Screen) putPrintable(r rune) { | |||||
| 107 | insertDamageX := s.Col | 107 | insertDamageX := s.Col | ||
| 108 | insertDamageWidth := 0 | 108 | insertDamageWidth := 0 | ||
| 109 | if s.insertMode { | 109 | if s.insertMode { | ||
| 110 | - | leftSplit := s.Col > 0 && s.frame.At(s.Col, s.Row).Continuation | 110 | + | leftSplit := s.Col > 0 && s.frame.IsContinuation(s.Col, s.Row) |
| 111 | s.frame.CopyRow(s.Row, s.Col+w, s.Col, s.frame.Width-s.Col-w) | 111 | s.frame.CopyRow(s.Row, s.Col+w, s.Col, s.frame.Width-s.Col-w) | ||
| 112 | s.frame.FillRow(s.Row, s.Col, s.Col+w, renderer.BlankCell()) | 112 | s.frame.FillRow(s.Row, s.Col, s.Col+w, renderer.BlankCell()) | ||
| 113 | s.repairRow(s.Row) | 113 | s.repairRow(s.Row) | ||
| @@ -120,15 +120,16 @@ func (s *Screen) putPrintable(r rune) { | |||||
| 120 | 120 | ||||
| 121 | // Determine the range of cells actually modified, extending over any wide | 121 | // Determine the range of cells actually modified, extending over any wide | ||
| 122 | // pair the write lands on so no orphaned half is left behind. | 122 | // pair the write lands on so no orphaned half is left behind. | ||
| 123 | + | // Cells inside [Col, Col+w) are overwritten below, so only the orphaned | |||
| 124 | + | // halves just outside it need blanking. | |||
| 123 | lo, hi := s.Col, s.Col+w-1 | 125 | lo, hi := s.Col, s.Col+w-1 | ||
| 124 | - | if s.frame.At(s.Col, s.Row).Continuation { | 126 | + | if s.frame.IsContinuation(s.Col, s.Row) { |
| 125 | lo = s.Col - 1 | 127 | lo = s.Col - 1 | ||
| 128 | + | s.frame.Set(lo, s.Row, renderer.BlankCell()) | |||
| 126 | } | 129 | } | ||
| 127 | - | if right := s.Col + w; right < s.frame.Width && s.frame.At(right, s.Row).Continuation { | 130 | + | if right := s.Col + w; right < s.frame.Width && s.frame.IsContinuation(right, s.Row) { |
| 128 | hi = right | 131 | hi = right | ||
| 129 | - | } | 132 | + | s.frame.Set(right, s.Row, renderer.BlankCell()) |
| 130 | - | for x := lo; x <= hi; x++ { | |||
| 131 | - | s.frame.Set(x, s.Row, renderer.BlankCell()) | |||
| 132 | } | 133 | } | ||
| 133 | s.frame.Set(s.Col, s.Row, renderer.Cell{Rune: r, Style: s.Style}) | 134 | s.frame.Set(s.Col, s.Row, renderer.Cell{Rune: r, Style: s.Style}) | ||
| 134 | s.buffer.content(s.Row, s.Col+w) | 135 | s.buffer.content(s.Row, s.Col+w) | ||