Repository navigation
Conversation
added 3 commits
October 13, 2026 08:33
Every Markdown append re-lays out every mounted transcript row, and Textual's MarkdownStream appends once per provider fragment (one per token, 50-150/s). With a long history that kept most of a core busy while text streamed: 64-78% CPU at 400 items in a headless probe. TranscriptMarkdownStream renders the first fragment at once and then takes whatever piled up every 100ms. The same probe drops to ~38%. stop() waits for an append already in flight before its final flush, since Markdown.append reads its parse position before taking its lock.
The long UI pauses while text streams (130-430ms, about one a second at 400 transcript items) were Python's automatic gen-2 collection walking a few hundred thousand objects, not rendering. Collect once after startup and after every settled turn, then freeze the survivors so the automatic collector only scans what the next turn creates. Each pass unfreezes first, so nothing frozen leaks past a turn. With coalesced appends, the headless probe's longest stall drops from 200-315ms to ~19-34ms. Tests stub the collection out: it acts on the whole pytest process.
Every streamed append re-lays out each mounted row, so the window size sets the cost of one streaming update. With coalescing and idle-time GC in place, the headless probe at 400 items (6s runs) measured: window 200: ~31% CPU, p99 stall 18-24ms, 3-5 frames >16ms/s window 80: ~24% CPU, p99 stall 12-14ms, <1 frame >16ms/s The longest stalls (30-50ms) are now gen-2 GC passes over objects created during the turn, about the same either way. Paging scales in proportion (32 page, 16 overscan); it only starts at the scroll edge, so 80 items still leaves several screens of scrollback.
This branch has not been deployed
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.
I noticed that tau's TUI text input had erratic latency, and that while a session has activity CPU usage would often climb to roughly 100% while stalling input entirely for 100s of ms. The behavior was most visible during long outputs or with thinking shown. The core cause was a combination of 1) updating the UI causes Textual to re-compute layout, which is expensive, and 2) updates were happening sometimes in excess of 100Hz. GC pauses also contributed. This change:
TranscriptMarkdownStreamdesigned for the transcript's requirementsgc.freeze()to reduce the scope of the GC's work while results are streaming inIn combination this reduces the stalls to <15ms P99, which in my subjective opinion feels much better, and reduces max CPU usage to about 25% of one Zen 5 core. There seems to be a pre-existing race condition in the autocomplete, this change does not fix that but it makes it much less visible. No visual change, all tests passing, tested on Python 3.13.5.