powered by
etapx

0%

(
July 21, 2026
)

What It Actually Costs to Keep 150 Terminals Alive at Once

We benchmarked what happens when Influxx parks hidden terminal tabs across many worktrees — real DOM and memory numbers, plus the flat reveal-tax tradeoff that shaped our tuning.
What It Actually Costs to Keep 150 Terminals Alive at Once
What It Actually Costs to Keep 150 Terminals Alive at Once
We benchmarked what happens when Influxx parks hidden terminal tabs across many worktrees — real DOM and memory numbers, plus the flat reveal-tax tradeoff that shaped our tuning.

Running a dozen coding agents in parallel sounds like a UI problem until you actually try it. Each worktree gets its own terminal, each terminal wants a live PTY, a full xterm-style emulator instance, and its own slice of the DOM — and none of that goes away just because you switched to look at a different tab. We stopped guessing about what this costs and benchmarked it directly, and the results told us we'd been solving the wrong problem.

The Problem With "Just Open Another Worktree"

Influxx's whole premise is that running several agents in parallel, each in its own real git worktree with its own terminal, is cheap enough that you should default to it rather than ration it. That's true at the git layer — worktrees are just directories — but it is not automatically true at the UI layer. Every worktree tab that opens a coding-agent CLI mounts a terminal view: a pane manager component, a live PTY connection, and a terminal-emulator instance rendering cell-by-cell text into the DOM, frequently with GPU-accelerated rendering underneath for scroll performance.

None of that is free, and it doesn't stop being expensive just because the tab is sitting behind eight other tabs you're not looking at. A background daemon in Influxx already keeps the actual PTYs alive independent of the UI — that's how a terminal survives an app restart — but the UI-side question is separate: does the renderer need to keep a full terminal-emulator instance and its DOM subtree mounted for a worktree you haven't looked at in twenty minutes?

The obvious answer is "no, unmount it," and for a long time that was the working assumption. But "obviously" isn't a number, and a workflow built around running many agents at once is exactly the kind of workflow where an untested assumption compounds. So the team built dedicated internal benchmark scripts that drive the real, running app over a remote-debugging protocol — not a synthetic mock, the actual renderer under actual load — and measured what unmounting hidden terminals really does, in both directions.

How Parking Actually Works

Mounted Versus Parked

Every worktree tab in Influxx has a terminal view that can be in one of two states: mounted, meaning its pane manager, terminal-emulator instance, and DOM subtree are live and receiving output; or parked, meaning the view has been unmounted and only a lightweight snapshot of its scrollback state remains in memory, ready to reconstruct the view on demand. Parking is triggered by visibility and recency — a worktree tab you haven't touched in a while and isn't currently frontmost becomes a parking candidate.

This is a deliberately narrow mechanism. It is not the same thing as suspending rendering — Influxx already had a separate system that suspends GPU-accelerated rendering work for any terminal that isn't visible, regardless of whether it's parked or mounted, which caps the app at one active GPU rendering context no matter how many tabs are open. Parking is strictly about DOM and renderer memory: whether the component tree and cell-buffer objects for a hidden terminal exist in memory at all.

What Gets Torn Down, What Gets Kept

When a tab parks, its pane manager component and its terminal-emulator instance unmount, releasing their DOM nodes and the in-memory objects the emulator was using to track cursor position, cell attributes, and rendering state. What survives is a serialized scrollback snapshot — enough to repaint the terminal's visible history the moment you switch back, without needing to replay every byte the agent ever printed.

  • Parked: the terminal-emulator instance, its pane manager, and the DOM subtree it was rendering into.
  • Preserved: a scrollback snapshot sufficient to reconstruct the visible terminal state on remount.
  • Untouched: the underlying PTY and the coding-agent process itself, which the background daemon keeps running regardless of UI mount state.

Not every tab is eligible. Parking deliberately excludes remote-runtime and SSH-backed terminals, because those have no local snapshot to restore from — reconstructing their state means round-tripping to the remote host, which defeats the purpose of a fast local unmount. It also skips any tab with a pending startup command or an in-flight spawn, since tearing down a terminal mid-launch would mean losing output the agent hasn't finished producing yet.

"We didn't want parking to be clever about scrollback replay — that's where you introduce bugs. The snapshot just has to be good enough to repaint what was on screen and let normal scrollback fetches take over from there. Simplicity there is what made the numbers predictable."

— Priya Nandakumar, Senior Engineer, Terminal Systems at ETAPX

The Numbers: What 8 Backgrounded Worktrees Actually Cost

The benchmark scenario was chosen to look like a realistic heavy session: eight backgrounded worktrees, each carrying 5,000 lines of scrollback — the terminal emulator's default scrollback depth. At that depth, each terminal's screen buffer alone holds roughly 4 to 5 megabytes of cell data just for the text, styling, and cursor metadata the emulator tracks per cell. Multiply that across a session with many worktrees open and you can be carrying tens of megabytes in hidden terminal buffers before parking ever kicks in — memory sitting behind tabs nobody is looking at.

With all eight background terminals mounted, the app was running 9 mounted pane managers (the eight background ones plus the active tab) and roughly 2,941 DOM nodes attributable to those terminal views. After parking ran, mounted pane managers dropped to 1 — just the active tab — and DOM node count fell to roughly 1,389. That's a release of about 1,552 DOM nodes and the renderer memory footprint of 8 full terminal-emulator instances, gone, simply by unmounting views nobody was currently rendering.

  • Mounted pane managers: 9 → 1.
  • DOM nodes: roughly 2,941 → roughly 1,389, a reduction of about 1,552 nodes.
  • Renderer memory reclaimed: the equivalent of 8 terminal-emulator instances' worth of internal state.

Those are real, measured numbers from the running app, not modeled estimates — and they confirmed the core hypothesis: for a workflow that scales to dozens of parallel worktrees, leaving every terminal mounted is not a viable default. The DOM and renderer memory savings are large and they compound with every additional worktree a session opens.

The Reveal Tax: The Cost the Benchmark Almost Missed

Parking isn't free in the other direction. Switching back to a parked tab means remounting the pane manager and the terminal-emulator instance, reconstructing the DOM subtree, and re-establishing GPU-accelerated rendering for that view. The benchmark measured this "reveal tax" directly, and the result was the most useful finding in the whole exercise: it cost roughly 160 to 190 milliseconds, and that number stayed flat whether the tab's scrollback was empty or 20,000 lines deep.

That flatness is the important part. If the reveal cost scaled with scrollback size, parking would be a bad trade for anyone with long-running, chatty agents — exactly the users who most need to park in the first place, since their terminals are the ones accumulating the most memory. Instead, the benchmark showed the cost is fixed remount overhead: React component initialization, the terminal-emulator's own setup work, and re-establishing GPU-accelerated rendering for the view. Replaying the saved scrollback state onto that freshly mounted terminal is comparatively cheap and scales fine — it's the remount machinery around it that has a fixed price, paid once per reveal regardless of history length.

"I run six or seven agents at once on a big refactor and I used to worry that flipping back to a tab I hadn't touched in a while would hang for a second while it loaded thousands of lines of history. It doesn't — it's the same short pause whether I left it five minutes ago or overnight. That's the thing that actually made me trust leaving tabs in the background instead of babysitting which ones I keep open."

— Marcus Ostrowski, Platform Engineer at a logistics analytics firm, Influxx user

What We Got Wrong: GPU Contexts Weren't the Bottleneck

Going into the benchmark, one internal assumption was that parking's biggest win would be protecting the app from GPU rendering-context exhaustion — the idea that too many simultaneously active GPU-accelerated terminal contexts would be the thing that actually broke down at scale. The measurements didn't support that. A separate mechanism, unrelated to parking, already suspends rendering work for any terminal that isn't visible and caps the app at one active GPU-accelerated context at a time, regardless of how many tabs are mounted or parked.

That meant parking's real contribution was narrower and, frankly, more mundane than the team expected: DOM node count and renderer memory, not graphics-context protection. It's a useful reminder that "unmount things you can't see" and "stop expensive rendering work for things you can't see" are two different optimizations that happen to look similar from the outside, and conflating them would have led to solving a problem that didn't exist while underweighting the one that did.

"The value of actually benchmarking this wasn't just the numbers — it was catching that we were about to over-invest in the wrong fix. If we'd shipped parking purely as a GPU-context safeguard, we would have missed that the retention limits were what real users were hitting, and we'd have tuned the wrong dial."

— Elena Voss, VP of Engineering at ETAPX

The Actual Fix: Tuning Frequency, Not Payload

Because the reveal tax is a fixed per-remount cost rather than something that scales with scrollback, the lever worth pulling wasn't shrinking what gets restored — it was reducing how often a remount happens in the first place. If every reveal costs roughly the same 160-190 milliseconds no matter what, the way to make the app feel faster is to make sure the tabs you're most likely to reveal next simply never got parked.

That reframing produced three concrete changes:

  • Hot-worktree count raised from 4 to 8: the number of worktrees kept mounted and never parked, regardless of visibility, doubled — matching the benchmark's own eight-worktree scenario rather than an arbitrary smaller number.
  • Hot-retention window raised from 5 to 15 minutes: a worktree tab you were actively using stays exempt from parking for three times as long before it becomes eligible.
  • Most-recently-hidden tab exempted entirely: whichever worktree tab you just switched away from is now excluded from both limits, so flipping back to the tab you were just in is always instant — no reveal tax, because it was never parked to begin with.

Put together, this is a deliberately different shape of fix than "make parking cheaper." Parking's cost structure is fixed and acceptable; what needed tuning was the boundary between hot and parked, so the tabs people actually bounce back to stay mounted, and the DOM/memory savings still apply to the tabs that are genuinely idle in the background.

Why This Matters for a Many-Agent Workflow

The headline promise of running dozens of worktrees in parallel only holds up if the tenth, twentieth, or fiftieth backgrounded terminal doesn't degrade the ones you're actively working in. A session with 150 terminals across a long day of agent work is not a hypothetical stress test — it's what a heavy user's real session looks like after enough worktrees pile up. Without parking, that session accumulates DOM nodes and renderer memory linearly with every worktree ever opened. With it, the app's live footprint tracks roughly your hot-set size — now 8 worktrees plus whatever you just left — instead of your all-time worktree count.

The flat reveal tax is what makes that tradeoff acceptable rather than merely theoretical. A fixed ~160-190ms cost that doesn't grow with history means the parking boundary can be tuned purely around usage patterns — how many worktrees people actually keep hot, how long they stay away before coming back — without worrying that a chatty, long-scrollback agent will make reveals painfully slow for the users who need parking the most.

Frequently Asked Questions

Does parking a terminal kill the agent process running inside it?

No. Parking only unmounts the UI-side pane manager and terminal-emulator instance. The underlying PTY and the coding-agent process it runs are kept alive by Influxx's background daemon independent of whether the terminal view is mounted or parked.

Will I lose scrollback history when a tab gets parked?

No. A scrollback snapshot is preserved when a tab parks, and it's what gets used to repaint the terminal the moment you switch back — the benchmark's flat 160-190ms reveal tax held true even at 20,000 lines of history.

Why doesn't parking apply to SSH or remote-runtime terminals?

Those terminals have no local snapshot to restore from, so parking them would mean a round trip to the remote host on reveal instead of a fast local remount, which defeats the point of the optimization. They're excluded from parking entirely.

How many worktrees can I keep "hot" before parking starts?

Up to 8 worktrees are kept mounted and never parked, up from a previous limit of 4, and the most recently hidden tab is exempt from limits entirely so you can always flip back to it instantly.

Does parking help with GPU or graphics performance?

Not directly. A separate mechanism already suspends rendering for hidden terminals and caps the app at one active GPU-accelerated context regardless of parking state. Parking's measured benefit is DOM node count and renderer memory, not graphics contexts.

Is the reveal tax noticeable in normal use?

It's roughly 160-190 milliseconds, a short but perceptible pause when switching into a tab that had been parked. Because the hot-retention window is now 15 minutes and the most-recently-hidden tab is always exempt, most everyday tab switching never triggers it at all.

How was this benchmarked?

With dedicated internal benchmark scripts that drive the actual running Influxx app over a remote-debugging protocol, measuring real DOM node counts, mounted component counts, and timing — not a simulated or synthetic environment.

The lesson here wasn't that parking is good or bad — it's that a fixed cost and a scaling cost require different fixes. Once the benchmark showed the reveal tax doesn't grow with scrollback, the problem stopped being "make unmounting cheaper" and became "decide, correctly, which terminals should never be unmounted at all."