Commit Graph
2 Commits
Author SHA1 Message Date
ddidderr 062c643ea8 perf: add ghostty-bench and bring terminal throughput to Zig parity
Adds crates/ghostty-bench, a port of ZIG/src/benchmark and src/synthetic:
the ghostty-bench binary (terminal-stream, terminal-parser,
codepoint-width, grapheme-break, is-symbol, osc-parser, screen-clone
with Zig's CLI) and ghostty-synthetic data generators (+ascii, +utf8,
+osc, plus a Rust-only +sgr mode).

Measured against Ghostty's own ghostty-bench built with Zig 0.15.2
(ReleaseFast) vs --profile production, the hot paths were optimized
without behaviour changes:
- Terminal::print single-lookup fast path for width-1 chars mid-line
- page grapheme allocator: bitmap first-fit instead of a linear scan
  (was 38% of utf8 stream time; randomized equivalence test added)
- plain cells skip per-cell work in clear_cells and row cloning
- ASCII fast path + strict hand-written UTF-8 decoder (exhaustive
  equivalence test against std)
- parser/OSC parser split into inlined fast and state-change paths
- RenderState allocates grapheme vectors only for grapheme rows
- Page::assert_integrity no-op stub so the production profile builds

terminal-stream is now at parity (ascii 1.08x, utf8 0.97x, SGR 1.04x of
Zig's time). Still slower: screen-clone (~2.6x, zero-filling fresh
cell Vecs vs Zig's lazily-zeroed mmap), osc-parser (1.9x), and the raw
parser on SGR data (1.5x). Full table in docs/rewrite/notes/perf-P1.md.

Test Plan:
- cargo test --workspace (no failures)
- cargo run --profile production -p ghostty-bench -- +terminal-stream
  --data=<file> and compare with Zig's ghostty-bench
2026-10-12 02:08:01 +02:00
ddidderr 800fe5be5e feat(vt): port the stream layer between parser and handlers
Ports ZIG/src/terminal/stream.zig: the Stream feeds bytes through the
UTF-8 decoder and parser and dispatches every CSI/ESC/OSC/DCS/APC and
C0/C1 control to a Handler as a high-level Action (all 95 Zig
variants). Zig's comptime handler interface becomes a Handler trait with
an associated error type, a required vt() and an optional vt_raw() hook.

Performance: next_slice() processes 4 KiB chunks, finds ESC with memchr
and decodes printable runs in bulk (scalar port of ZIG/src/simd's
index_of / utf8_decode_until_control_seq), plus the CSI parameter fast
path.

Terminals must construct the stream with Stream::new_alloc so the OSC
parser accepts allocating commands (colors, OSC 133), as in Zig.

Minor differences: after a stray UTF-8 byte the rest of the chunk
returns to the fast path, and a spurious Zig warning after CSI 2/5 W is
dropped.

Test Plan:
- cargo test -p ghostty-vt (448 passed; 37/38 stream tests ported,
  the skipped one only exercises the C ABI)
2026-10-11 21:59:15 +02:00