mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-08-05 15:18:40 +00:00
terminal: print repeated characters through printSlice (#13625)
`printRepeat` (CSI `b`, repeat the previous character N times) calls
`print()` once per repeat, so something like `\x1b[2000b` ran grapheme
checks, width lookups, wrap handling, and the integrity assert 2000
times for what is usually the same character on the same row.
`Terminal.print` was 24% of samples on a REP-heavy micro benchmark.
This PR just aims to add a fast path by introducing a chunking
mechanism. anything that needs care (insert mode, grapheme clustering,
hyperlinks) still falls back to per-codepoint print() inside printSlice,
so behavior *should* stay unchanged.
Some profiling data:
Generated with some plain stupid logic:
```py
D = "benchdata"
parts, total = [], 0
while total < 40_000_000:
line = "x" + "\x1b[80b" + "y" + "\x1b[35b" + "\r\n"
parts.append(line); total += len(line)
open(f"{D}/rep.bin", "wb").write("".join(parts).encode())
```
**macOS (hyperfine, 15 runs, warmup 3):**
| | mean |
|---|---|
| before | 2.360 s |
| after | 1.166 s |
And now the really interesting and promising stuff
**Linux, 24-core NixOS x86_64 (poop, 6s sampling):**
| | wall_time | instructions | branch_misses | peak_rss |
|---|---|---|---|---|
| before | 1.51 s | 50.9 G | 9.41 M | 6.82 MB |
| after | 562 ms | 9.07 G | 114 K | 6.74 MB |
This commit is contained in:
@@ -490,9 +490,19 @@ pub fn printString(self: *Terminal, str: []const u8) !void {
|
||||
|
||||
/// Print the previous printed character a repeated amount of times.
|
||||
pub fn printRepeat(self: *Terminal, count_req: usize) !void {
|
||||
if (self.previous_char) |c| {
|
||||
const count = @max(count_req, 1);
|
||||
for (0..count) |_| try self.print(c);
|
||||
const c = self.previous_char orelse return;
|
||||
var remaining = @max(count_req, 1);
|
||||
|
||||
// Print the repeated codepoint in slices so that eligible runs
|
||||
// take the batched printSlice fast path. printSlice is semantically
|
||||
// identical to calling print per codepoint: ineligible characters
|
||||
// or terminal states (insert mode, grapheme clustering, hyperlinks,
|
||||
// etc.) fall back to the per-codepoint print() path internally.
|
||||
var buf: [4096]u32 = @splat(c);
|
||||
while (remaining > 0) {
|
||||
const n = @min(remaining, buf.len);
|
||||
try self.printSlice(buf[0..n]);
|
||||
remaining -= n;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user