libghostty has always been fast, but it's also been getting significantly faster. Here are IO throughput measurements comparing Ghostty 1.3.1, 1.4.0, and main as of this morning. We'll be backporting most of these to a 1.4.1 release soon.
Here are some highlights of the changes since 1.4:
- Ghostty 1.4 had a fast-path for printing runs of plain text. But the fast-path only applied to limited cases. We now support mixed width characters and hyperlinks in the fast path.
- Our input path always widened the pty data to codepoints (8-bit to 32-bit) as part of UTF-8 decode, which also incurred a memory copy. We now have a SIMD fast path to detect pure-ASCII and we process the run direct from the read buffer (no copy, narrower data).
- New memory pages use most recent two pages actual utilization to pre-size themselves accordingly. This sped up styles, hyperlinks etc.
- When a VM page runs out of capacity for a certain type of data (e.g. hyperlinks, styles), we now project the usage so far to guess what you'll need, then shrink available rows to avoid a realloc. Before we'd naively double. This caused a classic "laddering" problem.
- We recognized "erase" being a slow path for us, and it's a common sequence, so we use another bit in row headers to track blank (we had space).
- Styles are now processed once per SGR. Previously, a sequence like "ESC[1;38;2;r;g;bm" would apply each style as it came in (1, then 38, then 2, etc.). We now apply all attributes at once and write to the cursor atomically.
- Styles are now packed into 128-bit values and use aarch64 and x86_64-specific assembly for minimizing instructions to use it.
- On x86_64, we found that LLVM was lowering some row resets (hot path) to a 4-byte store followed by an 8-byte load which couldn't be store-forwarded. This caused an almost 10% (!!!) slowdown on scroll operations only on x86_64. The fix was to change our reset to build the full header in a register then do a single memmove per row.
Net results are what you see in the graph, but written out main to 1.4: ASCII 1.5x, heavy styling 1.5x, styled Unicode 1.8x, random UTF-8 1.4x, random OSC 8 2.7x, mixed styled text with links 4.8x, cursor/erase 16x, hyperlink-dense text 5.6x.