Performance: measured wins across boot, broadcast path, and client bundle - #118
Open
ishu86 wants to merge 7 commits into
Open
Performance: measured wins across boot, broadcast path, and client bundle#118ishu86 wants to merge 7 commits into
ishu86 wants to merge 7 commits into
Conversation
Lazy-load @mariozechner/pi-coding-agent on first pi use instead of at boot. Measured ~170ms import cost for a provider most users never select. Also cap the startup npm-registry fetch at 5s so a slow or dead connection can't hang boot indefinitely.
getMessages() reads the entire transcript payload sidecar end to end (its own docstring says it's for export/handoff/fork, not per-push use), but chat.send called it unconditionally to check chat length. Made it lazy so only the rare handoff/restore paths pay for it. Measured 3ms on a short chat, 45ms on a 2,300-entry one.
stateVersion was bumped on every appended transcript entry, forcing a full sidebar re-derive and whole-snapshot JSON.stringify each time - even though the dedupe signature then discarded almost all of it as byte-identical. Bump it only when the entry moved a field the sidebar actually shows (hasMessages, lastMessageAt, or the quantized lastAgentMessageAt bucket). Verified: 120 agent entries inside one 15s bucket now produce 1 bump instead of 120.
A git command (stage/commit/etc) triggered an unfiltered broadcast, which rebuilds and resends every topic on every socket - including a full terminal snapshot, whose serializer walks the entire scrollback. Measured 3.7ms/72KB at default scrollback, 14.9ms/347KB at max, paid every 5s while the diff panel polls. Scoped to just this project's diff snapshot and the sidebar, matching the other 14 broadcast call sites in this file.
resolveCommandPath always spawned a synchronous login shell, blocking the event loop for ~5ms (up to 24ms observed) on every call - hit per-invocation by gh commands in diff-store and github.ts. Bun.which covers the common case (the server's own PATH) in ~0.045ms; the login shell stays as the fallback for PATH entries only a login shell would see (npm globals, ~/.local/bin). Not cached, so a caller that re-resolves after installing a CLI still gets a fresh answer.
The per-row wrapper (MessageScrollerItem + a div + KannaTranscriptRow) wasn't memoized, so every push walked all N rows through three component invocations before React's inner memo could bail. Extracted it into one memoized component - row identity was already stable via useStableResolvedRows, so a default shallow memo now bails immediately for every row but the one that changed. Also memo(SetupWizard): a zero-prop component that re-rendered on every chat push for nothing.
These were statically imported into the entry chunk despite being sidebar panels or a separate route, not first paint. GitPanel alone pulls @pierre/diffs -> shiki core -> ~300 language grammar chunks. TerminalWorkspace pulls all five @XTerm packages; one helper import (disposeCachedTerminal) was the only thing pinning it into the entry chunk, so that one call is now a dynamic import too. Also folded an inline arrow into a stable useCallback so it stops defeating ChatInputDock's memo on every streamed entry. Entry chunk: 2,180KB -> 1,183KB raw (626KB -> 356KB gzip).
|
| // captive portal that blackholes the registry would otherwise hang startup | ||
| // indefinitely on "checking for updates". UpdateManager re-checks post-listen. | ||
| const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`, { | ||
| signal: AbortSignal.timeout(5000), |
There was a problem hiding this comment.
Timeout Breaks Interactive Updates
The five-second timeout is applied inside the shared package-version lookup, not only during startup. The interactive stable-reinstall path also calls this helper, so if the npm registry takes more than five seconds to respond, an explicit reinstall is aborted and reported as “Update failed” even though the request could have succeeded. Make the timeout startup-specific or allow interactive callers to choose a longer limit.
Knowledge Base Used: Settings, updates, and usage controls
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.
Performance pass across boot, the sidebar broadcast path, and the client bundle. Every number below is measured on this machine, not estimated — commands and methodology in each commit body.
Results
What's here
chat.sendno longer reads the entire payload sidecar just to check if a chat is empty (3ms → 45ms depending on chat size, now skipped almost always).stateVersionwas bumped on every appended transcript entry, forcing a full sidebar re-derive + whole-snapshot stringify per entry — even though the dedupe signature discarded almost all of it as unchanged bytes. Now bumps only when an entry actually moves something the sidebar shows.resolveCommandPathalways spawned a synchronous login shell (~5ms, up to 24ms observed);Bun.whichnow covers the common case first (~0.045ms), with the login shell staying as fallback for PATH entries only it can see.@pierre/diffs→ shiki → ~300 language grammar chunks. Code-split all four.Known trade-offs
disposeCachedTerminalmoved behindimport()so the 500KB xterm bundle isn't pinned into the entry chunk by one cleanup helper. This lands a microtask later than before; only matters if a new terminal reused the exact same id in that window, which doesn't happen since ids are freshly generated.Verification
bun test: 1732 pass, 0 fail (baseline was 1730; +2 new tests locking the sidebar-bump semantics — verified they fail without the fix)bun run check: typecheck + both production builds clean@xterm/headlessserialization, realEventStore), not estimated