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
2 changes: 1 addition & 1 deletion .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: 5.0.x
dotnet-version: 9.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -348,3 +348,6 @@ MigrationBackup/

# Ionide (cross platform F# VS Code tools) working folder
.ionide/

.idea
.DS_Store
40 changes: 20 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,30 @@ Source code for [Dotnet code generation overview by example](https://mtkachenko.

``` ini

BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19043.1237 (21H1/May2021Update)
Intel Core i7-8550U CPU 1.80GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores
.NET SDK=5.0.401
[Host] : .NET 5.0.10 (5.0.1021.41214), X64 RyuJIT
DefaultJob : .NET 5.0.10 (5.0.1021.41214), X64 RyuJIT
BenchmarkDotNet v0.15.4, macOS 26.0.1 (25A362) [Darwin 25.0.0]
Apple M4, 1 CPU, 10 logical and 10 physical cores
.NET SDK 9.0.305
[Host] : .NET 9.0.9 (9.0.9, 9.0.925.41916), Arm64 RyuJIT armv8.0-a
DefaultJob : .NET 9.0.9 (9.0.9, 9.0.925.41916), Arm64 RyuJIT armv8.0-a


```

## Generation of parser
| Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|--------------- |-------------:|-------------:|-------------:|----------:|-------:|-------:|----------:|
| EmitIl | 22.02 μs | 0.495 μs | 1.429 μs | 1.2817 | 0.6409 | 0.0305 | 5 KB |
| ExpressionTree | 683.68 μs | 13.609 μs | 31.268 μs | 2.9297 | 0.9766 | - | 14 KB |
| Sigil | 642.63 μs | 12.305 μs | 29.243 μs | 112.3047 | - | - | 460 KB |
| Roslyn | 71,605.64 μs | 2,533.732 μs | 7,350.817 μs | 1000.0000 | - | - | 5,826 KB |
| Method | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated |
|--------------- |--------------:|------------:|------------:|----------:|---------:|---------:|-----------:|
| EmitIl | 3.370 us | 0.0283 us | 0.0251 us | 0.4196 | 0.2060 | 0.0305 | 3.48 KB |
| ExpressionTree | 131.847 us | 0.4957 us | 0.4394 us | 1.2207 | 0.4883 | - | 11.4 KB |
| Sigil | 130.371 us | 1.2540 us | 1.1730 us | 52.7344 | 13.6719 | - | 433.2 KB |
| Roslyn | 14,522.046 us | 263.8589 us | 246.8138 us | 1031.2500 | 343.7500 | 125.0000 | 7757.56 KB |

## Invocation of parser
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Allocated |
|---------------- |------------:|----------:|----------:|------:|--------:|-------:|----------:|
| EmitIl | 374.7 ns | 7.75 ns | 22.36 ns | 1.02 | 0.08 | 0.0095 | 40 B |
| ExpressionTree | 378.1 ns | 7.56 ns | 20.57 ns | 1.03 | 0.08 | 0.0095 | 40 B |
| Reflection | 13,625.0 ns | 272.60 ns | 750.81 ns | 37.29 | 2.29 | 0.7782 | 3,256 B |
| Sigil | 378.9 ns | 7.69 ns | 21.06 ns | 1.03 | 0.07 | 0.0095 | 40 B |
| Roslyn | 404.2 ns | 7.55 ns | 17.80 ns | 1.10 | 0.07 | 0.0095 | 40 B |
| SourceGenerator | 384.4 ns | 7.79 ns | 21.46 ns | 1.05 | 0.08 | 0.0095 | 40 B |
| ManuallyWritten | 367.8 ns | 7.36 ns | 15.68 ns | 1.00 | 0.00 | 0.0095 | 40 B |
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio |
|---------------- |------------:|----------:|----------:|------:|--------:|-------:|----------:|------------:|
| EmitIl | 75.14 ns | 0.352 ns | 0.294 ns | 1.01 | 0.00 | 0.0048 | 40 B | 1.00 |
| ExpressionTree | 76.12 ns | 1.306 ns | 1.222 ns | 1.02 | 0.02 | 0.0048 | 40 B | 1.00 |
| Reflection | 1,084.40 ns | 21.704 ns | 25.837 ns | 14.52 | 0.34 | 0.0992 | 832 B | 20.80 |
| Sigil | 75.51 ns | 0.319 ns | 0.249 ns | 1.01 | 0.00 | 0.0048 | 40 B | 1.00 |
| Roslyn | 82.45 ns | 0.117 ns | 0.110 ns | 1.10 | 0.00 | 0.0048 | 40 B | 1.00 |
| SourceGenerator | 75.59 ns | 0.092 ns | 0.086 ns | 1.01 | 0.00 | 0.0048 | 40 B | 1.00 |
| ManuallyWritten | 74.66 ns | 0.142 ns | 0.133 ns | 1.00 | 0.00 | 0.0048 | 40 B | 1.00 |
23 changes: 11 additions & 12 deletions benchmark/Data.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
using System;

namespace Parsers.Benchmarks
namespace Parsers.Benchmarks;

[ParserOutput]
public class Data
{
[ParserOutput]
public class Data
{
[ArrayIndex(0)]
public string Name { get; set; }
[ArrayIndex(0)]
public string Name { get; set; }

[ArrayIndex(2)]
public int Number { get; set; }
[ArrayIndex(2)]
public int Number { get; set; }

[ArrayIndex(1)]
public DateTime Birthday { get; set; }
}
}
[ArrayIndex(1)]
public DateTime Birthday { get; set; }
}
71 changes: 35 additions & 36 deletions benchmark/GetParser_Benchmark.cs
Original file line number Diff line number Diff line change
@@ -1,46 +1,45 @@
using System;
using BenchmarkDotNet.Attributes;

namespace Parsers.Benchmarks
namespace Parsers.Benchmarks;

// ReSharper disable once InconsistentNaming
[MemoryDiagnoser]
public class GetParser_Benchmark
{
// ReSharper disable once InconsistentNaming
[MemoryDiagnoser]
public class GetParser_Benchmark
{
private EmitIlParserFactory _emitIlParserFactory;
private ExpressionTreeParserFactory _expressionTreeParserFactory;
private SigilParserFactory _sigilParserFactory;
private EmitIlParserFactory _emitIlParserFactory;
private ExpressionTreeParserFactory _expressionTreeParserFactory;
private SigilParserFactory _sigilParserFactory;

[GlobalSetup]
public void GlobalSetup()
{
_emitIlParserFactory = new EmitIlParserFactory();
_expressionTreeParserFactory = new ExpressionTreeParserFactory();
_sigilParserFactory = new SigilParserFactory();
}
[GlobalSetup]
public void GlobalSetup()
{
_emitIlParserFactory = new EmitIlParserFactory();
_expressionTreeParserFactory = new ExpressionTreeParserFactory();
_sigilParserFactory = new SigilParserFactory();
}

[Benchmark]
public Func<string[], Data> EmitIl()
{
return _emitIlParserFactory.GetParser<Data>();
}
[Benchmark]
public Func<string[], Data> EmitIl()
{
return _emitIlParserFactory.GetParser<Data>();
}

[Benchmark]
public Func<string[], Data> ExpressionTree()
{
return _expressionTreeParserFactory.GetParser<Data>();
}
[Benchmark]
public Func<string[], Data> ExpressionTree()
{
return _expressionTreeParserFactory.GetParser<Data>();
}

[Benchmark]
public Func<string[], Data> Sigil()
{
return _sigilParserFactory.GetParser<Data>();
}
[Benchmark]
public Func<string[], Data> Sigil()
{
return _sigilParserFactory.GetParser<Data>();
}

[Benchmark]
public Func<string[], Data> Roslyn()
{
return RoslynParserInitializer.CreateFactory().GetParser<Data>();
}
[Benchmark]
public Func<string[], Data> Roslyn()
{
return RoslynParserInitializer.CreateFactory().GetParser<Data>();
}
}
}
135 changes: 67 additions & 68 deletions benchmark/ParserInvocation_Benchmark.cs
Original file line number Diff line number Diff line change
@@ -1,86 +1,85 @@
using System;
using BenchmarkDotNet.Attributes;

namespace Parsers.Benchmarks
namespace Parsers.Benchmarks;

// ReSharper disable once InconsistentNaming
[MemoryDiagnoser]
public class ParserInvocation_Benchmark
{
// ReSharper disable once InconsistentNaming
[MemoryDiagnoser]
public class ParserInvocation_Benchmark
{
private Func<string[], Data> _emitIlParser;
private Func<string[], Data> _expressionTreeParser;
private Func<string[], Data> _reflectionParser;
private Func<string[], Data> _sigilParser;
private Func<string[], Data> _roslynParser;
private Func<string[], Data> _sourceGeneratorParser;
private Func<string[], Data> _emitIlParser;
private Func<string[], Data> _expressionTreeParser;
private Func<string[], Data> _reflectionParser;
private Func<string[], Data> _sigilParser;
private Func<string[], Data> _roslynParser;
private Func<string[], Data> _sourceGeneratorParser;

private static readonly string[] Input = { "one", "1994-11-05T13:15:30", "22" };
private static readonly string[] Input = { "one", "1994-11-05T13:15:30", "22" };

[GlobalSetup]
public void GlobalSetup()
{
_emitIlParser = new EmitIlParserFactory().GetParser<Data>();
_expressionTreeParser = new ExpressionTreeParserFactory().GetParser<Data>();
_reflectionParser = new ReflectionParserFactory().GetParser<Data>();
_sigilParser = new SigilParserFactory().GetParser<Data>();
_roslynParser = RoslynParserInitializer.CreateFactory().GetParser<Data>();
// ReSharper disable once PossibleNullReferenceException
_sourceGeneratorParser = ((IParserFactory)Activator.CreateInstance(Type.GetType("BySourceGenerator.Parser"))).GetParser<Data>();
}
[GlobalSetup]
public void GlobalSetup()
{
_emitIlParser = new EmitIlParserFactory().GetParser<Data>();
_expressionTreeParser = new ExpressionTreeParserFactory().GetParser<Data>();
_reflectionParser = new ReflectionParserFactory().GetParser<Data>();
_sigilParser = new SigilParserFactory().GetParser<Data>();
_roslynParser = RoslynParserInitializer.CreateFactory().GetParser<Data>();
// ReSharper disable once PossibleNullReferenceException
_sourceGeneratorParser = ((IParserFactory)Activator.CreateInstance(Type.GetType("BySourceGenerator.Parser"))).GetParser<Data>();
}

[Benchmark]
public Data EmitIl()
{
return _emitIlParser.Invoke(Input);
}
[Benchmark]
public Data EmitIl()
{
return _emitIlParser.Invoke(Input);
}

[Benchmark]
public Data ExpressionTree()
{
return _expressionTreeParser.Invoke(Input);
}
[Benchmark]
public Data ExpressionTree()
{
return _expressionTreeParser.Invoke(Input);
}

[Benchmark]
public Data Reflection()
{
return _reflectionParser.Invoke(Input);
}
[Benchmark]
public Data Reflection()
{
return _reflectionParser.Invoke(Input);
}

[Benchmark]
public Data Sigil()
{
return _sigilParser.Invoke(Input);
}
[Benchmark]
public Data Sigil()
{
return _sigilParser.Invoke(Input);
}

[Benchmark]
public Data Roslyn()
[Benchmark]
public Data Roslyn()
{
return _roslynParser.Invoke(Input);
}

[Benchmark]
public Data SourceGenerator()
{
return _sourceGeneratorParser.Invoke(Input);
}

[Benchmark(Baseline = true)]
public Data ManuallyWritten()
{
var data = new Data();
if (0 < Input.Length)
{
return _roslynParser.Invoke(Input);
data.Name = Input[0];
}

[Benchmark]
public Data SourceGenerator()
if (1 < Input.Length && DateTime.TryParse(Input[1], out var bd))
{
return _sourceGeneratorParser.Invoke(Input);
data.Birthday = bd;
}

[Benchmark(Baseline = true)]
public Data ManuallyWritten()
if (2 < Input.Length && int.TryParse(Input[2], out var n))
{
var data = new Data();
if (0 < Input.Length)
{
data.Name = Input[0];
}
if (1 < Input.Length && DateTime.TryParse(Input[1], out var bd))
{
data.Birthday = bd;
}
if (2 < Input.Length && int.TryParse(Input[2], out var n))
{
data.Number = n;
}
return data;
data.Number = n;
}
return data;
}
}
}
4 changes: 2 additions & 2 deletions benchmark/Parsers.Benchmarks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
<PackageReference Include="BenchmarkDotNet" Version="0.15.4" />
</ItemGroup>

<ItemGroup>
Expand Down
41 changes: 14 additions & 27 deletions benchmark/Program.cs
Original file line number Diff line number Diff line change
@@ -1,30 +1,17 @@
using BenchmarkDotNet.Running;
using Parsers.Benchmarks;

namespace Parsers.Benchmarks
var mode = args.Length == 1 ? args[0] : "all";
switch (mode)
{
class Program
{
private const string AllMode = "all";
private const string GetParserMode = "gp";
private const string ParserInvocationMode = "pi";

static void Main(string[] args)
{
var mode = args.Length == 1 ? args[0] : AllMode;

switch (mode)
{
case GetParserMode:
BenchmarkRunner.Run<GetParser_Benchmark>();
break;
case ParserInvocationMode:
BenchmarkRunner.Run<ParserInvocation_Benchmark>();
break;
default:
BenchmarkRunner.Run<GetParser_Benchmark>();
BenchmarkRunner.Run<ParserInvocation_Benchmark>();
break;
}
}
}
}
case "gp":
BenchmarkRunner.Run<GetParser_Benchmark>();
break;
case "pi":
BenchmarkRunner.Run<ParserInvocation_Benchmark>();
break;
default:
BenchmarkRunner.Run<GetParser_Benchmark>();
BenchmarkRunner.Run<ParserInvocation_Benchmark>();
break;
}
2 changes: 0 additions & 2 deletions benchmark/run.cmd

This file was deleted.

Loading
Loading