Skip to content

perf(ui): reduce Linux workspace switch overhead#358

Draft
pascalandr wants to merge 4 commits intoNeuralNomadsAI:devfrom
Pagecran:PR330_perf_issue
Draft

perf(ui): reduce Linux workspace switch overhead#358
pascalandr wants to merge 4 commits intoNeuralNomadsAI:devfrom
Pagecran:PR330_perf_issue

Conversation

@pascalandr
Copy link
Copy Markdown
Contributor

@pascalandr pascalandr commented Apr 24, 2026

Refs #330

Summary

  • reduce Linux/Tauri workspace-switch overhead by unmounting inactive instance shells instead of keeping every workspace UI tree alive in parallel
  • keep sidecar tabs mounted, so the optimization stays focused on workspace shells and does not reset unrelated sidecar state
  • add targeted Tauri/Linux perf instrumentation for investigation, but gate the benchmark behind an explicit opt-in flag
  • keep the Linux/Tauri light-render experiment opt-in as well, so it can be tested without changing default Linux UI behavior

Measured Improvement So Far

In the Linux VM used for investigation, the measured workspace-switch path improved from roughly ~46ms average to ~33ms average after mounting only the active instance shell.

Perf Experiment Flags

  • benchmark: localStorage.setItem(\"perf330-bench\", \"1\")
  • light-render experiment: localStorage.setItem(\"perf330-light-render\", \"1\")

Reload after setting either flag.

Still Under Investigation

  • prompt/input lag is not fully diagnosed yet
  • the VM also shows graphics-stack warnings (libEGL / DRI3), so this PR remains a draft for validation on a real Linux machine

Validation So Far

  • npm run build --workspace @codenomad/ui
  • cargo build
  • built and installed a .deb from this branch for local testing

Experiment with mounting only the active workspace shell, add targeted Tauri/Linux perf instrumentation, and include a Linux/Tauri light-render mode to help investigate the remaining prompt lag on real hardware.
@pascalandr
Copy link
Copy Markdown
Contributor Author

@CodeNomadBot

I want you to perform the gatekeeper checks for latest PR state

check for regressions
check for better possible implementations
Be the PR gatekeeper
Be ruthless about code quality

@codenomadbot
Copy link
Copy Markdown
Contributor

codenomadbot Bot commented Apr 24, 2026

Gatekeeper review of the latest PR state.

Assumption: you wanted a ruthless PR-quality review of the current diff against dev, focused on regressions and stronger implementation choices, not a request for me to change code in this branch.

Findings:

  1. High: the Linux "light-render" experiment is currently unconditional production behavior, not an experiment.
    packages/ui/src/index.css:36-50 removes all transition durations, animation durations, backdrop filters, and several shadows for every Tauri/Linux render. packages/ui/src/main.tsx:20-33 and packages/ui/src/renderer/loading/main.tsx:44-60 stamp data-runtime-os="linux" on every run, so this ships broadly to all Linux users if merged.

Why this blocks:

  • The PR description frames this as an experiment to test whether WebKitGTK rendering cost is involved.
  • The implementation permanently changes Linux UI behavior instead of making it opt-in.
  • That creates obvious UX regressions on Linux and makes it harder to measure whether the workspace-shell change alone was the actual improvement.

Better implementation:

  • Gate the CSS behind an explicit experiment flag, dev-only flag, or user setting.
  • Keep the runtime OS annotation if needed, but do not tie production styling changes to it by default.
  1. High: switching tabs now unmounts all inactive tabs, which resets state for sidecars and instance-local UI, not just the heavy workspace tree.
    packages/ui/src/App.tsx:734-775 changed the tab content from rendering all tabs and hiding inactive ones to rendering only activeAppTab(). That means every switch unmounts the previous tab entirely. For sidecars this is a direct regression: packages/ui/src/components/sidecar-view.tsx:12-14 stores iframe/navigation state locally, and sidecar-view.tsx:80-194 rebuilds the iframe from props.tab.shellUrl on mount, so switching away from a sidecar tab will drop its current page/path/input state and reload it on return.

Why this blocks:

  • The PR goal is to reduce workspace-switch overhead.
  • The lifecycle change is broader than that goal and changes sidecar behavior too.
  • Instance-local UI state also resets on remount (packages/ui/src/components/instance/instance-shell2.tsx:107-125), so drawers/dialog state no longer survives tab switches.

Better implementation:

  • Restrict the mount-only-active optimization to instance shells, not all tab kinds.
  • If the full-shell unmount is required, preserve sidecar and instance local state in stores before unmounting, otherwise the UX regression is immediate.
  1. Medium: the dev benchmark mutates real app state automatically on Linux startup, which is too invasive for routine development runs.
    packages/ui/src/App.tsx:322-435 starts runPerf330Benchmark() on mount. On Linux/Tauri dev it may auto-open the most recent folder (App.tsx:330-336), create another instance (App.tsx:353-364), and create a session if needed (App.tsx:406-417).

Why this is risky:

  • Running the app in dev now launches real workspace/session activity without explicit consent.
  • It can pollute recents/session state and make normal debugging noisy.
  • It also means perf instrumentation is no longer isolated from app behavior.

Better implementation:

  • Make the benchmark opt-in via env var, query param, or explicit developer command.
  • Keep passive logging on by default if useful, but keep state-mutating benchmark setup off by default.

Validation notes:

  • cargo build did not finish in this environment because the Tauri build expects resources/server to exist.
  • npm run build --workspace @codenomad/ui did not run here because the workspace is missing the local vite dependency in this checkout.
  • I did not see evidence from this environment that these failures are caused by this PR specifically, so I am not treating them as PR findings.

Gatekeeper verdict: not ready yet. I would want the Linux rendering changes gated, and the tab-lifecycle optimization narrowed so it does not regress sidecars and other non-workspace tab state.

--
Yours,
CodeNomadBot

Make perf benchmarking opt-in, gate the Linux light-render experiment behind an explicit flag, and keep sidecar tabs mounted so the workspace-shell optimization does not reset unrelated tab state.
@pascalandr
Copy link
Copy Markdown
Contributor Author

@CodeNomadBot

I want you to perform the gatekeeper checks for latest PR state

check for regressions
check for better possible implementations
Be the PR gatekeeper
Be ruthless about code quality

@codenomadbot
Copy link
Copy Markdown
Contributor

codenomadbot Bot commented Apr 24, 2026

Gatekeeper review of the latest PR state.

Assumption: you wanted a fresh review of the current branch head after the follow-up fixes, focused on regressions and stronger implementation choices, not a request for code changes from me.

Findings:

  1. Medium: opening the folder-selection overlay now unmounts the active instance shell, so instance-local UI state is still reset outside the intended workspace-switch path.
    packages/ui/src/App.tsx:733-776 now keeps sidecars mounted, which fixes the earlier sidecar regression, but instance tabs are wrapped in <Show when={isVisible()} keyed>. isVisible() is defined as activeAppTabId() === tab.id && !showFolderSelection() (App.tsx:735), so the currently active instance shell is unmounted whenever the folder picker opens, then remounted on close.

Why this still matters:

  • The optimization goal is to reduce inactive workspace-shell overhead during tab switching.
  • Tying mount lifetime to showFolderSelection() broadens the behavior change beyond tab switching.
  • Remounting the active shell still drops instance-local ephemeral state such as drawer/dialog state and any other non-store UI state in packages/ui/src/components/instance/instance-shell2.tsx.

Better implementation:

  • Keep the instance-shell mount condition scoped to active-tab identity only.
  • Let the folder-selection overlay visually cover the shell without participating in shell lifetime.
  • If there is a specific reason to unmount during folder selection, that reason should be documented because it is not obvious from the PR goal.

What looks fixed from the previous review:

  • Sidecar tabs stay mounted again, so the sidecar iframe/state regression appears addressed.
  • The Linux light-render path is now opt-in via data-perf330-light-render.
  • The perf benchmark path is now gated through isPerf330BenchmarkEnabled() instead of running on every Linux/Tauri dev startup.

Residual note:

  • packages/ui/src/App.tsx is now about 824 lines, which is over the repository’s source-file size guideline. I am not treating that as a blocker by itself, but it is worth keeping in mind if this area changes again.

Gatekeeper verdict: closer, but not fully ready yet. I would want the instance-shell lifetime decoupled from showFolderSelection() before approving this as a tight, regression-safe perf change.

--
Yours,
CodeNomadBot

Keep the mount optimization scoped to actual tab switching by leaving the active instance shell mounted while the folder-selection overlay is open, preserving instance-local UI state outside the targeted workspace-switch path.
@pascalandr
Copy link
Copy Markdown
Contributor Author

@CodeNomadBot

I want you to perform the gatekeeper checks for latest PR state

check for regressions
check for better possible implementations
Be the PR gatekeeper
Be ruthless about code quality

@codenomadbot
Copy link
Copy Markdown
Contributor

codenomadbot Bot commented Apr 24, 2026

Gatekeeper review of the latest PR state.

Assumption: you wanted a fresh review of the current branch head after the latest follow-up fix, focused on regressions and stronger implementation choices, not a request for code changes from me.

Findings:

  • No blocking regressions found in the latest revision.

What changed relative to the previous review:

  • The active instance shell now stays mounted during folder selection. In packages/ui/src/App.tsx:735-750, mount lifetime is keyed off isTabActive() while visual visibility is controlled separately by isVisible(). That fixes the earlier issue where opening the folder-selection overlay unmounted and remounted the active shell.
  • Sidecar tabs are still kept mounted, so the earlier sidecar state-reset regression remains addressed.
  • The Linux light-render path is still opt-in via data-perf330-light-render.
  • The benchmark path is still opt-in via isPerf330BenchmarkEnabled().

Residual risks / testing gaps:

  • I was only able to do static review in this environment. I did not re-validate the full UI/Tauri builds here, so I am not independently confirming runtime behavior beyond code inspection.
  • packages/ui/src/App.tsx is now about 825 lines, which is over the repository’s source-file size guideline. I am not treating that as a blocker for this PR, but it is a maintenance risk if this area keeps growing.

Gatekeeper verdict: current diff looks acceptable from a regression-scope perspective. I do not have a remaining code-quality blocker on the latest PR state.

--
Yours,
CodeNomadBot

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