AnalyzerDriver performance improvements#45537
Conversation
src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.GroupedAnalyzerActions.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Most of the code here is just simplified and moved from GroupedAnalyzerActions, but avoids the dictionaries keyed on analyzers.
There was a problem hiding this comment.
These are the key changes in this PR mentioned in the description:
- Replace dictionaries from analyzer to actions with an array of tuples
(analyzer, actions)and avoid dictionary TryGetValue calls, which showed up quite a bit on performance profiles. - Change
_perSymbolAnalyzerActionsCacheto cacheIGroupedAnalyzerActionsinstead ofAnalyzerActions. Previously, we used to recomputeGroupedAnalyzerActionsfor the sameAnalyzerActionsshared between all member symbols of theINamespaceOrTypeSymbolwhich is part of the key for the dictionary_perSymbolAnalyzerActionsCache.
There was a problem hiding this comment.
This changes shows an example algorithmic change in execution model. Instead of iterating over all analyzers in the given AnalysisScope and then fetching each analyzers' registered actions from the dictionary, we iterate over the array of (analyzer, actions) and execute all relevant analyzers. For command line build, analysis scope is always the full scope and does not need any additional checks. For IDE invocation, checking if an analyzer belongs to the given analysis scope is a cheap lookup.
56ff307 to
255b074
Compare
1. Avoid creating and querying dictionaries from analyzers to actions. Instead maintain a tuple of (analyzer, actions) and special case IDE partial analysis scope to skip analyzers not in analysis scope. 2. Cache grouped analyzer actions for per-symbol actions registered within SymbolStart actions instead of re-computing them for every member symbol. Benchmark: https://github.com/dotnet/roslyn/blob/master/docs/wiki/Measuring-Compiler-Performance.md Default benchmark: | Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated | |---------------------------- |--------:|--------:|--------:|--------:|--------:|--------:|------------:|-----------:|----------:|----------:| | GetDiagnosticsWithAnalyzers | 14.79 s | 0.147 s | 0.304 s | 14.87 s | 14.17 s | 15.45 s | 218000.0000 | 64000.0000 | 3000.0000 | 1.28 GB | Default benchmark + one SymbolStart analyzer: | Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated | |---------------------------- |--------:|--------:|--------:|--------:|--------:|--------:|------------:|-----------:|----------:|----------:| | GetDiagnosticsWithAnalyzers | 16.96 s | 0.079 s | 0.088 s | 16.95 s | 16.85 s | 17.14 s | 225000.0000 | 66000.0000 | 2000.0000 | 1.32 GB | Default benchmark: | Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated | |---------------------------- |--------:|--------:|--------:|--------:|--------:|--------:|------------:|-----------:|----------:|----------:| | GetDiagnosticsWithAnalyzers | 12.76 s | 0.077 s | 0.085 s | 12.75 s | 12.62 s | 12.92 s | 218000.0000 | 60000.0000 | 2000.0000 | 1.28 GB | Default benchmark + one SymbolStart analyzer: | Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated | |---------------------------- |--------:|--------:|--------:|--------:|--------:|--------:|------------:|-----------:|----------:|----------:| | GetDiagnosticsWithAnalyzers | 14.87 s | 0.121 s | 0.101 s | 14.90 s | 14.72 s | 15.08 s | 220000.0000 | 65000.0000 | 2000.0000 | 1.29 GB | 1. **Microsoft.CodeAnalysis.CSharp.csproj**: `msbuild /v:m /m /t:rebuild /p:UseRoslynAnalyzers=true`: Improvement from _~60 seconds_ to _~51 seconds_ 2. **RoslynAnalyzers.sln**: Improvement from _~55 seconds_ to _~48 seconds_ AnalyzerDriver performance improvements 1. Avoid creating and querying dictionaries from analyzers to actions. Instead maintain a tuple of (analyzer, actions) and special case IDE partial analysis scope to skip analyzers not in analysis scope. 2. Cache grouped analyzer actions for per-symbol actions registered within SymbolStart actions instead of re-computing them for every member symbol. Benchmark: https://github.com/dotnet/roslyn/blob/master/docs/wiki/Measuring-Compiler-Performance.md Default benchmark: | Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated | |---------------------------- |--------:|--------:|--------:|--------:|--------:|--------:|------------:|-----------:|----------:|----------:| | GetDiagnosticsWithAnalyzers | 14.79 s | 0.147 s | 0.304 s | 14.87 s | 14.17 s | 15.45 s | 218000.0000 | 64000.0000 | 3000.0000 | 1.28 GB | Default benchmark + one SymbolStart analyzer: | Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated | |---------------------------- |--------:|--------:|--------:|--------:|--------:|--------:|------------:|-----------:|----------:|----------:| | GetDiagnosticsWithAnalyzers | 16.96 s | 0.079 s | 0.088 s | 16.95 s | 16.85 s | 17.14 s | 225000.0000 | 66000.0000 | 2000.0000 | 1.32 GB | Default benchmark: | Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated | |---------------------------- |--------:|--------:|--------:|--------:|--------:|--------:|------------:|-----------:|----------:|----------:| | GetDiagnosticsWithAnalyzers | 12.76 s | 0.077 s | 0.085 s | 12.75 s | 12.62 s | 12.92 s | 218000.0000 | 60000.0000 | 2000.0000 | 1.28 GB | Default benchmark + one SymbolStart analyzer: | Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated | |---------------------------- |--------:|--------:|--------:|--------:|--------:|--------:|------------:|-----------:|----------:|----------:| | GetDiagnosticsWithAnalyzers | 14.87 s | 0.121 s | 0.101 s | 14.90 s | 14.72 s | 15.08 s | 220000.0000 | 65000.0000 | 2000.0000 | 1.29 GB | 1. **Microsoft.CodeAnalysis.Workspaces.csproj**: `msbuild /v:m /m /t:rebuild /p:UseRoslynAnalyzers=true` 2. **Compilers.sln**: `msbuild /v:m /m /t:rebuild /p:UseRoslynAnalyzers=true Compilers.sln`
255b074 to
b2fae38
Compare
src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisScope.cs
Outdated
Show resolved
Hide resolved
...ompilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.GroupedAnalyzerActionsByAnalyzer.cs
Show resolved
Hide resolved
...ompilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.GroupedAnalyzerActionsByAnalyzer.cs
Show resolved
Hide resolved
...ompilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.GroupedAnalyzerActionsByAnalyzer.cs
Show resolved
Hide resolved
src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs
Outdated
Show resolved
Hide resolved
src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs
Outdated
Show resolved
Hide resolved
|
Ping @dotnet/roslyn-compiler for reviews |
1 similar comment
|
Ping @dotnet/roslyn-compiler for reviews |
src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisScope.cs
Outdated
Show resolved
Hide resolved
src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs
Outdated
Show resolved
Hide resolved
src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs
Outdated
Show resolved
Hide resolved
...ompilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.GroupedAnalyzerActionsByAnalyzer.cs
Show resolved
Hide resolved
src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.IGroupedAnalyzerActions.cs
Show resolved
Hide resolved
src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs
Outdated
Show resolved
Hide resolved
src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs
Outdated
Show resolved
Hide resolved
src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs
Outdated
Show resolved
Hide resolved
src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.GroupedAnalyzerActions.cs
Outdated
Show resolved
Hide resolved
src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs
Outdated
Show resolved
Hide resolved
src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.GroupedAnalyzerActions.cs
Outdated
Show resolved
Hide resolved
...ompilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.GroupedAnalyzerActionsByAnalyzer.cs
Outdated
Show resolved
Hide resolved
...ompilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.GroupedAnalyzerActionsByAnalyzer.cs
Outdated
Show resolved
Hide resolved
|
Thanks! |
Performance numbers
Around 10-15% improvement in analyzer execution time on following test sets:
1. Compiler benchmark
Benchmark: https://github.com/dotnet/roslyn/blob/master/docs/wiki/Measuring-Compiler-Performance.md
Using compiler built from current master branch
Using compiler built with this PR branch
2. Building real world projects
msbuild /v:m /m /t:rebuild /p:UseRoslynAnalyzers=true: Improvement from ~60 seconds to ~51 seconds