perf: idle CPU, dock icon memory, and closed-workspace retention - #335
Merged
Merged
Conversation
…lease closed workspaces Measured on a Debug build at a 1700x1050 window with one idle terminal (footprint, top, sample, heap; commands in docs/audits/performance-audit-2026-09-12.md): - Idle CPU 12-22% -> 1.2%. TmuxWorkspacePaneOverlayView ran TimelineView(.animation) permanently, which made every window's overlay NSHostingView re-run updateConstraints (and an App Nap XPC round trip) on every display frame. The timeline is now mounted only while a flash is within FocusFlashPattern.duration, and the overlay controller re-renders once when the flash ends. - CG image footprint 32 MB -> 8 MB. AppIconDark/AppIconLight were 1024px assets with no scale, so applicationIconImage rasterized a 2048px copy; they are now 512pt @2x. - Live Workspace objects after opening and closing six workspaces: 7 -> 3 with one open. The sidebar row's .onHover closure captured the whole row (including the Workspace) and SwiftUI's context-menu responder tree kept it alive; hover tracking moved to a leaf view that captures only the state binding.
Codex review flagged that reassigning an otherwise identical root view may not re-run the body, which would leave TimelineView mounted after a flash. The controller now passes isFlashActive as a stored input: true when a flash starts, false from the settle callback (which no longer re-reads the wall clock, so a backwards clock step cannot pin it on). Verified live with an lldb counter on -[NSView setNeedsUpdateConstraints:] for the overlay hosting view: 7 hits during a notification flash, 0 hits in the 5 s after it settled (341 hits per 4 s before this PR).
There was a problem hiding this comment.
The diff addresses idle CPU, dock icon memory, and workspace retention with targeted changes. No code review issues found per the provided standards.
Review coverage: 324/324 diff lines supplied. Partial input (truncated: standards). Inline comments are limited to fully visible, valid right-side hunks. Reviewed commit: 44d7448225bec0a8757e31b87e8b9ccc1df9cb27.
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.
What this does
Programa with one idle terminal burned 12 to 22% of a core doing nothing, and every launch parked a 32 MB dock icon bitmap in memory. Both are gone, and closing a workspace now frees more of it.
Measured on a Debug build of
a0dbcc882at a 1700x1050 window (same script before and after):Workspaceobjects with 1 open, after opening and closing 6The production process shows the same idle cost: main thread at 17.5% CPU and 214 CPU-minutes over 42 hours of uptime.
Why
TmuxWorkspacePaneOverlayViewwrapped its canvas inTimelineView(.animation)unconditionally. Every window installs this overlay at init, and a hidden hosting view still ticks, so each frame reset the root view, dirtied constraints (about 2,500 times per second), and ran an AppKit display cycle whoseNSHostingView.updateConstraintsacquires an App Nap assertion over a synchronous XPC call. The timeline now mounts only while a flash is withinFocusFlashPattern.duration, and the controller re-renders once when the flash ends so the timeline is torn down.AppIconDarkandAppIconLightare 1024 px PNGs declared without a scale, soapplicationIconImagerasterized a 2048 px copy on retina. They are now 512pt @2x..onHoverclosure captured the whole row value including itsWorkspace, and SwiftUI's context-menu responder tree kept those closures alive after the row was removed. Hover tracking moved to a leaf view that captures only the state binding.Full measurement log, commands, and the remaining unmeasured candidates are in
docs/audits/performance-audit-2026-09-12.mdon the maintainer's machine (audit reports are not committed).Test plan
TmuxWorkspacePaneOverlayTestsandWorkspaceContentViewVisibilityTests, 14 passed locally, including the two new tests (timeline gating window; icon decodes at 1024 px / 512pt).topcommand.BonsplitTabDragUITests.testMinimalModeSidebarControlsRevealOnlyFromSidebarHover).