Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Ensure that MEF is initialized using an old style non-async package #1560

Closed
wants to merge 2 commits into from

Conversation

jcansdale
Copy link
Collaborator

@jcansdale jcansdale commented Mar 22, 2018

This workaround causes the Scanning new and updated MEF components... dialog to appear. Without this the GitHub pane will be blamed for degrading startup performance.

What this PR does

It provokes MEF to initialize itself using an old style non-async package. This will happen before any tool windows are activated in the GitSccProvider auto-load context.

All the InitializeMefPackage package does is call GetService(typeof(SComponentModel)) when it initializes. Visual Studio knows how to handle synchronous initialization of MEF and pops up the Scanning new and updated MEF components... dialog.

The InitializeMefPackage package will only cause the GitHub.VisualStudio assembly to load, and uses an auto-load context we already use in GitContextPackage. It shouldn't degrade performance beyond the initial MEF initialization hit immediately after install.

How to test

  • Open Visual Studio 2017
  • Ensure the GitHub pane is visible
  • Click on Tools > Clear MEF Component Cache...
  • Allow Visual Studio to restart
  • If the fix is working you will see:
    image
  • If not would would see:
    image

NOTE: Visual Studio shows this banner based on an average startup time. You might need to do this a few times to being the average startup time back down. 😢 It should then consistently not appear.

Notes

I tried more sophisticated approaches but they didn't work nearly as well (or at all). For example:

When MEF is refreshing on a background thread, no Scanning new and updated MEF components... dialog appears. There were other issues that I wasn't able to resolve.

We could probably resolve this issue by converting the GitHub pane into an async initializing tool window. Unfortunately this is only supported on recent versions of Visual Studio 2017. We would need to support two code paths and deal with separate Visual Studio 2017 assemblies.

Related

Fixes #1550

Ensure that MEF is initialized using an old style non-async package.
This causes the Scanning new and updated MEF components... dialog to appear.
Without this the GitHub pane will be blamed for degrading startup performance.
@bchavez
Copy link

bchavez commented Mar 23, 2018

Looks great @jcansdale. Fantastic work m8. No performance warning banner. Legit improvements IMHO. :shipit:

vmware_1398

vmware_1399

Tested both Visual Studio versions with the same result

vmware_1400

vmware_1401

👍 ✌️ "Because we can, can, can... Yes we can, can, can, can..."

@jcansdale
Copy link
Collaborator Author

@bchavez Thanks for testing it. I'm relieved to hear it's working! 👍

Pre-initializing MEF is only required by GitHubPane.
@AArnott
Copy link

AArnott commented Mar 26, 2018

I'm surprised that this actually avoids the warning infobar. It seems like it would just shift it to a different package, but still from your VSIX. @BertanAygun does this make sense to you?

/// </summary>
protected override void Initialize()
{
GetService(typeof(SComponentModel));
Copy link

Choose a reason for hiding this comment

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

Can we remove this line and fix the tool window to not synchronously block on this service? Your tool window initialization could display something else while asynchronously awaiting MEF's initialization, thus the user experience is that VS starts up much more quickly, even if github's tool window is visible.
The VS 15.6 async tool window feature is basically a temporary control displayed while the window initializes, so you could make that up in your VSIX yourself and thus support it downlevel on VS 2015.

// This package must auto-load before its tool window is activated on startup.
// We're using the GitSccProvider UI context because this is triggered before
// tool windows are activated and we already use it elsewhere (the containing
// assembly would have been loaded anyway).
Copy link
Contributor

Choose a reason for hiding this comment

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

VS Shell will already ensure your package is loaded before the tool window is activated so you don't have to do it here.

This auto load ensures that your package is loaded for every startup session regardless of your tool window is open slowing down startup for all users instead of just those with the toolwindow visible.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

VS Shell will already ensure your package is loaded before the tool window is activated so you don't have to do it here.

If we allow that to happen, our package gets blamed for MEF initialization (which can take 10 seconds 😨).

This auto load ensures that your package is loaded for every startup session regardless of your tool window is open slowing down startup for all users instead of just those with the toolwindow visible.

We need to auto-load on this context anyway in order to install a custom UIContext. That's why I wasn't too concerned about loading this assembly.

Copy link
Contributor

Choose a reason for hiding this comment

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

As you pointed, the cost is still there just becomes part of SCC package which is unique in this case since it is the one that causes your auto load unlike other scenarios. At the end of the day startup is still impacted by 10 seconds which is the important part. I have to mention that SCC hiding cost of auto loads from the UI context it sets is a known issue that we will fix soon btw.

As for your auto load reason, would you be able to use a rule based UI context to activate your UI context instead of auto loading a package?

Copy link
Collaborator Author

@jcansdale jcansdale Mar 26, 2018

Choose a reason for hiding this comment

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

I have to mention that SCC hiding cost of auto loads from the UI context it sets is a known issue that we will fix soon btw.

Thanks, this is good to know.

As for your auto load reason, would you be able to use a rule based UI context to activate your UI context instead of auto loading a package?

Our UI context is a subset of the GitSccProvider context (which appears to activate if VS was using Git the last time it launched). Is there a way to implement a custom UI context that doesn't involve loading a package?

Am I correct in thinking that a rule based UI context is a composite of existing UI context (you can't use them to create a completely custom context)?

Copy link
Contributor

@BertanAygun BertanAygun Mar 26, 2018

Choose a reason for hiding this comment

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

What are the conditions that you set the UI context in? If it is GIT SCC context and presence of your package, you can just define a rule based UI contexts that is only based on GIT SCC context. Since the rule based UI context will only be added when your pkgdef is deployed it will only be active when GIT SCC is active and user has your extension installed.

https://msdn.microsoft.com/en-us/library/mt750411.aspx

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

What are the conditions that you set the UI context in?

Here is pseudo code for what we need:
If in GitSccProvider context && IGitExt.ActiveRepositories.Count > 0

We're pretty much always in the GitSccProvider context, because it's active even when there is no loaded solution but the previously loaded solution was in a Git repo. 😕 We therefore check the IGitExt service to find out if we're really in the context of an active repo.

We have made efforts to only load when absolutely necessary! 😄

using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.ComponentModelHost;

namespace GitHub.VisualStudio
Copy link
Contributor

Choose a reason for hiding this comment

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

I would not approve this change as this doesn't really solve any performance issues. In fact it seems to make things worse as it adds synchronous query of MEF to startup path for those users that have Git provider set as default source control provider.

@BertanAygun
Copy link
Contributor

As @AArnott stated this would not solve any performance issues and just move cost between packages but since both packages are from same extension. If you run Visual Studio with /log switch, the activity log should output inclusive cost of each package and tool window to help with diagnosis.

@jcansdale
Copy link
Collaborator Author

jcansdale commented Mar 26, 2018

@BertanAygun, @AArnott,

If you run Visual Studio with /log switch, the activity log should output inclusive cost of each package and tool window to help with diagnosis.

I've just tried that with this build. It appears that by using [ProvideAutoLoad(Guids.GitSccProviderId)], I'm passing the blame for GetService(typeof(SComponentModel)) on to the Visual Studio Source Control Integration Package.

image

I believe this is what we were inadvertently doing in the past and why we never triggered the startup performance warning until we moved to an AsyncPackage.

The thing is, we do want to trigger the following dialog so users know what is going on:

image

Is there any other way to ensure that this dialog gets displayed short of calling GetService(typeof(SComponentModel)) from an auto-loading, non-async Package?

@BertanAygun
Copy link
Contributor

@jcansdale, I would really recommend avoiding initializing MEF synchronously. Andrew has already good recommendations to be able to do that and starting in 15.6 you can use async tool window to make some of those changes easier.

Unfortunately initializing MEF synchronously in startup will make sure all users that have GitHub installed to have really bad startup experience.

@jcansdale
Copy link
Collaborator Author

@AArnott, @BertanAygun,

There's an implementation of what you suggested here:

If you could take a look/review, that would be very much appreciated!

Unfortunately initializing MEF synchronously in startup will make sure all users that have GitHub installed to have really bad startup experience.

Before writing the above PR, I assumed the hit from MEF was only from when it initializes its cache after install. I hadn't appreciated the few second delay the first time the SCompoentModel is requested. I see why you were keep we don't auto-load it! 😉

@jcansdale
Copy link
Collaborator Author

Made obsolete by #1569.

@jcansdale jcansdale closed this Apr 3, 2018
@jcansdale jcansdale deleted the fixes/1550-initialize-mef branch April 3, 2018 19:48
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

GitHub pane causes performance warning banner
4 participants