Optimize PTY IO throughput and NativeAOT support - #104
Draft
wieslawsoltes wants to merge 8 commits into
Draft
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR implements a Ghostty-style Unix PTY IO gather pipeline for RoyalTerminal, adds throughput fixtures and benchmark coverage, and makes the terminal/PTY stack publishable under NativeAOT.
The Unix PTY read path now splits kernel draining from subscriber dispatch:
PTY-Gatherowns nonblockingread(2)/poll(2)and fills a fixed ring of preallocated 64 KiB buffers.PTY-Dispatcherpublishes completed batches through the existingDataReceivedevent.IPtycontract stays unchanged.Reference Review
The implementation was checked against the required terminal references before changing behavior:
src/termio/Exec.zigConptyConnection.cppWriteBuffer.tsRoyalTerminal follows Ghostty for Unix PTY reads because the old implementation had the same serial
read -> process/dispatchshape described by Ghostty. Windows ConPTY behavior is left unchanged because it has a different pipe/overlapped IO model and needs separate measurement.What Changed
UnixPtyReadBatchPolicyand focused tests for batching thresholds and budget behavior.--io,--generate-io-fixtures,--fixtures,--fixture-size-mb,--io-mode, and--io-repeatsbenchmark options.BasicVtProcessorand capture childcatwall time through/usr/bin/time -p.--vt-parseparser-only benchmarks plus callback timing/allocation columns for PTY IO rows.--ghostty,--ghostty-app, and--ghostty-pathbenchmark options to compare RoyalTerminal against an installed Ghostty build using the same fixture files.docs/specs/terminal-io-throughput-optimization.md.NativeAOT Support
System.Text.Jsonserialization with source-generated metadata for terminal capture, command history, profiles, workspaces, and SSH secret payloads.RoyalTerminal.PtyIoAotSmokeexecutable that publishes and runs through NativeAOT.AppContext.BaseDirectoryinstead ofAssembly.Location.PublishAot=true;ReactiveUI.AvaloniaunderPublishAot=true;ReactiveWindow<T>activation with explicit Avalonia open/close lifetime disposal;WhenAnyValueuses in the app controller with explicit property-change observables;Dispatcher.UIThread.Validation
Validated locally on macOS arm64:
Results:
osx-arm64: passed.The demo NativeAOT publish emits macOS linker debug-info module-cache warnings from the toolchain, but no trim or AOT analysis errors.
Main vs Optimized PTY IO Comparison
The earlier managed-VT numbers were off because the benchmark was mixing PTY dispatch shape with managed parser/screen allocation cost. Parser-only profiling showed the real bottleneck: whole-screen scrolling allocated a fresh
TerminalRowfor every new line even after scrollback was full, and printable ASCII took the full Unicode grapheme/category and width path per codepoint.Parser-only
BasicVtProcessor.Processbefore/after on 16 MiB fixtures, five repeats:Compared
origin/main(97d4c4d) against this branch (f87b354) on macOS arm64 with the same 16 MiB fixtures and five repeats. The benchmark runs/usr/bin/time -p catthrough a real PTY, feeds every delivered batch throughBasicVtProcessor, records terminal-side elapsed throughput, records childcatwall time, and reports the median run.This now improves both sides of the original problem: saturated PTY output is delivered as 64 KiB batches instead of serial 1 KiB dispatches, and the managed parser no longer spends most of its time allocating rows for steady-state scrolling.
Installed Ghostty Comparison
Compared this branch against installed Ghostty
Ghostty 1.3.2-HEAD+28972454cfrom/Users/wieslawsoltes/GitHub/RoyalTerminal/external/ghostty/macos/build/ReleaseLocal/Ghostty.app. Both columns use writer-side/usr/bin/time -p catwall time, so this compares how quickly the child process can write the same fixture into each terminal path. RoyalTerminal used--io-mode managed-vt.16 MiB fixtures, five repeats:
150 MiB fixtures, three repeats:
Interpretation: this RoyalTerminal branch is competitive with the installed Ghostty build on writer-side throughput. Ghostty is ahead on larger ASCII and Unicode fixtures, while RoyalTerminal is ahead on the CSI-heavy fixture. This is not a renderer frame benchmark and does not compare GPU presentation latency.
Follow-Up