-
Notifications
You must be signed in to change notification settings - Fork 785
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Improve solution load performance #2909
Conversation
… running asyncronously after load
I could do with a hand with the following issues:
|
@@ -208,7 +209,7 @@ type | |||
CodeSense = true, | |||
DefaultToNonHotURLs = true, | |||
EnableCommenting = true, | |||
CodeSenseDelay = 100, | |||
CodeSenseDelay = 0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to actually investigate if it's made a difference...
is there already noticable difference? |
@forki with regards to what? |
open Microsoft.Build.Execution | ||
|
||
(* | ||
[<Export(typeof<ProjectFlagsService>)>] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just [<Export>]
would do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe make it a Workspace Service instead like this http://source.roslyn.io/#Microsoft.CodeAnalysis.EditorFeatures/Shared/DefaultDocumentSupportsSuggestionService.cs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Under the hood it still just uses ExportAttribute though - so I doubt that would help. Thanks for the input though :)
type internal ProjectFlagsService | ||
[<ImportingConstructor>] | ||
( | ||
[<Import(typeof<SVsBuildManagerAccessor>)>] accessor : IVsBuildManagerAccessor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure you'll get this from MEF.
IServiceProvider GetService maybe?
like here:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I'll give that a shot tomorrow.
Also in project.fs it looks like I need to call GetService on IComponentModel instead of on this.Site to actually get a reference to ProjectFlagsService 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I experimented a bit and it turns out exports from other assemblies like FSharp.Editor work fine and are resolved with IComponentModel. Anything declared here fails.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's bizarre, thanks for helping me with this. @cartermp do you know anyone who could weigh in/explain why our project system assembly doesn't seem to be linked up to MEF?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have no idea. @dsyme would you know, or know who might know?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@saul @cartermp I'd ask @OmarTawfik, @brettfo, @KevinRansom . It may be just historical. Note I know almost nothing about MEF
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@saul might be helpful, too https://github.com/Microsoft/vs-mef
@@ -1558,7 +1608,7 @@ namespace rec Microsoft.VisualStudio.FSharp.ProjectSystem | |||
Debug.Assert(System.Threading.Thread.CurrentThread.ManagedThreadId = uiThreadId, "called GetProjectSite() while still in Opening state from non-UI thread") | |||
#endif | |||
x.ComputeSourcesAndFlags() | |||
if not(projectSite.State = ProjectSiteOptionLifetimeState.Opened) then | |||
if projectSite.State <> ProjectSiteOptionLifetimeState.Opened then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 :)
|> Seq.map(fun ref -> (getProject ref.ProjectId).FilePath) | ||
|> hashSetIgnoreCase | ||
|
||
for referencedSite in do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updatedProjects.Except(workspaceProjects)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I was wondering that... will change them to use that tomorrow 👍
@saul I guess my question is if this work already shows performance benefits for your projects. |
@forki as in the PR description:
So yes, in large solutions with dense dependency graphs, it's essentially gone from impossible to load to 20 seconds 😃 |
let solution = workspace.CurrentSolution | ||
let documentIds = solution.GetDocumentIdsWithFilePath(fileName) | ||
if not documentIds.IsEmpty then | ||
analyzerService.Reanalyze(workspace, documentIds=documentIds, highPriority=true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also wondering if this is going to get us faster squiggles in the editor. Will probably move this change out of this PR though.
@@ -316,29 +328,50 @@ and | |||
projectContext.RemoveSourceFile(file) | |||
updated <- true | |||
|
|||
let updatedRefs = site.AssemblyReferences() |> hashSetIgnoreCase | |||
(* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will be put back - it's commented out as it's causing issues at the moment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I may be wrong but IIRC project updates must run on UI thread, contrary to what some comments in Roslyn say.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fine with this PR - the sources/flags notifier (which eventually calls through to this) is running on the UI thread.
@@ -697,7 +697,7 @@ public override string Url | |||
|
|||
public void SetDebugLogger(Microsoft.Build.Framework.ILogger debugLogger) | |||
{ | |||
myDebugLogger = debugLogger; | |||
//myDebugLogger = debugLogger; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Temporary change, should not have committed
How could I miss that. Probably just read the email. |
Yeah I added the perf stats in after I posted. I assume you've got the repo setup to email you immediately whenever there's action 😆 |
With regards to being able to export/get the ProjectFlagsService - any more ideas on this? Even if I use just cc @majocha |
@saul from what I gather ProjectPackage, where you want to import it, is not MEF composed itself. To get the ProjectFlagsService there you would need to go through SComponentModel. I tried to get SComponentModel service and then import a small dummy part. It fails, so it's not just ProjectFlagsService. |
@majocha yeah I was hoping to use it in FSharp.Editor. I want to call PrioritiseProject when a document is opened. |
…ion (add support changing project references) Fixes dotnet#2916, fixes dotnet#1944
In the latest commit I've fixed #1944 and #2916 as I moved some LanguageService code over to use IVsRunningDocumentTable instead of hooking into SetupNewTextView (that's been removed entirely). I've also re-enabled the Roslyn workspace syncronisation that I disabled in the previous commit. Also some more stats - the VisualFSharp solution now loads in 10 seconds (down from 20). |
That's so awesome. Can we already merge it and just iterate on it?
Am 25.04.2017 6:24 nachm. schrieb "Saul Rennison" <notifications@github.com
:
In the latest commit I've fixed #1944
<#1944> and #2916
<#2916> as I moved some
LanguageService code over to use IVsRunningDocumentTable instead of hooking
into SetupNewTextView (that's been removed entirely). I've also re-enabled
the Roslyn workspace syncronisation that I disabled in the previous commit.
Also some more stats - the VisualFSharp solution now loads in 10 seconds
(down from 20).
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#2909 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AADgNO1SIxFSDbiCY0zD_KRo4CECVhoSks5rzh46gaJpZM4NGYh5>
.
|
Awesome. |
@vasily-kirichenko I believe that's because some of the references aren't of a legal form. I'm now calling Path.GetFullPath to normalise the paths (some were like C:\Foo\Bar....\Something... and so were failing the equality check) |
@saul I tried to load FSharp.Configuration - it loaded insanely fast, but nothing works (coloring, etc.), no errors anywhere. |
@vasily-kirichenko @dsyme I think I've found the cause of why you aren't getting semantic colourisation. (I do get block structure and syntactic). See the reactor log https://gist.github.com/saul/bc9d2ef16d8d9b43e4c44f2d2c211b9b Search that log for long running tasks. Especially noteworthy:
I've no idea why these are taking so long... time to break out the profiler? 😦 |
} | ||
|
||
async { | ||
while true do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This basically looks very clear. But I'd somehow like to see some safeguards where we try/catch any exceptions in chunks of this code. Infinite loops starting mailbox processors make me nervous if exceptions might happen.
And are we sure the solution events get raised reliably?
Basically I'm wondering "what can go catastrophically wrong that can cause this service to malfunction in really bad ways"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confident that the solution events get raised reliably, yes.
With regards to try/catches, where abouts would you like those? It's worth noting that the project system DoMSBuildSubmission is already very resilient to exceptions and will always call our callback on the UI thread.
|
||
// Keep trying to grab the exclusive lock for a design time build | ||
while accessor.BeginDesignTimeBuild() |> ErrorHandler.Failed do | ||
do! Async.Sleep 2000 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2sec is a long pause - does it need to be so long?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No I'll reduce this to less than a second.
// Having an exclusive lock on design-time builds means the user | ||
// can't initiate any builds themselves. Wait a few seconds before | ||
// trying to grab one again. | ||
do! Async.Sleep 2000 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Likewise - why such a comparatively long pause?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It leaves some time so that the user can initiate builds/move files around in the Solution Explorer. When a design-time build is running, quite a few parts of the UI are locked out.
@@ -121,26 +127,38 @@ type internal ProjectSitesAndFiles() = | |||
| _ -> None) | |||
| None -> Seq.empty | |||
|
|||
static let projectCache = ConcurrentDictionary<string,FSharpProjectOptions>() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What removes entries from this cache?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing yet - good catch
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
UnresolvedReferences = None | ||
OriginalLoadReferences = [] | ||
ExtraProjectInfo=extraProjectInfo } | ||
and getProjectOptionsForProjectSite(projectSite:IProjectSite, fileName:string, extraProjectInfo, serviceProvider, cacheUsage:CacheUsage) = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will clash with my other proposed fix. The project options are already stored in a table in LanguageService.fs, it seems to me we should just callback to there to look up the table.
@@ -76,7 +76,7 @@ public override string Url | |||
{ | |||
get | |||
{ | |||
return this.myAssemblyPath; | |||
return Path.GetFullPath(this.myAssemblyPath); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fix still looks useful?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was used by the caching code to determine if two references were the same. this.myAssemblyPath
is not absolute and so it made comparing them difficult when the rooted-ness was inconsistent within a solution. It's not a bug fix per se.
|
||
string targetFrameworkMoniker = (string)otherTargetFrameworkMonikerObj; | ||
object otargetFrameworkMoniker = null; | ||
int hr = hierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID4.VSHPROPID_TargetFrameworkMoniker, out otargetFrameworkMoniker); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this the correctness fix?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No this stops an occasional assertion on startup
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, thanks
| _ -> () | ||
|
||
member this.TrackDocument(fileName) = | ||
match VsRunningDocumentTable.FindDocumentWithoutLocking(package.RunningDocumentTable, fileName) with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dsyme this file essentially encapsulates the correctness fixes. However with 15.3 now in preview we can move over to the Miscellaneous Files workspace. Read the comments on dotnet/roslyn#17263 and my comment about Miscellaneous Files in this file.
@saul This PR is now really old, and I will close it. Can you reopen or resubmit if you want to resurect it? thanks |
Fixes #2793, fixes #2916, fixes #1944.
This is a very WIP pull request that improves solution load performance by moving ComputeSourcesAndFlags from being a UI thread operation to a background thread operation.
The design consists of a new ProjectFlagsService which processes a queue of projects that are dirty and need their sources/flags recalculating. It takes an exclusive lock on VS's design-time build and then runs an asynchronous build of the project (stopping just before invoking the compiler).
This PR also contains changes to ProjectsSitesAndFiles options caching. These changes mean that the Dense solution loads in 20 seconds instead of hanging VS forever. The ProjectsSitesAndFiles changes brought the load time down to 2:50mins, and the off-loading of ComputeSourcesAndFlags brings it down further to 20 seconds.
Remaining work