Skip to content

perf(tui): batch-drain build events to kill post-completion hang#75

Open
raphaelvigee wants to merge 1 commit into
masterfrom
worktree-bridge-cse_01XD66Atygr2ZXES9LirP9NU
Open

perf(tui): batch-drain build events to kill post-completion hang#75
raphaelvigee wants to merge 1 commit into
masterfrom
worktree-bridge-cse_01XD66Atygr2ZXES9LirP9NU

Conversation

@raphaelvigee

Copy link
Copy Markdown
Member

Problem

A warm heph run test //... shows 44/44 done, then the inline TUI hangs a few seconds "just processing events" — switching to the all view, the counter keeps climbing right up to exit.

Root cause (consumer-bound)

The engine emits typed events for every target in the transitive closure (ResultStart/ResultEnd + LocalCacheHit per dep) — tens of thousands on a warm run, flooded near-instantly into the unbounded channel. Both backends folded at most one event per tokio::select! iteration, so the burst drained one-at-a-time across thousands of iterations (interleaved with the 80ms ticker) after the headline had already painted. apply() itself is cheap; volume × per-iteration dispatch was the cost.

Fix

fold_buffered (src/tui/backend/mod.rs): on each build-event wake, greedily try_recv the rest of the buffered backlog in one pass. N select iterations collapse to one; the ticker still repaints per tick. Used by both the live arm and the final drain in interactive + ci backends. Engine emission unchanged — events stay typed, collapsed only at render.

Tests

  • fold_buffered unit tests: one wake folds the whole K-event backlog and leaves the channel empty; empty channel folds nothing.
  • Existing interactive loop tests (ticks / bg-pending / held) stay green.
  • Full suite green; clippy -D warnings + fmt clean.

🤖 Generated with Claude Code

A warm `heph run test //...` emits typed events for the whole transitive
closure (ResultStart/End + cache-hit per dep) — tens of thousands, flooded
into the unbounded channel near-instantly. Both backends folded at most one
event per `select!` iteration, so the burst drained one-at-a-time over
seconds after the headline `44/44 done` had already painted.

Add `fold_buffered`: on each build-event wake, greedily `try_recv` the rest
of the buffered backlog in one pass. N select iterations collapse to one;
the 80ms ticker still repaints. Engine event emission is unchanged — events
stay typed, collapsed only at render.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant