提交 2e8656bf 编写于 作者: M Manish Vasani

Fix for perf issues in CompilationWithAnalyzers found from perf traces

1. Check cancellation at multiple places.
2. Run partial tree completion logic sequentially when `AnalysisOptions.ConcurrentAnalysis = false`
上级 4726029a
......@@ -135,6 +135,7 @@ private PerAnalyzerState GetAnalyzerState(DiagnosticAnalyzer analyzer)
AnalyzerDriver driver,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
lock (_gate)
{
if (_treesWithGeneratedSourceEvents.Contains(tree))
......@@ -147,6 +148,7 @@ private PerAnalyzerState GetAnalyzerState(DiagnosticAnalyzer analyzer)
var symbols = GetDeclaredSymbolsInTree(tree, compilation, getCachedSemanticModel, cancellationToken);
var compilationEvents = CreateCompilationEventsForTree(symbols.Concat(globalNs), tree, compilation);
cancellationToken.ThrowIfCancellationRequested();
lock (_gate)
{
if (_treesWithGeneratedSourceEvents.Contains(tree))
......@@ -191,6 +193,7 @@ private static ImmutableArray<CompilationEvent> CreateCompilationEventsForTree(I
private void GenerateSimulatedCompilationNonSourceEvent(Compilation compilation, AnalyzerDriver driver, bool started, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
lock (_gate)
{
var eventAlreadyGenerated = started ? _compilationStartGenerated : _compilationEndGenerated;
......@@ -218,6 +221,7 @@ public async Task OnCompilationEventsGeneratedAsync(ImmutableArray<CompilationEv
{
await EnsureAnalyzerActionCountsInitializedAsync(driver, cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
lock (_gate)
{
OnCompilationEventsGenerated_NoLock(compilationEvents, filterTreeOpt: null, driver: driver, cancellationToken: cancellationToken);
......@@ -488,8 +492,9 @@ public async Task OnCompilationEventProcessedAsync(CompilationEvent compilationE
/// <summary>
/// Gets pending events for given set of analyzers for the given syntax tree.
/// </summary>
public ImmutableArray<CompilationEvent> GetPendingEvents(ImmutableArray<DiagnosticAnalyzer> analyzers, SyntaxTree tree)
public ImmutableArray<CompilationEvent> GetPendingEvents(ImmutableArray<DiagnosticAnalyzer> analyzers, SyntaxTree tree, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
lock (_gate)
{
return GetPendingEvents_NoLock(analyzers, tree);
......@@ -544,8 +549,14 @@ private ImmutableArray<CompilationEvent> GetPendingEvents_NoLock(ImmutableArray<
/// <param name="analyzers"></param>
/// <param name="includeSourceEvents">Indicates if source events (symbol declared, compilation unit completed event) should be included.</param>
/// <param name="includeNonSourceEvents">Indicates if compilation wide events (compilation started and completed event) should be included.</param>
public ImmutableArray<CompilationEvent> GetPendingEvents(ImmutableArray<DiagnosticAnalyzer> analyzers, bool includeSourceEvents, bool includeNonSourceEvents)
/// <param name="cancellationToken">Cancellation token.</param>
public ImmutableArray<CompilationEvent> GetPendingEvents(
ImmutableArray<DiagnosticAnalyzer> analyzers,
bool includeSourceEvents,
bool includeNonSourceEvents,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
lock (_gate)
{
return GetPendingEvents_NoLock(analyzers, includeSourceEvents, includeNonSourceEvents);
......@@ -628,11 +639,11 @@ public bool HasPendingSyntaxAnalysis(AnalysisScope analysisScope)
/// <summary>
/// Returns true if we have any pending symbol analysis for given analysis scope.
/// </summary>
public bool HasPendingSymbolAnalysis(AnalysisScope analysisScope)
public bool HasPendingSymbolAnalysis(AnalysisScope analysisScope, CancellationToken cancellationToken)
{
Debug.Assert(analysisScope.FilterTreeOpt != null);
var symbolDeclaredEvents = GetPendingSymbolDeclaredEvents(analysisScope.FilterTreeOpt);
var symbolDeclaredEvents = GetPendingSymbolDeclaredEvents(analysisScope.FilterTreeOpt, cancellationToken);
foreach (var symbolDeclaredEvent in symbolDeclaredEvents)
{
if (analysisScope.ShouldAnalyze(symbolDeclaredEvent.Symbol))
......@@ -651,10 +662,11 @@ public bool HasPendingSymbolAnalysis(AnalysisScope analysisScope)
return false;
}
private ImmutableArray<SymbolDeclaredCompilationEvent> GetPendingSymbolDeclaredEvents(SyntaxTree tree)
private ImmutableArray<SymbolDeclaredCompilationEvent> GetPendingSymbolDeclaredEvents(SyntaxTree tree, CancellationToken cancellationToken)
{
Debug.Assert(tree != null);
cancellationToken.ThrowIfCancellationRequested();
lock (_gate)
{
HashSet<CompilationEvent> compilationEvents;
......
......@@ -355,7 +355,7 @@ private async Task<ImmutableArray<Diagnostic>> GetAnalyzerCompilationDiagnostics
var diagnostics = ImmutableArray<Diagnostic>.Empty;
var analysisScope = new AnalysisScope(_compilation, analyzers, _analysisOptions.ConcurrentAnalysis, categorizeDiagnostics: true);
Func<AsyncQueue<CompilationEvent>> getEventQueue = () =>
GetPendingEvents(analyzers, includeSourceEvents: true, includeNonSourceEvents: true);
GetPendingEvents(analyzers, includeSourceEvents: true, includeNonSourceEvents: true, cancellationToken);
// Compute the analyzer diagnostics for the given analysis scope.
await ComputeAnalyzerDiagnosticsAsync(analysisScope, getEventQueue, newTaskToken: 0, cancellationToken: cancellationToken).ConfigureAwait(false);
......@@ -580,18 +580,20 @@ private async Task<ImmutableArray<Diagnostic>> GetAnalyzerSemanticDiagnosticsCor
{
var pendingAnalysisScope = pendingAnalyzers.Length < analyzers.Length ? analysisScope.WithAnalyzers(pendingAnalyzers) : analysisScope;
Func<AsyncQueue<CompilationEvent>> getEventQueue = () => GetPendingEvents(analyzers, model.SyntaxTree);
Func<AsyncQueue<CompilationEvent>> getEventQueue = () => GetPendingEvents(analyzers, model.SyntaxTree, cancellationToken);
// Compute the analyzer diagnostics for the given analysis scope.
// We need to loop till symbol analysis is complete for any partial symbols being processed for other tree diagnostic requests.
do
{
cancellationToken.ThrowIfCancellationRequested();
(ImmutableArray<CompilationEvent> compilationEvents, bool hasSymbolStartActions) = await ComputeAnalyzerDiagnosticsAsync(pendingAnalysisScope, getEventQueue, taskToken, cancellationToken).ConfigureAwait(false);
if (hasSymbolStartActions)
{
await processPartialSymbolLocationsAsync(compilationEvents, analysisScope).ConfigureAwait(false);
}
} while (_analysisOptions.ConcurrentAnalysis && _analysisState.HasPendingSymbolAnalysis(pendingAnalysisScope));
} while (_analysisOptions.ConcurrentAnalysis && _analysisState.HasPendingSymbolAnalysis(pendingAnalysisScope, cancellationToken));
if (_analysisOptions.ConcurrentAnalysis)
{
......@@ -642,12 +644,24 @@ async Task processPartialSymbolLocationsAsync(ImmutableArray<CompilationEvent> c
if (partialTrees != null)
{
await Task.WhenAll(partialTrees.Select(t =>
Task.Run(() =>
if (AnalysisOptions.ConcurrentAnalysis)
{
await Task.WhenAll(partialTrees.Select(tree =>
Task.Run(() =>
{
var treeModel = _compilationData.GetOrCreateCachedSemanticModel(tree, _compilation, cancellationToken);
return GetAnalyzerSemanticDiagnosticsAsync(treeModel, filterSpan: null, cancellationToken);
}, cancellationToken))).ConfigureAwait(false);
}
else
{
foreach (var tree in partialTrees)
{
var treeModel = _compilationData.GetOrCreateCachedSemanticModel(t, _compilation, cancellationToken);
return GetAnalyzerSemanticDiagnosticsAsync(treeModel, filterSpan: null, cancellationToken);
}))).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
var treeModel = _compilationData.GetOrCreateCachedSemanticModel(tree, _compilation, cancellationToken);
await GetAnalyzerSemanticDiagnosticsAsync(treeModel, filterSpan: null, cancellationToken).ConfigureAwait(false);
}
}
}
}
}
......@@ -1062,13 +1076,13 @@ private void ClearExecutingTask(Task computeTask, SyntaxTree treeOpt)
}
}
private AsyncQueue<CompilationEvent> GetPendingEvents(ImmutableArray<DiagnosticAnalyzer> analyzers, SyntaxTree tree)
private AsyncQueue<CompilationEvent> GetPendingEvents(ImmutableArray<DiagnosticAnalyzer> analyzers, SyntaxTree tree, CancellationToken cancellationToken)
{
var eventQueue = _eventQueuePool.Allocate();
Debug.Assert(!eventQueue.IsCompleted);
Debug.Assert(eventQueue.Count == 0);
foreach (var compilationEvent in _analysisState.GetPendingEvents(analyzers, tree))
foreach (var compilationEvent in _analysisState.GetPendingEvents(analyzers, tree, cancellationToken))
{
eventQueue.TryEnqueue(compilationEvent);
}
......@@ -1076,7 +1090,11 @@ private AsyncQueue<CompilationEvent> GetPendingEvents(ImmutableArray<DiagnosticA
return eventQueue;
}
private AsyncQueue<CompilationEvent> GetPendingEvents(ImmutableArray<DiagnosticAnalyzer> analyzers, bool includeSourceEvents, bool includeNonSourceEvents)
private AsyncQueue<CompilationEvent> GetPendingEvents(
ImmutableArray<DiagnosticAnalyzer> analyzers,
bool includeSourceEvents,
bool includeNonSourceEvents,
CancellationToken cancellationToken)
{
Debug.Assert(includeSourceEvents || includeNonSourceEvents);
......@@ -1084,7 +1102,7 @@ private AsyncQueue<CompilationEvent> GetPendingEvents(ImmutableArray<DiagnosticA
Debug.Assert(!eventQueue.IsCompleted);
Debug.Assert(eventQueue.Count == 0);
foreach (var compilationEvent in _analysisState.GetPendingEvents(analyzers, includeSourceEvents, includeNonSourceEvents))
foreach (var compilationEvent in _analysisState.GetPendingEvents(analyzers, includeSourceEvents, includeNonSourceEvents, cancellationToken))
{
eventQueue.TryEnqueue(compilationEvent);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册