Skip to content

Lazily load non-critical contribution modules#316155

Draft
dmitrivMS wants to merge 2 commits into
mainfrom
dev/dmitriv/perf-lazy-chat-contributions
Draft

Lazily load non-critical contribution modules#316155
dmitrivMS wants to merge 2 commits into
mainfrom
dev/dmitriv/perf-lazy-chat-contributions

Conversation

@dmitrivMS
Copy link
Copy Markdown
Contributor

@dmitrivMS dmitrivMS commented May 13, 2026

What

Defers fifteen contribution modules off the eager workbench bundle parse path. A new loader at src/vs/workbench/browser/workbench.contribution.lazy.ts registers a single workbench contribution at WorkbenchPhase.Eventually that dynamic-import()s the deferred modules after the workbench has fully restored.

Module Side effect Phase
chat/browser/chat.view.contribution.ts Agent-plugins views AfterRestored
mcp/browser/mcp.view.contribution.ts MCP-servers views AfterRestored
services/policies/browser/accountPolicyGate.contribution.ts gate contrib AfterRestored
meteredConnection/browser/meteredConnection.contribution.ts status contrib + 1 action AfterRestored
welcomeOnboarding/browser/welcomeOnboarding.contribution.ts IOnboardingService Delayed + 1 action (Delayed)
surveys/browser/nps.contribution.ts NPS survey (en-only) Restored
surveys/browser/languageSurveys.contribution.ts language surveys (en-only) Restored
bracketPairColorizer2Telemetry/browser/bracketPairColorizer2Telemetry.contribution.ts telemetry Restored
scrollLocking/browser/scrollLocking.contribution.ts scroll-lock action Eventually
dropOrPasteInto/browser/dropOrPasteInto.contribution.ts commands + schema Eventually ×2
opener/browser/opener.contribution.ts opener contrib Eventually
relauncher/browser/relauncher.contribution.ts 2 settings/workspace relaunchers Restored ×2
update/browser/update.contribution.ts 6 update widgets Restored ×5 + Eventually
sash/browser/sash.contribution.ts sash settings controller AfterRestored
languageDetection/browser/languageDetection.contribution.ts status contrib Restored

Why this is safe

The workbench contributions registry instantiates a contribution immediately if it is registered after its target phase has already fired (contributions.ts:170–180). All fifteen deferred modules register only via registerWorkbenchContribution2, which is retroactive.

Modules that use non-retroactive mechanisms were deliberately not included (or reverted, see review thread):

  • registerEditorFeature(...) — snapshot taken once by EditorFeaturesInstantiator at BlockRestore on first editor creation; not re-read.
  • registerEditorContribution(...)CodeEditorWidget snapshots EditorExtensionsRegistry at editor construction time.
  • registerEditorPane / registerEditorSerializer / registerExtensionPoint — consumed during workspace/editor restore or extension activation, which happen before Eventually.

That's why chatContext.contribution.ts, imageCarousel.contribution.ts, chatSessions.contribution.ts, agentSessions.contribution.ts, chatManagement.contribution.ts, aiCustomizationManagement.contribution.ts, the three prompt/plan-review editor contributions, and modules whose services are consumed by mainThread* bridges (share, editTelemetry, accessibilitySignals) are kept eager.

Impact

Transitive-closure analysis on the previous (18-module) variant of this set showed ~200 KB of TS source uniquely leaves the eager parse graph — most transitive imports are shared with other eager paths and cost nothing extra to dynamic-import. The current (15-module) set is similar in net size: the three reverted modules together accounted for ~30 KB of direct source plus minor transitive surface.

Estimated cold-start savings:

  • V8 parse + ignition compile: ~10–15 ms
  • Top-level registration work across 15 modules: ~5–15 ms
  • Total: ~15–30 ms on Windows, ~20–40 ms on Linux

First in a planned series of lazy-loading changes (tracked locally in perf-changes.md).

Trade-offs

  • Views (Agent Plugins, MCP Servers): appear in their view containers a few hundred ms later — not first-paint surfaces.
  • NPS / language surveys / update notifications / post-update widget: fire after Eventually instead of during Restored. All gated and rare.
  • Onboarding action / metered-connection status / drop-or-paste commands / opener / scroll-locking / sash settings / language-detection status: registered slightly later; no user-visible impact.
  • SettingsChangeRelauncher / WorkspaceChangeExtHostRelauncher: settings changes made in the first ~2.5 s of session won't be observed. Users do not normally change settings that early.

No functionality removed.

Validation

  • npm run compile-check-ts-native — clean
  • npm run valid-layers-check — clean
  • No external module references any of the deferred files by name (grep)
  • Pre-commit hygiene hook passed

Move ten contribution modules off the eager workbench bundle parse path:

- chat/browser/chat.view.contribution.ts (AfterRestored)
- mcp/browser/mcp.view.contribution.ts (AfterRestored)
- chat/browser/promptSyntax/promptCodingAgentActionContribution.ts (editor contrib, AfterFirstRender)
- chat/browser/promptSyntax/promptToolsCodeLensProvider.ts (editor feature / code lens)
- chat/browser/planReviewFeedback/planReviewFeedbackEditorContribution.ts (editor contrib, Eventually)
- services/policies/browser/accountPolicyGate.contribution.ts (AfterRestored)
- meteredConnection/browser/meteredConnection.contribution.ts (AfterRestored + 1 action)
- welcomeOnboarding/browser/welcomeOnboarding.contribution.ts (Delayed IOnboardingService + 1 action)
- surveys/browser/nps.contribution.ts (Restored, en-only)
- surveys/browser/languageSurveys.contribution.ts (Restored, en-only)

None have early consumers: they register views, code lenses, editor
contributions, or workbench contributions that only matter once an
editor is rendered or the user takes action. A small loader at
browser/workbench.contribution.lazy.ts registered at
WorkbenchPhase.Eventually dynamically imports them after restore.
Copilot AI review requested due to automatic review settings May 13, 2026 00:38
@dmitrivMS dmitrivMS added the perf label May 13, 2026
@dmitrivMS dmitrivMS self-assigned this May 13, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR reduces cold-start workbench bundle parsing by removing several non-critical contribution modules from the eager import graph and deferring them via a new lazy-loader contribution that runs at WorkbenchPhase.Eventually.

Changes:

  • Remove eager imports for several view/survey/onboarding/policy/metered-connection contributions from workbench.common.main.ts.
  • Stop eagerly importing certain chat editor-related contributions from chat.contribution.ts.
  • Add workbench.contribution.lazy.ts, registering a single Eventually workbench contribution that dynamic-import()s the deferred modules.
Show a summary per file
File Description
src/vs/workbench/workbench.common.main.ts Drops eager imports for select contributions and adds the new lazy-loader entrypoint import.
src/vs/workbench/contrib/chat/browser/chat.contribution.ts Removes eager imports for prompt/editor-related chat contributions so they can be deferred.
src/vs/workbench/browser/workbench.contribution.lazy.ts New Eventually workbench contribution that dynamic-imports the deferred contribution modules.

Copilot's findings

  • Files reviewed: 3/3 changed files
  • Comments generated: 1

Comment on lines +19 to +23
load(() => import('../contrib/chat/browser/chat.view.contribution.js'));
load(() => import('../contrib/mcp/browser/mcp.view.contribution.js'));
load(() => import('../contrib/chat/browser/promptSyntax/promptCodingAgentActionContribution.js'));
load(() => import('../contrib/chat/browser/promptSyntax/promptToolsCodeLensProvider.js'));
load(() => import('../contrib/chat/browser/planReviewFeedback/planReviewFeedbackEditorContribution.js'));
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — confirmed. EditorFeaturesInstantiator (editorFeatures.ts:13–53) snapshots getEditorFeatures() exactly once (gated by _instantiated) at BlockRestore / first editor creation, and CodeEditorWidget reads EditorExtensionsRegistry at construction time. Both are non-retroactive, so deferring these three modules to Eventually could leave them unattached.

Reverted in commit a833565f778promptCodingAgentActionContribution.ts, promptToolsCodeLensProvider.ts, and planReviewFeedbackEditorContribution.ts are back on the eager path. I also expanded the lazy set with eight more registerWorkbenchContribution2-only modules in the same commit, all of which are retroactive per contributions.ts:170–180.

@microsoft microsoft deleted a comment from github-actions Bot May 13, 2026
@dmitrivMS dmitrivMS changed the title perf: lazy-load non-critical contribution modules Lazily load non-critical contribution modules May 13, 2026
Address Copilot review feedback: revert lazy-loading of three modules
that use `registerEditorFeature` / `registerEditorContribution`. Those
registries are non-retroactive — `EditorFeaturesInstantiator` (registered
at `BlockRestore`) snapshots `getEditorFeatures()` once on first editor
creation, and `CodeEditorWidget` snapshots `EditorExtensionsRegistry`
at editor construction time.

Reverted to eager:
- chat/browser/promptSyntax/promptCodingAgentActionContribution.ts
- chat/browser/promptSyntax/promptToolsCodeLensProvider.ts
- chat/browser/planReviewFeedback/planReviewFeedbackEditorContribution.ts

Newly deferred (workbench-contribution-only, all retroactive via
`registerWorkbenchContribution2`):
- bracketPairColorizer2Telemetry.contribution.ts (Restored)
- scrollLocking.contribution.ts (Eventually)
- dropOrPasteInto.contribution.ts (Eventually x2)
- opener.contribution.ts (Eventually)
- relauncher.contribution.ts (Restored x2)
- update.contribution.ts (Restored x5 + Eventually)
- sash.contribution.ts (AfterRestored)
- languageDetection.contribution.ts (Restored)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants