Skip to content

perf(desktop): make multitab streaming sessions fast - #71780

Merged
OutThisLife merged 3 commits into
mainfrom
bb/multitab-perf
Jul 26, 2026
Merged

perf(desktop): make multitab streaming sessions fast#71780
OutThisLife merged 3 commits into
mainfrom
bb/multitab-perf

Conversation

@OutThisLife

@OutThisLife OutThisLife commented Jul 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

With several session tabs streaming at once (the multi-PR-review workload), the renderer crawled. Profiling with a new multitab harness scenario found two costs and this PR removes both:

  • Hidden tabs rendered everything. Keep-alive keeps inactive tabs mounted, and each one's $messages subscription stayed live — every delta flush re-rendered every busy tab's entire thread. The pane layer now flows its visibility down (PaneVisibleContext), and a hidden tab freezes its transcript, catching up in one commit on reveal. Status dots stay live through the separate status atoms.
  • ThreadTimeline forced a reflow per rect read during streaming (620ms of getBoundingClientRect self-time in one 5-tab run). While the viewport follows the bottom the active prompt is just the last entry, so the walk is skipped entirely; real scrollback keeps the precise compute.

The multitab scenario joins the CI tier of scripts/perf/ so this stays fixed. Its --code flag streams one growing fenced code block per tab with no settle boundaries — the coding-agent worst case, where block memoization can't help and every flush re-renders the whole block.

Measurements

Dev renderer, isolated instance (perf:serve), 5 tabs each streaming into a 20-turn transcript at the real 33ms flush cadence. worst-second fps = the slowest sliding 1-second window — the number you feel.

Coding workload (--code, one growing code block per tab):

before after
avg fps 24.9 55.8
worst-second fps 16.4 53.5
frame p95 / p99 78 / 184ms 40 / 45ms
frames >33ms 181 60
longtasks 72 (max 213ms) 1 (53ms)

Longer streams degrade further on main as the blocks grow (frames >200ms appear — the sub-10fps crawl at ~30s in); after the fix a 30s 5-tab code stream holds 59 avg / 55 worst-second fps, zero longtasks. Hidden-tab cost also scaled with tab count before (each mounted tab re-rendered per flush), which is why more tabs made it worse; after, only the visible tab pays.

Prose workload (blocks settle as they stream): frame p95 44.8→24.5ms, p99 118→30.9ms, slow frames 109→2.

Stash-toggle re-runs confirmed the regression returns without the patch in both workloads. Single-tab stream/keystroke scenarios are unchanged.

Test plan

  • npm run perf -- multitab --tiles 5 --code before/after (numbers above)
  • Stash-toggle: regression returns with fixes stashed, disappears with them applied
  • npm run typecheck (all three tsconfigs)
  • npm run test:ui — 2287 passed; one pre-existing fuzz-test timeout (markdown-blocks.test.ts) passes in isolation and is untouched by this change

Keep-alive keeps every ever-active tab mounted, but each hidden tab's
ChatRuntimeBoundary still subscribed to its view's $messages — so every
streaming delta flush (~30x/s) re-rendered every busy tab's whole thread,
and five concurrent sessions dropped the app to a crawl.

Flow the pane layer's visibility down as PaneVisibleContext and gate the
$messages subscription on it: a hidden tab freezes its transcript (status
dots stay live through the separate status atoms) and catches up in one
commit on reveal, since subscribe fires immediately with the current value.
ThreadTimeline's scroll compute read getBoundingClientRect for every user
message per scroll frame; interleaved with React's streaming style writes
each read forced a full reflow — the hottest self-time frame in the
multitab profile (620ms over one 5-tab run). While the viewport is pinned
to the bottom the active prompt is simply the last entry, so answer from
data and save the layout reads for actual scrollback.
@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on 9173f58

all good!

@alt-glitch alt-glitch added type/perf Performance improvement or optimization comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have labels Jul 26, 2026
N session tiles stacked as tabs, all mounted (keep-alive) and all
streaming concurrently through the real publishSessionState path — the
"several PR reviews at once" workload. Frame pacing + longtask metrics,
no backend or credits needed; the workload that exposed both fixes above
and the regression gate that keeps them fixed.
@OutThisLife
OutThisLife merged commit be00a71 into main Jul 26, 2026
30 checks passed
@OutThisLife
OutThisLife deleted the bb/multitab-perf branch July 26, 2026 05:41
randlee pushed a commit to randlee/hermes-agent that referenced this pull request Aug 11, 2026
perf(desktop): make multitab streaming sessions fast
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have type/perf Performance improvement or optimization

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants