From ee788008e9e4a53b93753b7ddca3a7e53edf789a Mon Sep 17 00:00:00 2001 From: Timothy Makkison Date: Tue, 27 Jan 2026 20:05:51 +0000 Subject: [PATCH] feat: add source generator benchmarks --- Directory.Packages.props | 2 +- ...TUnit.Core.SourceGenerator.Roslyn47.csproj | 2 +- TUnit.SourceGenerator.Benchmarks/Program.cs | 14 +++++ .../TUnit.SourceGenerator.Benchmarks.csproj | 25 +++++++++ .../TestMetadataGeneratorBenchmarks.cs | 34 +++++++++++ .../WorkspaceHelper.cs | 56 +++++++++++++++++++ TUnit.sln | 14 +++++ 7 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 TUnit.SourceGenerator.Benchmarks/Program.cs create mode 100644 TUnit.SourceGenerator.Benchmarks/TUnit.SourceGenerator.Benchmarks.csproj create mode 100644 TUnit.SourceGenerator.Benchmarks/TestMetadataGeneratorBenchmarks.cs create mode 100644 TUnit.SourceGenerator.Benchmarks/WorkspaceHelper.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 17a949b9c6..8c48047653 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -101,4 +101,4 @@ - \ No newline at end of file + diff --git a/TUnit.Core.SourceGenerator.Roslyn47/TUnit.Core.SourceGenerator.Roslyn47.csproj b/TUnit.Core.SourceGenerator.Roslyn47/TUnit.Core.SourceGenerator.Roslyn47.csproj index 5474b65ce3..7a9abf09c6 100644 --- a/TUnit.Core.SourceGenerator.Roslyn47/TUnit.Core.SourceGenerator.Roslyn47.csproj +++ b/TUnit.Core.SourceGenerator.Roslyn47/TUnit.Core.SourceGenerator.Roslyn47.csproj @@ -6,4 +6,4 @@ - \ No newline at end of file + diff --git a/TUnit.SourceGenerator.Benchmarks/Program.cs b/TUnit.SourceGenerator.Benchmarks/Program.cs new file mode 100644 index 0000000000..4e4b4ab3cb --- /dev/null +++ b/TUnit.SourceGenerator.Benchmarks/Program.cs @@ -0,0 +1,14 @@ +using BenchmarkDotNet.Running; +using Microsoft.Build.Locator; +using TUnit.SourceGenerator.Benchmarks; + +MSBuildLocator.RegisterDefaults(); + +if (args is { Length: > 0 }) +{ + BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); +} +else +{ + BenchmarkRunner.Run(); +} diff --git a/TUnit.SourceGenerator.Benchmarks/TUnit.SourceGenerator.Benchmarks.csproj b/TUnit.SourceGenerator.Benchmarks/TUnit.SourceGenerator.Benchmarks.csproj new file mode 100644 index 0000000000..7309e3d09e --- /dev/null +++ b/TUnit.SourceGenerator.Benchmarks/TUnit.SourceGenerator.Benchmarks.csproj @@ -0,0 +1,25 @@ + + + Exe + net10.0 + enable + enable + true + + + + + + + + + + + + + + + + + + diff --git a/TUnit.SourceGenerator.Benchmarks/TestMetadataGeneratorBenchmarks.cs b/TUnit.SourceGenerator.Benchmarks/TestMetadataGeneratorBenchmarks.cs new file mode 100644 index 0000000000..7c1ce60261 --- /dev/null +++ b/TUnit.SourceGenerator.Benchmarks/TestMetadataGeneratorBenchmarks.cs @@ -0,0 +1,34 @@ +using BenchmarkDotNet.Attributes; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.MSBuild; +using TUnit.Core.SourceGenerator.Generators; +using TUnit.SourceGenerator.Benchmarks; + +namespace TUnit.SourceGenerator.Benchmarks; + +[MemoryDiagnoser] +[InProcess] +public class TestMetadataGeneratorBenchmarks +{ + private const string SampleProjectPath = "../TUnit.TestProject/TUnit.TestProject.csproj"; + + private MSBuildWorkspace? _workspace; + private GeneratorDriver? _sampleDriver; + private Compilation? _sampleCompilation; + + [GlobalSetup(Target = nameof(Compile))] + public void SetupCompile() => + (_sampleCompilation, _sampleDriver, _workspace) = + WorkspaceHelper.SetupAsync(SampleProjectPath) + .GetAwaiter() + .GetResult(); + + [Benchmark] + public GeneratorDriver Compile() => _sampleDriver!.RunGeneratorsAndUpdateCompilation(_sampleCompilation!, out _, out _); + + [GlobalCleanup] + public void Cleanup() + { + _workspace?.Dispose(); + } +} diff --git a/TUnit.SourceGenerator.Benchmarks/WorkspaceHelper.cs b/TUnit.SourceGenerator.Benchmarks/WorkspaceHelper.cs new file mode 100644 index 0000000000..2a1cc7c6bb --- /dev/null +++ b/TUnit.SourceGenerator.Benchmarks/WorkspaceHelper.cs @@ -0,0 +1,56 @@ +using System.Runtime.CompilerServices; +using BenchmarkDotNet.Loggers; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.MSBuild; + +namespace TUnit.SourceGenerator.Benchmarks; + +public static class WorkspaceHelper +{ + private static string GetDirectoryRelativePath(string projectPath, [CallerFilePath] string callerFilePath = null!) => + Path.Combine(Path.GetDirectoryName(callerFilePath)!, projectPath); + + public static async Task<(Compilation, CSharpGeneratorDriver, MSBuildWorkspace)> SetupAsync( string projectPath) + where TSourceGenerator : IIncrementalGenerator, new() + { + MSBuildWorkspace workspace = MSBuildWorkspace.Create(); + workspace.RegisterWorkspaceFailedHandler(args => + { + ConsoleLogger.Default.WriteLineError("-------------------------"); + ConsoleLogger.Default.WriteLineError(args.Diagnostic.ToString()); + ConsoleLogger.Default.WriteLineError("-------------------------"); + }); + + var projectFile = GetDirectoryRelativePath(projectPath); + + if (!File.Exists(projectFile)) + throw new Exception($"Project doesn't exist at {projectFile}"); + + ConsoleLogger.Default.WriteLine($"Project exists at {projectFile}"); + + Project project; + try + { + ConsoleLogger.Default.WriteLine("Loading project\n"); + project = await workspace.OpenProjectAsync(projectFile); + ConsoleLogger.Default.WriteLine("\nLoaded project"); + } + catch (Exception ex) + { + ConsoleLogger.Default.WriteError($"Error: {ex.Message}"); + throw; + } + + var compilation = await project.GetCompilationAsync(); + if (compilation == null) + throw new InvalidOperationException("Compilation returned null"); + + var generator = new TSourceGenerator().AsSourceGenerator(); + + var driver = + CSharpGeneratorDriver.Create([generator], parseOptions: (CSharpParseOptions) project.ParseOptions!); + + return (compilation, driver, workspace); + } +} diff --git a/TUnit.sln b/TUnit.sln index 3350695215..7273f2676d 100644 --- a/TUnit.sln +++ b/TUnit.sln @@ -155,6 +155,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TUnit.FsCheck", "TUnit.FsCh EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TUnit.Example.FsCheck.TestProject", "TUnit.Example.FsCheck.TestProject\TUnit.Example.FsCheck.TestProject.csproj", "{3428D7AD-B362-4647-B1B0-72674CF3BC7C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TUnit.SourceGenerator.Benchmarks", "TUnit.SourceGenerator.Benchmarks\TUnit.SourceGenerator.Benchmarks.csproj", "{F686AD4B-FC90-48B9-84C9-C7B16C2E13E5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -897,6 +899,18 @@ Global {3428D7AD-B362-4647-B1B0-72674CF3BC7C}.Release|x64.Build.0 = Release|Any CPU {3428D7AD-B362-4647-B1B0-72674CF3BC7C}.Release|x86.ActiveCfg = Release|Any CPU {3428D7AD-B362-4647-B1B0-72674CF3BC7C}.Release|x86.Build.0 = Release|Any CPU + {F686AD4B-FC90-48B9-84C9-C7B16C2E13E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F686AD4B-FC90-48B9-84C9-C7B16C2E13E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F686AD4B-FC90-48B9-84C9-C7B16C2E13E5}.Debug|x64.ActiveCfg = Debug|Any CPU + {F686AD4B-FC90-48B9-84C9-C7B16C2E13E5}.Debug|x64.Build.0 = Debug|Any CPU + {F686AD4B-FC90-48B9-84C9-C7B16C2E13E5}.Debug|x86.ActiveCfg = Debug|Any CPU + {F686AD4B-FC90-48B9-84C9-C7B16C2E13E5}.Debug|x86.Build.0 = Debug|Any CPU + {F686AD4B-FC90-48B9-84C9-C7B16C2E13E5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F686AD4B-FC90-48B9-84C9-C7B16C2E13E5}.Release|Any CPU.Build.0 = Release|Any CPU + {F686AD4B-FC90-48B9-84C9-C7B16C2E13E5}.Release|x64.ActiveCfg = Release|Any CPU + {F686AD4B-FC90-48B9-84C9-C7B16C2E13E5}.Release|x64.Build.0 = Release|Any CPU + {F686AD4B-FC90-48B9-84C9-C7B16C2E13E5}.Release|x86.ActiveCfg = Release|Any CPU + {F686AD4B-FC90-48B9-84C9-C7B16C2E13E5}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE