Repository navigation
Commit f45b95b
Add Ghostty comparison benchmarks and speed up the builtin engine (#61)
## Summary
This adds a benchmark that compares DT's default engine with Ghostty on
identical input, then uses it to speed the default engine up. The
default engine now matches or beats the Ghostty library on 8 of 13
workloads. It started at 0.01–0.4× on most of them.
Full methodology, results and remaining gaps:
`docs/performance-benchmarks.md`.
### Benchmarks
- **New `vt` mode** in `tools/Devolutions.Terminal.Bench`. It feeds
identical bytes, in one process, to three targets:
- the builtin engine;
- raw `libghostty-vt`;
- DT's Ghostty engine.
- **Before timing**, it checks that the final screens match.
- **The verdict is conservative:** `faster` only when the builtin's
slowest run beats Ghostty's fastest.
- **13 deterministic workloads**, modeled on `ghostty-gen` and vtebench.
`--frame-kb N` also takes a screen snapshot every N KiB, as the renderer
would.
- **`scripts/Invoke-TerminalBenchmarks.ps1`** runs one process per
workload.
- `-Baseline` fails on a builtin/Ghostty ratio drop of more than 15% on
identical inputs.
- `-GhosttyBench` also replays the inputs through upstream
`ghostty-bench`.
### Builtin engine
- **Packed cell storage** (`CellRow`/`PackedCell`): 16 bytes per cell
with no garbage-collected references, down from about 40. Combining
marks and hyperlinks move to per-line side arrays. The public `Cell` and
`TermColor` types are unchanged.
- **Logically blank cells are never written.** Reusing a scrollback line
and erasing to the end of a line are O(1).
- **Bulk paths**, each checked against the original
character-by-character path:
- printable ASCII (AVX2 where available);
- well-formed UTF-8 runs;
- plain `ESC [` sequences;
- control characters such as CR and LF.
- **Lookup tables** for character width and grapheme joining, covering
planes 0 and 1.
### Ghostty engine
- The screen is copied out of Ghostty only when something reads it, and
only for the rows Ghostty reports as changed. Before, it was copied in
full after every chunk of output.
## Results (one Windows x64 machine, builtin speed ÷ Ghostty speed in
the same run)
| Workload | Before | After |
| --- | ---: | ---: |
| ascii / ascii-lines / light-cells | 0.009x | 1.19–1.24x |
| scrolling (`y\n`) | 0.015x | 1.33x |
| dense-cells | 0.42x | 1.44x |
| styled / osc | 0.30x / 0.29x | 2.91x / 4.59x |
| mixed | 0.03x | 1.84x |
| scroll-region / cursor-motion | 0.018x / 0.11x | 0.96x / 0.90x |
| utf8 / cjk / emoji | 0.10x / — / — | 0.77x / 0.77x / 0.80x |
Screens are identical on every workload except one `styled` row. There,
the builtin engine drops a combining mark that follows a space, a
pre-existing difference.
## Known gaps
- **Non-ASCII text is still about 0.8×.** Closing it needs a dedicated
SIMD UTF-8 decoder or 8-byte cells.
- **Frame snapshots cost about 0.03 ms more** (0.11 ms per 120x30
frame), because each packed cell is unpacked into a public `Cell`.
- **These are JIT numbers** from one Windows machine, not from the
NativeAOT build.
## Tests
- New tests:
- `AsciiFastPathTests`, `CsiFastPathTests`, `CellRowModelTests`,
`PackedCellStorageTests` (Core);
- `GhosttyProjectionTests` (Ghostty).
- Every new shortcut was also checked by deliberately breaking it and
confirming a test fails.
- Release build of `Devolutions.Terminal.slnx`: 0 warnings, 0 errors.
- Core, Render, Control, Ghostty, Compatibility, Connection, Cli, App,
UI, Broker and Settings tests all pass (3,394 tests; 11 skipped in
Connection).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>1 parent dc1cd26 commit f45b95b
23 files changed
Lines changed: 4217 additions & 191 deletions
File tree
- docs
- scripts
- src
- Devolutions.Terminal.Core
- Devolutions.Terminal.Ghostty
- tests
- Devolutions.Terminal.Core.Tests
- Devolutions.Terminal.Ghostty.Tests
- tools/Devolutions.Terminal.Bench
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
53 | 53 | | |
54 | 54 | | |
55 | 55 | | |
56 | | - | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
57 | 63 | | |
58 | 64 | | |
59 | 65 | | |
| |||
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
7 | 10 | | |
8 | 11 | | |
9 | 12 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
0 commit comments