From 7822f353b10ccf89ea09021895188475b6a0bd44 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 22 Feb 2021 11:21:17 -0800 Subject: [PATCH 1/7] Add parallel option --- .../CodeLens/CodeLensReferencesService.cs | 9 +++- .../FindReferencesSearchOptions.cs | 43 +++++++++++++++---- .../SymbolFinder_FindReferences_Legacy.cs | 2 +- 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/src/Features/Core/Portable/CodeLens/CodeLensReferencesService.cs b/src/Features/Core/Portable/CodeLens/CodeLensReferencesService.cs index f88628805530b..20d61b641a204 100644 --- a/src/Features/Core/Portable/CodeLens/CodeLensReferencesService.cs +++ b/src/Features/Core/Portable/CodeLens/CodeLensReferencesService.cs @@ -26,6 +26,11 @@ internal sealed class CodeLensReferencesService : ICodeLensReferencesService typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, memberOptions: SymbolDisplayMemberOptions.IncludeContainingType); + // Do not perform the finding operation in parallel. We're running ephemerally in the BG and do not want to + // saturate the system with work that then slows the user down. + private static readonly FindReferencesSearchOptions s_nonParallelSearch = + FindReferencesSearchOptions.Default.With(parallel: false); + private static async Task FindAsync(Solution solution, DocumentId documentId, SyntaxNode syntaxNode, Func> onResults, Func> onCapped, int searchCap, CancellationToken cancellationToken) where T : struct @@ -49,8 +54,8 @@ internal sealed class CodeLensReferencesService : ICodeLensReferencesService using var progress = new CodeLensFindReferencesProgress(symbol, syntaxNode, searchCap, cancellationToken); try { - await SymbolFinder.FindReferencesAsync(symbol, solution, progress, null, - progress.CancellationToken).ConfigureAwait(false); + await SymbolFinder.FindReferencesAsync( + symbol, solution, progress, documents: null, s_nonParallelSearch, progress.CancellationToken).ConfigureAwait(false); return await onResults(progress).ConfigureAwait(false); } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchOptions.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchOptions.cs index eccb2f2421ad5..9ccaa2473e46b 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchOptions.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchOptions.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Runtime.Serialization; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -15,7 +13,8 @@ internal sealed class FindReferencesSearchOptions public static readonly FindReferencesSearchOptions Default = new( associatePropertyReferencesWithSpecificAccessor: false, - cascade: true); + cascade: true, + parallel: true); /// /// When searching for property, associate specific references we find to the relevant @@ -39,19 +38,45 @@ internal sealed class FindReferencesSearchOptions [DataMember(Order = 1)] public bool Cascade { get; } + /// + /// Whether or not we should perform the find operation in parallel. This can produce results more quickly, but + /// at the cost of more system resources. + /// + /// + /// Features that run automatically should consider setting this to to avoid + /// unnecessarily impacting the user while they are doing other work. + /// + [DataMember(Order = 1)] + public bool Parallel { get; } + public FindReferencesSearchOptions( bool associatePropertyReferencesWithSpecificAccessor, - bool cascade) + bool cascade, + bool parallel) { AssociatePropertyReferencesWithSpecificAccessor = associatePropertyReferencesWithSpecificAccessor; Cascade = cascade; + Parallel = parallel; } - public FindReferencesSearchOptions WithAssociatePropertyReferencesWithSpecificAccessor(bool associatePropertyReferencesWithSpecificAccessor) - => new(associatePropertyReferencesWithSpecificAccessor, Cascade); + public FindReferencesSearchOptions With( + Optional associatePropertyReferencesWithSpecificAccessor = default, + Optional cascade = default, + Optional parallel = default) + { + var newAssociatePropertyReferencesWithSpecificAccessor = associatePropertyReferencesWithSpecificAccessor.HasValue ? associatePropertyReferencesWithSpecificAccessor.Value : AssociatePropertyReferencesWithSpecificAccessor; + var newCascade = cascade.HasValue ? cascade.Value : Cascade; + var newParallel = parallel.HasValue ? parallel.Value : Parallel; + + if (newAssociatePropertyReferencesWithSpecificAccessor == AssociatePropertyReferencesWithSpecificAccessor && + newCascade == Cascade && + newParallel == Parallel) + { + return this; + } - public FindReferencesSearchOptions WithCascade(bool cascade) - => new(AssociatePropertyReferencesWithSpecificAccessor, cascade); + return new FindReferencesSearchOptions(newAssociatePropertyReferencesWithSpecificAccessor, newCascade, newParallel); + } /// /// For IDE features, if the user starts searching on an accessor, then we want to give @@ -59,6 +84,6 @@ public FindReferencesSearchOptions WithCascade(bool cascade) /// then associate everything with the property. /// public static FindReferencesSearchOptions GetFeatureOptionsForStartingSymbol(ISymbol symbol) - => Default.WithAssociatePropertyReferencesWithSpecificAccessor(symbol.IsPropertyAccessor()); + => Default.With(associatePropertyReferencesWithSpecificAccessor: symbol.IsPropertyAccessor()); } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_FindReferences_Legacy.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_FindReferences_Legacy.cs index 8389c3d45c788..72dc8dc1151da 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_FindReferences_Legacy.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_FindReferences_Legacy.cs @@ -82,7 +82,7 @@ public static async Task> FindReferencesAsync( FindReferencesSearchOptions.Default, cancellationToken).ConfigureAwait(false); } - private static async Task> FindReferencesAsync( + internal static async Task> FindReferencesAsync( ISymbol symbol, Solution solution, IFindReferencesProgress progress, From 7a9f6e53ddcc0acc6cbd163c0b78e26d61849f30 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 22 Feb 2021 11:42:10 -0800 Subject: [PATCH 2/7] Run code on a particular scheduler --- .../FindReferences/FindReferencesSearchEngine.cs | 11 ++++++++++- .../FindReferencesSearchEngine_MapCreation.cs | 11 ++++++----- .../FindReferencesSearchEngine_ProjectProcessing.cs | 2 +- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs index 5ac5b885504da..381a01f0ae8c3 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs @@ -29,6 +29,12 @@ internal partial class FindReferencesSearchEngine private readonly CancellationToken _cancellationToken; private readonly FindReferencesSearchOptions _options; + /// + /// Scheduler to run our tasks on. If we're in mode, we'll + /// run all our tasks concurrently. Otherwise, we will run them serially. + /// + private readonly TaskScheduler _scheduler; + public FindReferencesSearchEngine( Solution solution, IImmutableSet? documents, @@ -45,6 +51,9 @@ public FindReferencesSearchEngine( _options = options; _progressTracker = progress.ProgressTracker; + + var pair = new ConcurrentExclusiveSchedulerPair(); + _scheduler = _options.Parallel ? pair.ConcurrentScheduler : pair.ExclusiveScheduler; } public async Task FindReferencesAsync(ISymbol symbol) @@ -87,7 +96,7 @@ private async Task ProcessAsync(ProjectToDocumentMap projectToDocumentMap) using var _ = ArrayBuilder.GetInstance(out var tasks); foreach (var (project, documentMap) in projectToDocumentMap) - tasks.Add(Task.Run(() => ProcessProjectAsync(project, documentMap), _cancellationToken)); + tasks.Add(Task.Factory.StartNew(() => ProcessProjectAsync(project, documentMap), _cancellationToken, TaskCreationOptions.None, _scheduler)); await Task.WhenAll(tasks).ConfigureAwait(false); } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_MapCreation.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_MapCreation.cs index a59c78e7eb398..41b9af1499709 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_MapCreation.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_MapCreation.cs @@ -34,8 +34,8 @@ private async Task CreateProjectToDocumentMapAsync(Project { foreach (var (symbol, finder) in projectQueue) { - tasks.Add(Task.Run(() => - DetermineDocumentsToSearchAsync(project, symbol, finder), _cancellationToken)); + tasks.Add(Task.Factory.StartNew(() => + DetermineDocumentsToSearchAsync(project, symbol, finder), _cancellationToken, TaskCreationOptions.None, _scheduler)); } } @@ -139,7 +139,7 @@ private async Task DetermineAllSymbolsCoreAsync( using var _ = ArrayBuilder.GetInstance(out var finderTasks); foreach (var f in _finders) { - finderTasks.Add(Task.Run(async () => + finderTasks.Add(Task.Factory.StartNew(async () => { using var _ = ArrayBuilder.GetInstance(out var symbolTasks); @@ -159,7 +159,7 @@ private async Task DetermineAllSymbolsCoreAsync( _cancellationToken.ThrowIfCancellationRequested(); await Task.WhenAll(symbolTasks).ConfigureAwait(false); - }, _cancellationToken)); + }, _cancellationToken, TaskCreationOptions.None, _scheduler)); } await Task.WhenAll(finderTasks).ConfigureAwait(false); @@ -177,7 +177,8 @@ private void AddSymbolTasks( { Contract.ThrowIfNull(child); _cancellationToken.ThrowIfCancellationRequested(); - symbolTasks.Add(Task.Run(() => DetermineAllSymbolsCoreAsync(child, result), _cancellationToken)); + symbolTasks.Add(Task.Factory.StartNew( + () => DetermineAllSymbolsCoreAsync(child, result), _cancellationToken, TaskCreationOptions.None, _scheduler)); } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_ProjectProcessing.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_ProjectProcessing.cs index aa7c49aae6f30..901af2deb9e58 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_ProjectProcessing.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_ProjectProcessing.cs @@ -30,7 +30,7 @@ private async Task ProcessProjectAsync( foreach (var (document, documentQueue) in documentMap) { if (document.Project == project) - documentTasks.Add(Task.Run(() => ProcessDocumentQueueAsync(document, documentQueue), _cancellationToken)); + documentTasks.Add(Task.Factory.StartNew(() => ProcessDocumentQueueAsync(document, documentQueue), _cancellationToken, TaskCreationOptions.None, _scheduler)); } await Task.WhenAll(documentTasks).ConfigureAwait(false); From 40c68337beef09c3f314716103fa3fc0b72ed20b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 22 Feb 2021 11:58:31 -0800 Subject: [PATCH 3/7] Have tests check both codepaths --- .../FindReferences/FindReferencesTests.vb | 21 +++++++++++++++---- ...lementExplicitlyCodeRefactoringProvider.cs | 2 +- ...tInlineTemporaryCodeRefactoringProvider.cs | 2 +- .../FindReferencesSearchEngine.cs | 2 +- .../FindReferencesSearchEngine_MapCreation.cs | 6 +++--- ...eferencesSearchEngine_ProjectProcessing.cs | 2 +- .../PropertyAccessorSymbolReferenceFinder.cs | 4 ++-- 7 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb index 2f1e8761c5f16..0a7d70958c2ca 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb @@ -51,10 +51,11 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences Await TestStreamingFeature(element, searchSingleFileOnly, uiVisibleOnly, host) End Function - Private Shared Async Function TestStreamingFeature(element As XElement, - searchSingleFileOnly As Boolean, - uiVisibleOnly As Boolean, - host As TestHost) As Task + Private Shared Async Function TestStreamingFeature( + element As XElement, + searchSingleFileOnly As Boolean, + uiVisibleOnly As Boolean, + host As TestHost) As Task ' We don't support testing features that only expect partial results. If searchSingleFileOnly OrElse uiVisibleOnly Then Return @@ -254,7 +255,19 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences Optional uiVisibleOnly As Boolean = False, Optional options As FindReferencesSearchOptions = Nothing) As Task + Await TestAPI(definition, host, parallel:=False, searchSingleFileOnly, uiVisibleOnly, options) + Await TestAPI(definition, host, parallel:=True, searchSingleFileOnly, uiVisibleOnly, options) + End Function + + Private Async Function TestAPI( + definition As XElement, + host As TestHost, + parallel As Boolean, + searchSingleFileOnly As Boolean, + uiVisibleOnly As Boolean, + options As FindReferencesSearchOptions) As Task options = If(options, FindReferencesSearchOptions.Default) + options = options.With(parallel:=parallel) Using workspace = TestWorkspace.Create(definition, composition:=s_composition.WithTestHostParts(host)) workspace.SetTestLogger(AddressOf _outputHelper.WriteLine) diff --git a/src/Features/CSharp/Portable/ImplementInterface/CSharpImplementExplicitlyCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ImplementInterface/CSharpImplementExplicitlyCodeRefactoringProvider.cs index b61bbfe4973a0..92aa021771479 100644 --- a/src/Features/CSharp/Portable/ImplementInterface/CSharpImplementExplicitlyCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ImplementInterface/CSharpImplementExplicitlyCodeRefactoringProvider.cs @@ -53,7 +53,7 @@ protected override async Task UpdateReferencesAsync( // // This can save a lot of extra time spent finding callers, especially for methods with // high fan-out (like IDisposable.Dispose()). - var findRefsOptions = FindReferencesSearchOptions.Default.WithCascade(false); + var findRefsOptions = FindReferencesSearchOptions.Default.With(cascade: false); var references = await SymbolFinder.FindReferencesAsync( implMember, solution, findRefsOptions, cancellationToken).ConfigureAwait(false); diff --git a/src/Features/Core/Portable/InlineTemporary/AbstractInlineTemporaryCodeRefactoringProvider.cs b/src/Features/Core/Portable/InlineTemporary/AbstractInlineTemporaryCodeRefactoringProvider.cs index 251a6111c6ee7..acff36fb962da 100644 --- a/src/Features/Core/Portable/InlineTemporary/AbstractInlineTemporaryCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/InlineTemporary/AbstractInlineTemporaryCodeRefactoringProvider.cs @@ -30,7 +30,7 @@ protected static async Task> GetReferenceLocat // Do not cascade when finding references to this local. Cascading can cause us to find linked // references as well which can throw things off for us. For inline variable, we only care about the // direct real references in this project context. - var options = FindReferencesSearchOptions.Default.WithCascade(cascade: false); + var options = FindReferencesSearchOptions.Default.With(cascade: false); var findReferencesResult = await SymbolFinder.FindReferencesAsync( local, document.Project.Solution, options, cancellationToken).ConfigureAwait(false); diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs index 381a01f0ae8c3..3d54d42a91893 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs @@ -96,7 +96,7 @@ private async Task ProcessAsync(ProjectToDocumentMap projectToDocumentMap) using var _ = ArrayBuilder.GetInstance(out var tasks); foreach (var (project, documentMap) in projectToDocumentMap) - tasks.Add(Task.Factory.StartNew(() => ProcessProjectAsync(project, documentMap), _cancellationToken, TaskCreationOptions.None, _scheduler)); + tasks.Add(Task.Factory.StartNew(() => ProcessProjectAsync(project, documentMap), _cancellationToken, TaskCreationOptions.None, _scheduler).Unwrap()); await Task.WhenAll(tasks).ConfigureAwait(false); } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_MapCreation.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_MapCreation.cs index 41b9af1499709..81c18d64475d3 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_MapCreation.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_MapCreation.cs @@ -35,7 +35,7 @@ private async Task CreateProjectToDocumentMapAsync(Project foreach (var (symbol, finder) in projectQueue) { tasks.Add(Task.Factory.StartNew(() => - DetermineDocumentsToSearchAsync(project, symbol, finder), _cancellationToken, TaskCreationOptions.None, _scheduler)); + DetermineDocumentsToSearchAsync(project, symbol, finder), _cancellationToken, TaskCreationOptions.None, _scheduler).Unwrap()); } } @@ -159,7 +159,7 @@ private async Task DetermineAllSymbolsCoreAsync( _cancellationToken.ThrowIfCancellationRequested(); await Task.WhenAll(symbolTasks).ConfigureAwait(false); - }, _cancellationToken, TaskCreationOptions.None, _scheduler)); + }, _cancellationToken, TaskCreationOptions.None, _scheduler).Unwrap()); } await Task.WhenAll(finderTasks).ConfigureAwait(false); @@ -178,7 +178,7 @@ private void AddSymbolTasks( Contract.ThrowIfNull(child); _cancellationToken.ThrowIfCancellationRequested(); symbolTasks.Add(Task.Factory.StartNew( - () => DetermineAllSymbolsCoreAsync(child, result), _cancellationToken, TaskCreationOptions.None, _scheduler)); + () => DetermineAllSymbolsCoreAsync(child, result), _cancellationToken, TaskCreationOptions.None, _scheduler).Unwrap()); } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_ProjectProcessing.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_ProjectProcessing.cs index 901af2deb9e58..e35744ee0a02d 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_ProjectProcessing.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_ProjectProcessing.cs @@ -30,7 +30,7 @@ private async Task ProcessProjectAsync( foreach (var (document, documentQueue) in documentMap) { if (document.Project == project) - documentTasks.Add(Task.Factory.StartNew(() => ProcessDocumentQueueAsync(document, documentQueue), _cancellationToken, TaskCreationOptions.None, _scheduler)); + documentTasks.Add(Task.Factory.StartNew(() => ProcessDocumentQueueAsync(document, documentQueue), _cancellationToken, TaskCreationOptions.None, _scheduler).Unwrap()); } await Task.WhenAll(documentTasks).ConfigureAwait(false); diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PropertyAccessorSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PropertyAccessorSymbolReferenceFinder.cs index 2831f558a81a0..a88717d9f8c3e 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PropertyAccessorSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PropertyAccessorSymbolReferenceFinder.cs @@ -56,7 +56,7 @@ protected override async Task> DetermineDocumentsToSear // defer to the Property finder to find these docs and combine them with the result. var propertyDocuments = await ReferenceFinders.Property.DetermineDocumentsToSearchAsync( property, project, documents, - options.WithAssociatePropertyReferencesWithSpecificAccessor(false), + options.With(associatePropertyReferencesWithSpecificAccessor: false), cancellationToken).ConfigureAwait(false); result = result.AddRange(propertyDocuments); @@ -77,7 +77,7 @@ protected override async ValueTask> FindReference { var propertyReferences = await ReferenceFinders.Property.FindReferencesInDocumentAsync( property, document, semanticModel, - options.WithAssociatePropertyReferencesWithSpecificAccessor(false), + options.With(associatePropertyReferencesWithSpecificAccessor: false), cancellationToken).ConfigureAwait(false); var syntaxFacts = document.GetRequiredLanguageService(); From de0659d527ec11bf370c5d7ea4a30b54a5944121 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 22 Feb 2021 12:09:48 -0800 Subject: [PATCH 4/7] message pack --- .../FindSymbols/FindReferences/FindReferencesSearchOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchOptions.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchOptions.cs index 9ccaa2473e46b..592d2b5995775 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchOptions.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchOptions.cs @@ -46,7 +46,7 @@ internal sealed class FindReferencesSearchOptions /// Features that run automatically should consider setting this to to avoid /// unnecessarily impacting the user while they are doing other work. /// - [DataMember(Order = 1)] + [DataMember(Order = 2)] public bool Parallel { get; } public FindReferencesSearchOptions( From bc7adda3b57ef860d057a43376b4275ad06bb40e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 22 Feb 2021 12:19:16 -0800 Subject: [PATCH 5/7] Feedback from stephen --- .../FindReferences/FindReferencesSearchEngine.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs index 3d54d42a91893..7ac4d8d4198c4 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs @@ -52,8 +52,10 @@ public FindReferencesSearchEngine( _progressTracker = progress.ProgressTracker; - var pair = new ConcurrentExclusiveSchedulerPair(); - _scheduler = _options.Parallel ? pair.ConcurrentScheduler : pair.ExclusiveScheduler; + // If we're running in parallel, just defer to the threadpool to execute all our work in parallel. If we're + // running serially, then use a ConcurrentExclusiveSchedulerPair as that's the most built-in way in the TPL + // to get a sheduler that can execute in that fashion. + _scheduler = _options.Parallel ? TaskScheduler.Default : new ConcurrentExclusiveSchedulerPair().ExclusiveScheduler; } public async Task FindReferencesAsync(ISymbol symbol) From b812e76d6e340f9f9aeaad89cab75378363c2710 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 22 Feb 2021 13:41:58 -0800 Subject: [PATCH 6/7] Rename option --- .../FindReferences/FindReferencesTests.vb | 8 ++++---- .../CodeLens/CodeLensReferencesService.cs | 7 ++++--- .../FindReferencesSearchEngine.cs | 9 +++++---- .../FindReferencesSearchOptions.cs | 20 +++++++++---------- 4 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb index 0a7d70958c2ca..a844959deb7a0 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb @@ -255,19 +255,19 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences Optional uiVisibleOnly As Boolean = False, Optional options As FindReferencesSearchOptions = Nothing) As Task - Await TestAPI(definition, host, parallel:=False, searchSingleFileOnly, uiVisibleOnly, options) - Await TestAPI(definition, host, parallel:=True, searchSingleFileOnly, uiVisibleOnly, options) + Await TestAPI(definition, host, explicit:=False, searchSingleFileOnly, uiVisibleOnly, options) + Await TestAPI(definition, host, explicit:=True, searchSingleFileOnly, uiVisibleOnly, options) End Function Private Async Function TestAPI( definition As XElement, host As TestHost, - parallel As Boolean, + explicit As Boolean, searchSingleFileOnly As Boolean, uiVisibleOnly As Boolean, options As FindReferencesSearchOptions) As Task options = If(options, FindReferencesSearchOptions.Default) - options = options.With(parallel:=parallel) + options = options.With(explicit:=explicit) Using workspace = TestWorkspace.Create(definition, composition:=s_composition.WithTestHostParts(host)) workspace.SetTestLogger(AddressOf _outputHelper.WriteLine) diff --git a/src/Features/Core/Portable/CodeLens/CodeLensReferencesService.cs b/src/Features/Core/Portable/CodeLens/CodeLensReferencesService.cs index 20d61b641a204..f25391ebe9b17 100644 --- a/src/Features/Core/Portable/CodeLens/CodeLensReferencesService.cs +++ b/src/Features/Core/Portable/CodeLens/CodeLensReferencesService.cs @@ -26,10 +26,11 @@ internal sealed class CodeLensReferencesService : ICodeLensReferencesService typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, memberOptions: SymbolDisplayMemberOptions.IncludeContainingType); - // Do not perform the finding operation in parallel. We're running ephemerally in the BG and do not want to - // saturate the system with work that then slows the user down. + // Set ourselves as an implicit invocation of FindReferences. This will cause the finding operation to operate + // in serial, not parallel. We're running ephemerally in the BG and do not want to saturate the system with + // work that then slows the user down. private static readonly FindReferencesSearchOptions s_nonParallelSearch = - FindReferencesSearchOptions.Default.With(parallel: false); + FindReferencesSearchOptions.Default.With(@explicit: false); private static async Task FindAsync(Solution solution, DocumentId documentId, SyntaxNode syntaxNode, Func> onResults, Func> onCapped, diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs index 7ac4d8d4198c4..fcf820edddd76 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs @@ -52,10 +52,11 @@ public FindReferencesSearchEngine( _progressTracker = progress.ProgressTracker; - // If we're running in parallel, just defer to the threadpool to execute all our work in parallel. If we're - // running serially, then use a ConcurrentExclusiveSchedulerPair as that's the most built-in way in the TPL - // to get a sheduler that can execute in that fashion. - _scheduler = _options.Parallel ? TaskScheduler.Default : new ConcurrentExclusiveSchedulerPair().ExclusiveScheduler; + // If we're an explicit invocation, just defer to the threadpool to execute all our work in parallel to get + // things done as quickly as possible. If we're running implicitly, then use a + // ConcurrentExclusiveSchedulerPair's exclusive scheduler as that's the most built-in way in the TPL to get + // will run things serially. + _scheduler = _options.Explicit ? TaskScheduler.Default : new ConcurrentExclusiveSchedulerPair().ExclusiveScheduler; } public async Task FindReferencesAsync(ISymbol symbol) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchOptions.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchOptions.cs index 592d2b5995775..0581091aa9f4d 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchOptions.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchOptions.cs @@ -14,7 +14,7 @@ internal sealed class FindReferencesSearchOptions new( associatePropertyReferencesWithSpecificAccessor: false, cascade: true, - parallel: true); + @explicit: true); /// /// When searching for property, associate specific references we find to the relevant @@ -39,43 +39,43 @@ internal sealed class FindReferencesSearchOptions public bool Cascade { get; } /// - /// Whether or not we should perform the find operation in parallel. This can produce results more quickly, but - /// at the cost of more system resources. + /// Whether or not this find ref operation was explicitly invoked or not. If explicit invoked, the find + /// references operation may use more resources to get the results faster. /// /// /// Features that run automatically should consider setting this to to avoid /// unnecessarily impacting the user while they are doing other work. /// [DataMember(Order = 2)] - public bool Parallel { get; } + public bool Explicit { get; } public FindReferencesSearchOptions( bool associatePropertyReferencesWithSpecificAccessor, bool cascade, - bool parallel) + bool @explicit) { AssociatePropertyReferencesWithSpecificAccessor = associatePropertyReferencesWithSpecificAccessor; Cascade = cascade; - Parallel = parallel; + Explicit = @explicit; } public FindReferencesSearchOptions With( Optional associatePropertyReferencesWithSpecificAccessor = default, Optional cascade = default, - Optional parallel = default) + Optional @explicit = default) { var newAssociatePropertyReferencesWithSpecificAccessor = associatePropertyReferencesWithSpecificAccessor.HasValue ? associatePropertyReferencesWithSpecificAccessor.Value : AssociatePropertyReferencesWithSpecificAccessor; var newCascade = cascade.HasValue ? cascade.Value : Cascade; - var newParallel = parallel.HasValue ? parallel.Value : Parallel; + var newExplicit = @explicit.HasValue ? @explicit.Value : Explicit; if (newAssociatePropertyReferencesWithSpecificAccessor == AssociatePropertyReferencesWithSpecificAccessor && newCascade == Cascade && - newParallel == Parallel) + newExplicit == Explicit) { return this; } - return new FindReferencesSearchOptions(newAssociatePropertyReferencesWithSpecificAccessor, newCascade, newParallel); + return new FindReferencesSearchOptions(newAssociatePropertyReferencesWithSpecificAccessor, newCascade, newExplicit); } /// From 5c5ee7989153e846e2d469b143460a1d5942fefd Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 22 Feb 2021 15:31:51 -0800 Subject: [PATCH 7/7] Single scheduler --- .../FindSymbols/FindReferences/FindReferencesSearchEngine.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs index 7ac4d8d4198c4..483b11f75cd7d 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs @@ -31,9 +31,10 @@ internal partial class FindReferencesSearchEngine /// /// Scheduler to run our tasks on. If we're in mode, we'll - /// run all our tasks concurrently. Otherwise, we will run them serially. + /// run all our tasks concurrently. Otherwise, we will run them serially using /// private readonly TaskScheduler _scheduler; + private static readonly TaskScheduler s_exclusiveScheduler = new ConcurrentExclusiveSchedulerPair().ExclusiveScheduler; public FindReferencesSearchEngine( Solution solution, @@ -55,7 +56,7 @@ public FindReferencesSearchEngine( // If we're running in parallel, just defer to the threadpool to execute all our work in parallel. If we're // running serially, then use a ConcurrentExclusiveSchedulerPair as that's the most built-in way in the TPL // to get a sheduler that can execute in that fashion. - _scheduler = _options.Parallel ? TaskScheduler.Default : new ConcurrentExclusiveSchedulerPair().ExclusiveScheduler; + _scheduler = _options.Parallel ? TaskScheduler.Default : s_exclusiveScheduler; } public async Task FindReferencesAsync(ISymbol symbol)