terminal: vectorize reflow run scan

The masked-compare scan that finds bulk-copyable cell runs still
processed one cell per iteration and remained the largest single
cost in a column reflow.

Scan whole groups of cells at a time using the group variants of the
Mask helper: a group that fully matches the run pattern (and, for
text runs, contains no Kitty virtual placeholder, via eqlAny)
extends the run by the whole group, and any mismatch falls through
to the scalar loop which finds the exact end of the run within it.
The group length comes from the shared simd.lanes helper where the
target has SIMD support and falls back to a plain unrolled group
elsewhere.

1.19x faster on ghostty-bench +terminal-resize --mode=cols (120x80
terminal, 10k-line scrollback, shrink/grow column reflow cycles).
Combined with the preceding reflow optimizations, resize with reflow
is 5.8x faster than before the series.
This commit is contained in:
Mitchell Hashimoto
2026-07-31 20:58:35 -07:00
parent d4e446c480
commit ec5b369611

View File

@@ -9,6 +9,7 @@ const build_options = @import("terminal_options");
const Allocator = std.mem.Allocator;
const assert = @import("../quirks.zig").inlineAssert;
const fastmem = @import("../fastmem.zig");
const simd = @import("../simd/main.zig");
const tripwire = @import("../tripwire.zig");
const DoublyLinkedList = @import("../datastruct/main.zig").IntrusiveDoublyLinkedList;
const color = @import("color.zig");
@@ -1859,10 +1860,11 @@ const ReflowCursor = struct {
};
}
/// The group length for the masked-compare helpers below. This
/// matches the group length used by other cell scans (e.g. the
/// The group length for the vectorized bulk run scan below: the
/// SIMD lane count where the target supports it, otherwise a
/// plain unrolled group like other cell scans use (e.g. the
/// render state scans).
const bulk_group_len = 8;
const bulk_group_len = simd.lanes(u64) orelse 8;
/// Masked compare helper covering every cell field that a run
/// must share to be copied by copyRun: given that the first cell
@@ -1918,6 +1920,21 @@ const ReflowCursor = struct {
};
var len: usize = 1;
// Vectorized scan: check whole groups of cells at once. If a
// group fully matches, the run extends by the whole group;
// otherwise fall through to the scalar loop below, which
// finds the exact end of the run within it.
while (cells.len - len >= bulk_group_len) {
if (!BulkRunMask.eql(cells, len, run_pattern)) break;
if (check_placeholder and PlaceholderMask.eqlAny(
cells,
len,
placeholder_pattern,
)) break;
len += bulk_group_len;
}
while (len < cells.len) : (len += 1) {
if (!BulkRunMask.eqlScalar(cells[len], run_pattern)) break;
if (check_placeholder and PlaceholderMask.eqlScalar(