Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions EfficientUnionGenerator.slnx
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
<Solution>
<Configurations>
<BuildType Name="Debug" />
<BuildType Name="Profiling" />
<BuildType Name="Release" />
</Configurations>
<Folder Name="/etc/">
<File Path=".editorconfig" />
<File Path="global.json" />
<File Path="LICENSE.txt" />
<File Path="README.md" />
</Folder>
<Project Path="src/EfficientUnionGenerator.GeneratorBenchmark/EfficientUnionGenerator.GeneratorBenchmark.csproj" Id="aeb80d67-a615-4c91-8160-6a94dc47da64" />
<Project Path="src/EfficientUnionGenerator.SampleApp/EfficientUnionGenerator.SampleApp.csproj" Id="328c4817-42f5-44d3-be03-8118c44d1d58" />
<Project Path="src/EfficientUnionGenerator.Test/EfficientUnionGenerator.Test.csproj" Id="b0f436db-3eee-48fb-aaf8-264e78a1a938" />
<Project Path="src/EfficientUnionGenerator/EfficientUnionGenerator.csproj" Id="7b1b2fc2-1729-497a-be6a-5970166dc8b6" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<Configurations>Debug;Release;Profiling</Configurations>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.15.8" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="*" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\EfficientUnionGenerator\EfficientUnionGenerator.csproj" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="../EfficientUnionGenerator.SampleApp/SimpleUnionOfInt32OrString/Int32OrString.cs" />
<EmbeddedResource Include="../EfficientUnionGenerator.SampleApp/SimpleUnionOfSignedInteger/SignedInteger.cs" />
<EmbeddedResource Include="../EfficientUnionGenerator.SampleApp/PositiveOnlyIntOrFloat/PositiveOnlyIntOrFloat.cs" />
<EmbeddedResource Include="../EfficientUnionGenerator.SampleApp/ElfHeader/ElfHeader.cs" />
<EmbeddedResource Update="../EfficientUnionGenerator.SampleApp/**/*.cs" LinkBase="resources" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
using System.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using BenchmarkDotNet.Attributes;

namespace EfficientUnionGenerator.GeneratorBenchmark;

[MemoryDiagnoser]
public partial class GeneratorBenchmarkContext
{
[Params([
"Int32OrString",
"SignedInteger",
"PositiveOnlyIntOrFloat",
"ElfHeader",
])]
public string? TypeName { get; set; } = null!;

private IIncrementalGenerator _generator = null!;
private IIncrementalGenerator _negativeControlGenerator = null!;
private Compilation _baseCompilation = null!;
private Compilation _changedCompilation = null!;

[Params("Experimental", "NegativeControl")]
public string Target { get; set; } = null!;
private GeneratorDriver TargetDriver { get; set; } = null!;

public string? SourceCode { get; private set; }

[GlobalSetup]
public void Setup()
{
if(TypeName is null)
{
return;
}
var resourceName = $"EfficientUnionGenerator.GeneratorBenchmark.resources.{TypeName}.cs";
var executingAssembly = Assembly.GetExecutingAssembly();
using (var stream = executingAssembly.GetManifestResourceStream(resourceName))
{
if (stream == null)
{
throw new InvalidOperationException($"Resource '{resourceName}' not found.");
}
using var reader = new StreamReader(stream);
SourceCode = reader.ReadToEnd();
}

_generator = new Generator();
_negativeControlGenerator = new NegativeControlGenerator();

var tree = CSharpSyntaxTree.ParseText(SourceCode);
_baseCompilation = CSharpCompilation.Create(
assemblyName: "BenchmarkDummy",
syntaxTrees: [tree],
references: [
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
]);

var modifiedTree = tree.WithRootAndOptions(
new Visitor(TypeName).Visit(tree.GetRoot()),
tree.Options);
_changedCompilation = _baseCompilation.ReplaceSyntaxTree(tree, modifiedTree);
}

[IterationSetup]
public void ResetDriver()
{
TargetDriver = Target switch
{
"Experimental" => CSharpGeneratorDriver
.Create(_generator),
"NegativeControl" => CSharpGeneratorDriver
.Create(_negativeControlGenerator),
_ => throw new InvalidOperationException($"Unknown target: {Target}"),
};
}

[Benchmark]
public void RunGenerator()
{
TargetDriver = TargetDriver
.RunGenerators(_baseCompilation);
}

[Benchmark]
public void IncrementalUpdate_NoChanges()
{
TargetDriver = TargetDriver
.RunGenerators(_baseCompilation)
.RunGenerators(_baseCompilation)
.RunGenerators(_baseCompilation)
.RunGenerators(_baseCompilation)
.RunGenerators(_baseCompilation)
.RunGenerators(_baseCompilation)
.RunGenerators(_baseCompilation)
.RunGenerators(_baseCompilation)
.RunGenerators(_baseCompilation)
.RunGenerators(_baseCompilation);
}

[Benchmark]
public void IncrementalUpdate_Regenerate()
{
TargetDriver = TargetDriver
.RunGenerators(_baseCompilation)
.RunGenerators(_changedCompilation)
.RunGenerators(_baseCompilation)
.RunGenerators(_changedCompilation)
.RunGenerators(_baseCompilation)
.RunGenerators(_changedCompilation)
.RunGenerators(_baseCompilation)
.RunGenerators(_changedCompilation)
.RunGenerators(_baseCompilation)
.RunGenerators(_changedCompilation);
}
}


file class Visitor(string typeName) : CSharpSyntaxRewriter
{
public override SyntaxToken VisitToken(SyntaxToken token)
{
if(token.IsKind(SyntaxKind.IdentifierToken) && token.Text == typeName)
{
return SyntaxFactory.Identifier(typeName + "_Modified");
}
return base.VisitToken(token);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace EfficientUnionGenerator.GeneratorBenchmark;

using static Constants;

internal class NegativeControlGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
context.RegisterPostInitializationOutput(static cxt =>
{
cxt.AddSource(
$"{AttributeNamespace}.{EfficientUnionAttributeName}.g.cs",
EfficientUnionAttributeSource);
});

var sourceAttr = context.CompilationProvider
.Select(static (compilation, token) => compilation.GetTypeByMetadataName("System.Runtime.CompilerServices.UnionAttribute") is { });
context.RegisterSourceOutput(sourceAttr, (context, isAvailable) =>
{
if (!isAvailable)
{
context.AddSource(
$"{AttributeNamespace}.UnionAttribute.g.cs",
CompilerServicesUnionAttributeSource);
}
});

var sourceInterface = context.CompilationProvider
.Select(static (compilation, token) => compilation.GetTypeByMetadataName("System.Runtime.CompilerServices.IUnion") is { });
context.RegisterSourceOutput(sourceInterface, (context, isAvailable) =>
{
if (!isAvailable)
{
context.AddSource(
$"{AttributeNamespace}.IUnion.g.cs",
CompilerServicesIUnionSource);
}
});

var sourceUnion = context.SyntaxProvider.ForAttributeWithMetadataName(
$"{AttributeNamespace}.{EfficientUnionAttributeName}",
static (node, token) => node is ClassDeclarationSyntax or StructDeclarationSyntax,
static (context, token) => context)
.Where(static _ => false);
context.RegisterSourceOutput(sourceUnion, EmitUnionType);
}

private void EmitUnionType(SourceProductionContext context, GeneratorAttributeSyntaxContext source)
{
}
}
49 changes: 49 additions & 0 deletions src/EfficientUnionGenerator.GeneratorBenchmark/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// #define PROFILING

using BenchmarkDotNet.Running;
using EfficientUnionGenerator;
using EfficientUnionGenerator.GeneratorBenchmark;

#if DEBUG

var defaultConsoleColor = Console.ForegroundColor;
Console.WriteLine("This application is running in DEBUG mode.");
Console.WriteLine("This build mode is only for debugging benchmark context.");
Console.WriteLine("Rebuild release mode for benchmarking.");
Console.WriteLine();

var context = new GeneratorBenchmarkContext()
{
TypeName = "Int32OrString",
};
context.Setup();
context.ResetDriver();
context.IncrementalUpdate_NoChanges();

Console.WriteLine("[loaded source code]:");
Console.ForegroundColor = ConsoleColor.DarkGray;
Console.WriteLine(context.SourceCode);
Console.ForegroundColor = defaultConsoleColor;

#elif PROFILING

var context = new GeneratorBenchmarkContext()
{
TypeName = "Int32OrString",
};
context.Setup();
for(var i = 0; i < (1 << 16); ++i)
{
context.ResetDriver();
context.IncrementalUpdate_NoChanges();
}

var reports = Generator.Profiler.CreateProfileReports();
var report = reports["PredicateUnionType"];
Console.WriteLine(report);

#else

BenchmarkRunner.Run<GeneratorBenchmarkContext>();

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<Nullable>enable</Nullable>
<ReportAnalyzer>true</ReportAnalyzer>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<Configurations>Debug;Release;Profiling</Configurations>
</PropertyGroup>

<ItemGroup>
Expand Down
Loading
Loading