Skip to content
Draft
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using Microsoft.TypeSpec.Generator.Input;
using Microsoft.TypeSpec.Generator.Primitives;
using Microsoft.TypeSpec.Generator.Providers;
using Microsoft.TypeSpec.Generator.SourceInput;
using Microsoft.TypeSpec.Generator.Tests.Common;

namespace Microsoft.TypeSpec.Generator.Perf
{
public class GeneratedCodeWorkspaceNameResolverBenchmark
{
private TypeProvider[] _providers = [];
[Params(100)]
public int ModelCount { get; set; }

[Params(10)]
public int PropertyCount { get; set; }

[GlobalSetup]
public void Setup()
{
var outputPath = Path.Combine(Path.GetTempPath(), "Microsoft.TypeSpec.Generator.Perf", nameof(GeneratedCodeWorkspaceNameResolverBenchmark));
Directory.CreateDirectory(outputPath);

var inputModels = new List<InputModelType>(ModelCount);
for (int i = 0; i < ModelCount; i++)
{
inputModels.Add(CreateModel(i));
}

var generator = new BenchmarkCodeModelGenerator(new GeneratorContext(Configuration.Load(
outputPath,
"""
{
"package-name": "Benchmark"
}
""")),
InputFactory.Namespace("Benchmark", models: [.. inputModels]));
generator.SourceInputModel = new SourceInputModel(null, null);
CodeModelGenerator.Instance = generator;

var providers = new List<TypeProvider>(ModelCount);
foreach (var inputModel in inputModels)
{
providers.Add(new ModelProvider(inputModel));
}

_providers = [.. providers];
}

[Benchmark(Baseline = true)]
public Task<int> LegacyWriterWithRoslynSimplifier()
=> WriteAndPostProcessAsync(resolveTypeNames: false);

[Benchmark]
public Task<int> OptimizedWriterWithoutRoslynSimplifier()
=> WriteAndPostProcessAsync(resolveTypeNames: true);

private async Task<int> WriteAndPostProcessAsync(bool resolveTypeNames)
{
CodeModelGenerator.Instance.ShouldResolveTypeNames = resolveTypeNames;

var workspace = await GeneratedCodeWorkspace.Create(isCustomCodeProject: false);
foreach (var provider in _providers)
{
await workspace.AddGeneratedFile(new TypeProviderWriter(provider).Write());
}

var totalLength = 0;
await foreach (var (_, text) in workspace.GetGeneratedFilesAsync())
{
totalLength += text.Length;
}

return totalLength;
}

private InputModelType CreateModel(int modelIndex)
{
var properties = new List<InputModelProperty>(PropertyCount);
for (int i = 0; i < PropertyCount; i++)
{
properties.Add(InputFactory.Property($"property{i}", GetPropertyType(i), isRequired: true));
}

return InputFactory.Model($"BenchmarkModel{modelIndex}", properties: properties);
}

private static InputType GetPropertyType(int propertyIndex)
=> (propertyIndex % 4) switch
{
0 => InputPrimitiveType.String,
1 => InputPrimitiveType.Int32,
2 => InputFactory.Array(InputPrimitiveType.String),
_ => InputFactory.Dictionary(InputPrimitiveType.Int32),
};

private sealed class BenchmarkCodeModelGenerator : CodeModelGenerator
{
private readonly InputLibrary _inputLibrary;

public BenchmarkCodeModelGenerator(GeneratorContext context, InputNamespace inputNamespace)
: base(context)
{
_inputLibrary = new BenchmarkInputLibrary(inputNamespace);
}

public override InputLibrary InputLibrary => _inputLibrary;
}

private sealed class BenchmarkInputLibrary : InputLibrary
{
private readonly InputNamespace _inputNamespace;

public BenchmarkInputLibrary(InputNamespace inputNamespace)
{
_inputNamespace = inputNamespace;
}

public override InputNamespace InputNamespace => _inputNamespace;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,12 @@ await customCodeWorkspace.GetCompilationAsync(),
{
// Ensure back-compatibility processing is done after all visitors have run
outputType.ProcessTypeForBackCompatibility();
}

CodeModelGenerator.Instance.ShouldResolveTypeNames = true;

foreach (var outputType in output.TypeProviders)
{
var writer = CodeModelGenerator.Instance.GetWriter(outputType);
generateFilesTasks.Add(generatedCodeWorkspace.AddGeneratedFile(writer.Write()));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ internal set
public virtual OutputLibrary OutputLibrary { get; } = new();
public virtual InputLibrary InputLibrary => _inputLibrary;
public virtual TypeProviderWriter GetWriter(TypeProvider provider) => new(provider);
internal bool ShouldResolveTypeNames { get; set; }
internal bool ShouldSkipRoslynSimplifier => ShouldResolveTypeNames && Rewriters.Count == 0;
public IReadOnlyList<MetadataReference> AdditionalMetadataReferences => _additionalMetadataReferences;

public IReadOnlyList<string> SharedSourceDirectories => _sharedSourceDirectories;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ public sealed record AssignmentExpression(ValueExpression Variable, ValueExpress
{
public ValueExpression Variable { get; private set; } = Variable;
public ValueExpression Value { get; private set; } = Value;

internal override bool ShouldParenthesize => true;

internal override void Write(CodeWriter writer)
{
Variable.Write(writer);
Variable.WriteNested(writer);
if (UseNullCoalesce)
{
writer.Append($" ??= ");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ namespace Microsoft.TypeSpec.Generator.Expressions
{
public sealed record BinaryOperatorExpression(string Operator, ValueExpression Left, ValueExpression Right) : ValueExpression
{
internal override bool ShouldParenthesize => true;

internal override void Write(CodeWriter writer)
{
writer.AppendRaw("(");
Left.Write(writer);
Left.WriteNested(writer);
writer.AppendRawIf(" ", !Left.IsEmptyExpression());
writer.AppendRaw(Operator);
writer.AppendRawIf(" ", !Right.IsEmptyExpression());
Right.Write(writer);
writer.AppendRaw(")");
Right.WriteNested(writer);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ internal override void Write(CodeWriter writer)
// if the parenthesis are not needed, the roslyn reducer will remove it.
writer.AppendRaw("(");
writer.Append($"({Type})");
Inner.Write(writer);
Inner.WriteNested(writer);
writer.AppendRaw(")");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,17 @@ internal override void Write(CodeWriter writer)
writer.AppendRaw(Keyword);
if (Expression is not null)
{
writer.AppendRaw(" ");
Expression.Write(writer);
if (Keyword is "checked" or "unchecked")
{
writer.AppendRaw("(");
Expression.WriteNested(writer);
writer.AppendRaw(")");
}
else
{
writer.AppendRaw(" ");
Expression.Write(writer);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ internal override void Write(CodeWriter writer)
{
if (Inner is not null)
{
Inner.Write(writer);
Inner.WriteNested(writer);
writer.AppendRaw(".");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public sealed record NullConditionalExpression(ValueExpression Inner) : ValueExp
{
internal override void Write(CodeWriter writer)
{
Inner.Write(writer);
Inner.WriteNested(writer);
writer.AppendRaw("?");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ namespace Microsoft.TypeSpec.Generator.Expressions
/// </summary>
public sealed record TernaryConditionalExpression(ValueExpression Condition, ValueExpression Consequent, ValueExpression Alternative) : ValueExpression
{
internal override bool ShouldParenthesize => true;

internal override void Write(CodeWriter writer)
{
Condition.Write(writer);
Condition.WriteNested(writer);
writer.AppendRaw(" ? ");
Consequent.Write(writer);
Consequent.WriteNested(writer);
writer.AppendRaw(" : ");
Alternative.Write(writer);
Alternative.WriteNested(writer);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public sealed record UnaryOperatorExpression(string Operator, ValueExpression Op
internal override void Write(CodeWriter writer)
{
writer.AppendRawIf(Operator, !OperandOnTheLeft);
Operand.Write(writer);
Operand.WriteNested(writer);
writer.AppendRawIf(Operator, OperandOnTheLeft);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,25 @@ public abstract record ValueExpression

private protected ValueExpression() { }

internal virtual bool ShouldParenthesize => false;

internal virtual void Write(CodeWriter writer) { }

internal void WriteNested(CodeWriter writer)
{
if (ShouldParenthesize)
{
writer.AppendRaw("(");
}

Write(writer);

if (ShouldParenthesize)
{
writer.AppendRaw(")");
}
}

internal virtual ValueExpression? Accept(LibraryVisitor visitor, MethodProvider method)
{
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,10 @@ private async Task<Document> ProcessDocument(Document document, MemberRemoverRew
}
document = document.WithSyntaxRoot(root);

document = await Simplifier.ReduceAsync(document);
if (!CodeModelGenerator.Instance.ShouldSkipRoslynSimplifier)
{
document = await Simplifier.ReduceAsync(document);
}

// Reformat if any custom rewriters have been applied
if (CodeModelGenerator.Instance.Rewriters.Count > 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ public TypeProviderWriter(TypeProvider provider)

public virtual CodeFile Write()
{
using var writer = new CodeWriter();
using (var ns = writer.SetNamespace(_provider.Type.Namespace))
using var writer = new CodeWriter(CodeModelGenerator.Instance.ShouldResolveTypeNames);
writer.CollectTypeReferences(_provider.Type.Namespace, WriteType);

using (writer.SetNamespace(_provider.Type.Namespace))
{
WriteType(writer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public ScopedApi(CSharpType type, ValueExpression original)

private MethodBodyStatement? _terminated;

internal override bool ShouldParenthesize => Original.ShouldParenthesize;

internal override void Write(CodeWriter writer)
{
Original.Write(writer);
Expand Down
Loading