Measuring performance of Roslyn Source Generators

 
 
  • Gérald Barré

A Roslyn source generator is a tool that allows you to programmatically generate C# or VB.NET source code at compile time. Source generators are implemented as Roslyn analyzers, which are NuGet packages that contain one or more source generators. When you include a source generator NuGet package in your project, the source generator will run during the build process and generate additional source code that is then compiled along with the rest of your project.

Source Generators are more and more common. The .NET SDK provides a few generators. For instance, you can generate code to support the JSON serializer, or to generate optimized code for logging. Each source generator takes time when compiling a project, but also when editing the code in Visual Studio. So, it is important to understand the performance characteristics of your source generators, so that you can avoid performance issues in your projects.

In this post, I describe how to measure the performance of Roslyn Source Generator during build or when editing code in Visual Studio.

#MSBuild - Binary Log

Roslyn can measure the execution of each source generator. To do so, you need to add <ReportAnalyzer> to your project file:

csproj (MSBuild project file)
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <ReportAnalyzer>true</ReportAnalyzer>
  </PropertyGroup>
</Project>

Now, you can build using the binary log option:

Shell
dotnet build my_project.csproj /binaryLogger

This should create a new file named msbuild.binlog. This file contains the execution time of each source generator. You can open it using the MSBuild Structured Log Viewer:

#Visual Studio - ETW

The binary log is a great way to measure the performance of source generators during the build. But, it is not possible to use it when editing code in Visual Studio. In this case, you can use ETW to get live events when Roslyn uses the source generators. There are multiple ways to listen to ETW events. You can use logman, PerfView, or a custom application. In this post, I will use a custom application to show live events in a console.

Shell
dotnet new console
dotnet add package Microsoft.Diagnostics.Tracing.TraceEvent
C#
using Microsoft.Diagnostics.Tracing.Session;

using var session = new TraceEventSession("roslyn-sg");
Console.CancelKeyPress += (_, _) => session.Dispose();

session.Source.Dynamic.AddCallbackForProviderEvent("Microsoft-CodeAnalysis-General", "SingleGeneratorRunTime/Stop", traceEvent =>
{
    var generatorName = (string)traceEvent.PayloadByName("generatorName");
    var ticks = (long)traceEvent.PayloadByName("elapsedTicks");
    //var id = (string)data.PayloadByName("id");
    //var assemblyPath = (string)data.PayloadByName("assemblyPath");

    Console.WriteLine($"{generatorName}: {TimeSpan.FromTicks(ticks).TotalMilliseconds:N0}ms");

    // As suggested by Lucas Trzesniewski (https://twitter.com/Lucas_Trz/status/1631739866915434497)
    // you can use Console.Beep() to get a sound when a source generator is executed.
    // If this is too noisy, you know one of the source generators doesn't use the cache correctly.
});

session.EnableProvider("Microsoft-CodeAnalysis-General");
session.Source.Process();

Now, you can run the application as an Administrator and open Visual Studio. You should see the execution time of each source generator:

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?Buy Me A Coffee💖 Sponsor on GitHub