Skip to content

Detect style run breaks via cheap CellStyleKey rather than full CellStyle comparison#208

Merged
dakra merged 2 commits into
dakra:mainfrom
emil-e:emil-e/style-id-comparison
Apr 29, 2026
Merged

Detect style run breaks via cheap CellStyleKey rather than full CellStyle comparison#208
dakra merged 2 commits into
dakra:mainfrom
emil-e:emil-e/style-id-comparison

Conversation

@emil-e
Copy link
Copy Markdown
Collaborator

@emil-e emil-e commented Apr 29, 2026

What

The hot inner loop in buildRowContent previously called readCellStyle on
every terminal cell and compared the result field-by-field with CellStyle.eql()
across all ten style attributes (fg, bg, bold, italic, faint, underline,
underline_color, strikethrough, inverse, hyperlink).

This PR replaces that with a two-field CellStyleKey (style_id + hyperlink)
that is cheap to read and compare. The full readCellStyle call is now deferred
to run boundaries only — once per style change rather than once per cell.

style_id is Ghostty's unique identifier for a cell's complete attribute set.
hyperlink is tracked separately because Ghostty does not fold hyperlink state
into the style ID, but Ghostel uses text properties for it.

Supporting changes:

  • RunInfo.style becomes ?CellStyle (null = default style), removing the
    isDefault() guard from applyStyle and making the null check explicit at
    the call site
  • Wide-character spacer tails compensate with end_char -= 1 after the style
    logic runs, preventing boundary overcounting that would otherwise bleed style
    from a styled wide character onto the following default-style cell
  • CellStyle.eql() is removed; std.meta.eql on CellStyleKey replaces it
  • The explicit "close final run" block at the end of the loop is no longer needed

Benchmark

All runs: zig build -Doptimize=ReleaseFast, Elisp byte-compiled.
Machine: Apple M5 Pro, macOS 26.4.1, Emacs 30.2.

Streaming (chunked write + periodic redraw, no PTY)

Scenario main branch Δ
stream/incr 25 ms 20 ms −20%
stream/full 26 ms 20 ms −23%

TUI frame rendering (full-screen rewrites)

Scenario main branch Δ
incr 24×80 11 379 fps 13 466 fps +18%
full 24×80 11 448 fps 13 805 fps +21%
incr 40×120 5 182 fps 6 550 fps +26%
full 40×120 5 285 fps 6 539 fps +24%

TUI partial update (bottom-row update over static screen)

Scenario main branch Δ
full 24×80 14 180 fps 18 110 fps +28%
full 40×120 6 801 fps 9 634 fps +42%
incr 24×80 124 031 fps 133 969 fps +8%
incr 40×120 87 782 fps 99 775 fps +14%

Engine micro-benchmarks (single bulk call, no PTY)

Scenario main branch Δ
plain/incr 128 MB/s 135 MB/s +5%
styled/incr 89 MB/s 95 MB/s +7%
unicode/incr 179 MB/s 197 MB/s +10%

Real-world PTY (cat 1 MB through process pipe)

Scenario main branch Δ
plain/incr 105 MB/s 111 MB/s +6%
plain/full 107 MB/s 110 MB/s +3%

PTY improvements are modest: throughput there is dominated by process I/O and
timer overhead rather than per-cell render cost.

@emil-e
Copy link
Copy Markdown
Collaborator Author

emil-e commented Apr 29, 2026

Flaky test, as far as I can tell.

emil-e added 2 commits April 29, 2026 22:23
…module

When running profilers like Instruments to optimize Ghostel itself,
you need debugging symbols and thus you need to build with
-Doptimize=Debug. However, libghostty-vt is way too slow to get any
useful data unless it's build in optimized release mode. This allows
setting separate optimization levels.
…tyle comparison

Replaces per-cell CellStyle.eql() with a two-field CellStyleKey (style_id +
hyperlink) that is cheap to read and compare in the tight inner loop. The full
readCellStyle call is deferred to run boundaries only. Default styles are
represented as ?CellStyle = null, removing the isDefault() guard from applyStyle
and the explicit "close final run" block. Wide-character spacer tails are
compensated with end_char -= 1 to prevent boundary overcounting.
@dakra dakra force-pushed the emil-e/style-id-comparison branch 2 times, most recently from 5cbb409 to 81f1258 Compare April 29, 2026 20:30
@dakra dakra merged commit 81f1258 into dakra:main Apr 29, 2026
16 checks passed
@dakra
Copy link
Copy Markdown
Owner

dakra commented Apr 29, 2026

Very nice. Thanks.

BTW, if you want I can give you maintainer rights here on this repo.
Then you could trigger the flaky pipeline yourself and also close PRs/Issues etc.
But don't feel obligated to help maintain, just in case you're interested since you're also
very invested in ghostel already :)

@emil-e
Copy link
Copy Markdown
Collaborator Author

emil-e commented Apr 30, 2026

@dakra I am indeed pretty invested by now and I'd love to have a chat about. I'll drop you an e-mail!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants