Change:
During verification I saw and it raises an interesting challenge. Workbench contributions are registered for a at which they are instantiated. For the sake of performance we are interested in having very few contributions during early phases (so that they don't compete with editor opening). The recommendation is to use or and we are very good at sticking to this rule. However, workbench contributions are rarely self-contained. For instance, we often have commands to the side (). Those are typically registered statically but depend on what the contribution contributes. This means an action can be invoked before its "partner-contribution" is ready. To tackle this you can only register actions when the contribution is registered (which can lead to UI flashes and non-working commands) register contribution for a very early phase (which is correct but not great for perf) Another alternative is to let actions wait for the lifecycle phase of their "partner-contribution" (via ). Tho, that can be frustrating for users (extra waiting) and seems like code smell (spread the phase knowledge). A better alternative might be to ask the workbench to ensure the contribution is now ready. That would force-instantiate it and remove it from lifecycle-phase based instantiation. The code editor does something similar (it took inspiration from lifecycle phases but took this much further, see ) and it might be time to bring those learnings into the workbench. One difference tho is that workbench contributions often only cause side effects, e.g the presence of an editor type which isn't the contribution itself. It might be enough to begin with ). That function would simply ensure a contribution is loaded (instantiate it unless already done). With that we can add an equivalent to - meaning some workbench contributions are never initialised unless used. Many contributions from the -phase are likely candidates for this
I am all up for making workbench contributions instantiate only when actually needed because today its a wild zoo. Some random thoughts though: if we make changes here we should also invest in tooling to better visualise and measure exactly what kind of contributions instantiate when and how long that takes (e.g. from dev tools). even with our model today I find it very hard to understand why perf gets slower overall when restoring editors without knowing the change in contributions during a month one worry I have is that the new is unpredictable because it may actually result in very eager instantiation when the component that requires it is early on thus, maybe we combine with at least the phase, so that its not getting instantiated before that but will instantiate then and callers have a way to await that programmatically with the API you suggest
I share that concern but I think it depends how/why is called, in my mental model that's only from a user-actions (like some action or opening an editor) and not deterministically programatically.
Yeah I am all up for having that new capability and I think it allows us to move early contributions to later phases One thing I would like to explore is if we can make early contributions more visible (phases , ). For example, we could have a ESLint rule that requires people to register their intent in some file (similar to NLS) if they really must block the restore phase. This would require an identifier and a description. When we see perf regressions we can diff that file to see how contributions changed, if at all. We can also monitor our success in making more contributions instantiate lazy or late. On the perf dashboard I would log the identifier when creating the contribution and visualise the timings so that we can see trends.
When PR lands, we have: new with new a new I have adopted the new method for all contributions of and but we need an adoption issue for people to look into their contributions for adopting the new if possible.
Continues in

Discussion:
Registry.as<IWorkbenchContributionsRegistry>(Extensions.Workbench).registerWorkbenchContribution2( PerfviewContrib.ID, PerfviewContrib, <del> WorkbenchContributionInstantiation.BlockRestore </del> <ins> WorkbenchContributionInstantiation.Lazy </ins> ); Registry.as<IEditorFactoryRegistry>(EditorExtensions.EditorFactory).registerEditorSerializer(

run(accessor: ServicesAccessor) { const editorService = accessor.get(IEditorService); <del> const instaService = accessor.get(IInstantiationService); return editorService.openEditor(instaService.createInstance(PerfviewInput), { pinned: true }); </del> <ins> const contrib = PerfviewContrib.get(); return editorService.openEditor(contrib.getEditorInput(), { pinned: true }); </ins> } });

export class PerfviewContrib { <ins> static get() { return Registry. as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench) .getWorkbenchContribution<PerfviewContrib>(PerfviewContrib.ID); } </ins> static readonly ID = 'workbench.contrib.perfview'; <ins> private readonly _inputUri = URI.from({ scheme: 'perf', path: 'Startup Performance' }); </ins> private readonly _registration: IDisposable; constructor( <del> @IInstantiationService instaService: IInstantiationService, </del> <ins> @IInstantiationService private readonly _instaService: IInstantiationService, </ins> @ITextModelService textModelResolverService: ITextModelService ) { <del> this._registration = textModelResolverService.registerTextModelContentProvider('perf', instaService.createInstance(PerfModelContentProvider)); </del> <ins> this._registration = textModelResolverService.registerTextModelContentProvider('perf', _instaService.createInstance(PerfModelContentProvider)); </ins> } dispose(): void { this._registration.dispose(); } <ins> getInputUri(): URI { return this._inputUri; } getEditorInput(): PerfviewInput { return this._instaService.createInstance(PerfviewInput); } </ins> } export class PerfviewInput extends TextResourceEditorInput { static readonly Id = 'PerfviewInput'; <del> static readonly Uri = URI.from({ scheme: 'perf', path: 'Startup Performance' }); </del> override get typeId(): string { return PerfviewInput.Id;

@ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService ) { super( <del> PerfviewInput.Uri, </del> <ins> PerfviewContrib.get().getInputUri(), </ins> localize('name', "Startup Performance"), undefined, undefined,

import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; <del> import { PerfviewInput } from 'vs/workbench/contrib/performance/browser/perfviewEditor'; </del> <ins> import { PerfviewContrib } from 'vs/workbench/contrib/performance/browser/perfviewEditor'; </ins> import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { URI } from 'vs/base/common/uri';

return; } <del> const ref = await this._textModelResolverService.createModelReference(PerfviewInput.Uri); </del> <ins> const contrib = PerfviewContrib.get(); const ref = await this._textModelResolverService.createModelReference(contrib.getInputUri()); </ins> try { await this._clipboardService.writeText(ref.object.textEditorModel.getValue()); } finally {