From c983c3accc73a9161c225cb61c48c4044dd6d062 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 10 Jun 2020 22:08:29 -0700 Subject: [PATCH] Process projects in parallel in FAR> --- .../FindReferencesSearchEngine.cs | 26 ++------ ...eferencesSearchEngine_ProjectProcessing.cs | 64 +------------------ 2 files changed, 7 insertions(+), 83 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs index 2c90cab0039..3002131b767 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.FindSymbols.Finders; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; @@ -78,33 +79,18 @@ private async Task ProcessAsync(ProjectToDocumentMap projectToDocumentMap) return; } - // Get the connected components of the dependency graph and process each individually. - // That way once a component is done we can throw away all the memory associated with - // it. - // For each connected component, we'll process the individual projects from bottom to - // top. i.e. we'll first process the projects with no dependencies. Then the projects - // that depend on those projects, and so on. This way we always have created the - // dependent compilations when they're needed by later projects. If we went the other - // way (i.e. processed the projects with lots of project dependencies first), then we'd - // have to create all their dependent compilations in order to get their compilation. - // This would be very expensive and would take a lot of time before we got our first - // result. - var connectedProjects = _dependencyGraph.GetDependencySets(_cancellationToken); - // Add a progress item for each (document, symbol, finder) set that we will execute. // We'll mark the item as completed in "ProcessDocumentAsync". var totalFindCount = projectToDocumentMap.Sum( kvp1 => kvp1.Value.Sum(kvp2 => kvp2.Value.Count)); await _progressTracker.AddItemsAsync(totalFindCount).ConfigureAwait(false); - // Now, go through each connected project set and process it independently. - foreach (var connectedProjectSet in connectedProjects) - { - _cancellationToken.ThrowIfCancellationRequested(); + using var _ = ArrayBuilder.GetInstance(out var tasks); - await ProcessProjectsAsync( - connectedProjectSet, projectToDocumentMap).ConfigureAwait(false); - } + foreach (var (project, documentMap) in projectToDocumentMap) + tasks.Add(Task.Run(() => ProcessProjectAsync(project, documentMap), _cancellationToken)); + + await Task.WhenAll(tasks).ConfigureAwait(false); } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_ProjectProcessing.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_ProjectProcessing.cs index f00b242cc02..365921bd4f8 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_ProjectProcessing.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_ProjectProcessing.cs @@ -12,72 +12,10 @@ namespace Microsoft.CodeAnalysis.FindSymbols { using DocumentMap = MultiDictionary; - using ProjectToDocumentMap = Dictionary>; internal partial class FindReferencesSearchEngine { - private async Task ProcessProjectsAsync( - IEnumerable connectedProjectSet, - ProjectToDocumentMap projectToDocumentMap) - { - var visitedProjects = new HashSet(); - - // Make sure we process each project in the set. Process each project in depth first - // order. That way when we process a project, the compilations for all projects that it - // depends on will have been created already. - foreach (var projectId in connectedProjectSet) - { - _cancellationToken.ThrowIfCancellationRequested(); - - await ProcessProjectAsync( - projectId, projectToDocumentMap, visitedProjects).ConfigureAwait(false); - } - } - - private async Task ProcessProjectAsync( - ProjectId projectId, - ProjectToDocumentMap projectToDocumentMap, - HashSet visitedProjects) - { - // Don't visit projects more than once. - if (visitedProjects.Add(projectId)) - { - var project = _solution.GetProject(projectId); - - // Visit dependencies first. That way the compilation for a project that we depend - // on is already ready for us when we need it. - foreach (var dependent in project.ProjectReferences) - { - _cancellationToken.ThrowIfCancellationRequested(); - - await ProcessProjectAsync( - dependent.ProjectId, projectToDocumentMap, visitedProjects).ConfigureAwait(false); - } - - await ProcessProjectAsync(project, projectToDocumentMap).ConfigureAwait(false); - } - } - - private async Task ProcessProjectAsync( - Project project, - ProjectToDocumentMap projectToDocumentMap) - { - if (!projectToDocumentMap.TryGetValue(project, out var documentMap)) - { - // No files in this project to process. We can bail here. We'll have cached our - // compilation if there are any projects left to process that depend on us. - return; - } - - projectToDocumentMap.Remove(project); - - // Now actually process the project. - await ProcessProjectAsync(project, documentMap).ConfigureAwait(false); - } - - private async Task ProcessProjectAsync( - Project project, - DocumentMap documentMap) + private async Task ProcessProjectAsync(Project project, DocumentMap documentMap) { using (Logger.LogBlock(FunctionId.FindReference_ProcessProjectAsync, project.Name, _cancellationToken)) { -- GitLab