@@ -124,6 +124,51 @@ private static async Task MainAsync(string[] args, CancellationToken cancellatio
124124
125125 Console . WriteLine ( $ "Found { allDiagnostics . Length } diagnostics in { stopwatch . ElapsedMilliseconds } ms") ;
126126
127+ bool testDocuments = args . Contains ( "/editperf" ) ;
128+ if ( testDocuments )
129+ {
130+ var projectPerformance = new Dictionary < ProjectId , double > ( ) ;
131+ var documentPerformance = new Dictionary < DocumentId , DocumentAnalyzerPerformance > ( ) ;
132+ foreach ( var projectId in solution . ProjectIds )
133+ {
134+ Project project = solution . GetProject ( projectId ) ;
135+ if ( project . Language != LanguageNames . CSharp )
136+ {
137+ continue ;
138+ }
139+
140+ foreach ( var documentId in project . DocumentIds )
141+ {
142+ var currentDocumentPerformance = await TestDocumentPerformanceAsync ( analyzers , project , documentId , force , cancellationToken ) . ConfigureAwait ( false ) ;
143+ Console . WriteLine ( $ "{ documentId } : { currentDocumentPerformance . EditsPerSecond : 0.00} ") ;
144+ documentPerformance . Add ( documentId , currentDocumentPerformance ) ;
145+ }
146+
147+ double sumOfDocumentAverages = 0 ;
148+ foreach ( var documentId in project . DocumentIds )
149+ {
150+ sumOfDocumentAverages += documentPerformance [ documentId ] . EditsPerSecond ;
151+ }
152+
153+ if ( sumOfDocumentAverages != 0 )
154+ {
155+ projectPerformance [ project . Id ] = sumOfDocumentAverages / project . DocumentIds . Count ;
156+ }
157+ }
158+
159+ foreach ( var projectId in solution . ProjectIds )
160+ {
161+ double averageEditsInProject ;
162+ if ( ! projectPerformance . TryGetValue ( projectId , out averageEditsInProject ) )
163+ {
164+ continue ;
165+ }
166+
167+ Project project = solution . GetProject ( projectId ) ;
168+ Console . WriteLine ( $ "{ project . Name } ({ project . DocumentIds . Count } documents): { averageEditsInProject : 0.00} edits per second") ;
169+ }
170+ }
171+
127172 foreach ( var group in allDiagnostics . GroupBy ( i => i . Id ) . OrderBy ( i => i . Key , StringComparer . OrdinalIgnoreCase ) )
128173 {
129174 Console . WriteLine ( $ " { group . Key } : { group . Count ( ) } instances") ;
@@ -157,6 +202,45 @@ private static async Task MainAsync(string[] args, CancellationToken cancellatio
157202 }
158203 }
159204
205+ private static async Task < DocumentAnalyzerPerformance > TestDocumentPerformanceAsync ( ImmutableArray < DiagnosticAnalyzer > analyzers , Project project , DocumentId documentId , bool force , CancellationToken cancellationToken )
206+ {
207+ var supportedDiagnosticsSpecificOptions = new Dictionary < string , ReportDiagnostic > ( ) ;
208+ if ( force )
209+ {
210+ foreach ( var analyzer in analyzers )
211+ {
212+ foreach ( var diagnostic in analyzer . SupportedDiagnostics )
213+ {
214+ // make sure the analyzers we are testing are enabled
215+ supportedDiagnosticsSpecificOptions [ diagnostic . Id ] = ReportDiagnostic . Default ;
216+ }
217+ }
218+ }
219+
220+ // Report exceptions during the analysis process as errors
221+ supportedDiagnosticsSpecificOptions . Add ( "AD0001" , ReportDiagnostic . Error ) ;
222+
223+ // update the project compilation options
224+ var modifiedSpecificDiagnosticOptions = supportedDiagnosticsSpecificOptions . ToImmutableDictionary ( ) . SetItems ( project . CompilationOptions . SpecificDiagnosticOptions ) ;
225+ var modifiedCompilationOptions = project . CompilationOptions . WithSpecificDiagnosticOptions ( modifiedSpecificDiagnosticOptions ) ;
226+
227+ var stopwatch = Stopwatch . StartNew ( ) ;
228+ const int iterations = 10 ;
229+ for ( int i = 0 ; i < iterations ; i ++ )
230+ {
231+ var processedProject = project . WithCompilationOptions ( modifiedCompilationOptions ) ;
232+
233+ Compilation compilation = await processedProject . GetCompilationAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
234+ CompilationWithAnalyzers compilationWithAnalyzers = compilation . WithAnalyzers ( analyzers , new CompilationWithAnalyzersOptions ( new AnalyzerOptions ( ImmutableArray . Create < AdditionalText > ( ) ) , null , true , false ) ) ;
235+
236+ SyntaxTree tree = await project . GetDocument ( documentId ) . GetSyntaxTreeAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
237+ await compilationWithAnalyzers . GetAnalyzerSyntaxDiagnosticsAsync ( tree , cancellationToken ) . ConfigureAwait ( false ) ;
238+ await compilationWithAnalyzers . GetAnalyzerSemanticDiagnosticsAsync ( compilation . GetSemanticModel ( tree ) , null , cancellationToken ) . ConfigureAwait ( false ) ;
239+ }
240+
241+ return new DocumentAnalyzerPerformance ( iterations / stopwatch . Elapsed . TotalSeconds ) ;
242+ }
243+
160244 private static void WriteDiagnosticResults ( ImmutableArray < Tuple < ProjectId , Diagnostic > > diagnostics , string fileName )
161245 {
162246 var orderedDiagnostics =
@@ -498,5 +582,18 @@ private static void PrintHelp()
498582 Console . WriteLine ( "/apply Write code fix changes back to disk" ) ;
499583 Console . WriteLine ( "/force Force an analyzer to be enabled, regardless of the configured rule set(s) for the solution" ) ;
500584 }
585+
586+ private struct DocumentAnalyzerPerformance
587+ {
588+ public DocumentAnalyzerPerformance ( double editsPerSecond )
589+ {
590+ this . EditsPerSecond = editsPerSecond ;
591+ }
592+
593+ public double EditsPerSecond
594+ {
595+ get ;
596+ }
597+ }
501598 }
502599}
0 commit comments