Skip to content

Instantly share code, notes, and snippets.

@usausa
Last active December 22, 2023 12:23
Show Gist options
  • Save usausa/ae86554e58950fce25ebf39939ce5ae5 to your computer and use it in GitHub Desktop.
Save usausa/ae86554e58950fce25ebf39939ce5ae5 to your computer and use it in GitHub Desktop.
CallBenchmark
namespace CallBenchmark;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;
public static class Program
{
public static void Main()
{
BenchmarkRunner.Run<Benchmark>();
}
}
public class BenchmarkConfig : ManualConfig
{
public BenchmarkConfig()
{
AddExporter(MarkdownExporter.GitHub);
AddColumn(
StatisticColumn.Mean,
StatisticColumn.Min,
StatisticColumn.Max,
StatisticColumn.P90,
StatisticColumn.Error,
StatisticColumn.StdDev);
AddDiagnoser(MemoryDiagnoser.Default, new DisassemblyDiagnoser(new DisassemblyDiagnoserConfig(maxDepth: 3, printSource: true, printInstructionAddresses: true, exportDiff: true)));
}
}
#pragma warning disable CA1822
[Config(typeof(BenchmarkConfig))]
[MediumRunJob(RuntimeMoniker.Net60)]
[MediumRunJob(RuntimeMoniker.Net80)]
public unsafe class Benchmark
{
private Func<object> factoryFunc = default!;
private ClassFactoryDelegate factoryDelegate = default!;
#pragma warning disable CA1859
private IFactory factoryImplement = default!;
private IFactory sealedFactoryImplement = default!;
#pragma warning restore CA1859
private AbstractFactory abstractFactoryImplement = default!;
private AbstractFactory sealedAbstractFactoryImplement = default!;
private delegate*<object> staticFactory = default!;
[GlobalSetup]
public void Setup()
{
var value = new object();
factoryFunc = () => value;
factoryDelegate = () => value;
factoryImplement = new FactoryImplement(value);
sealedFactoryImplement = new SealedFactoryImplement(value);
abstractFactoryImplement = new AbstractFactoryImplement(value);
sealedAbstractFactoryImplement = new SealedAbstractFactoryImplement(value);
StaticFactory.SetValue(value);
staticFactory = &StaticFactory.Create;
}
[Benchmark]
public object Func() => factoryFunc();
[Benchmark]
public object Delegate() => factoryDelegate();
[Benchmark]
public object FactoryImplement() => factoryImplement.Create();
[Benchmark]
public object SealedFactoryImplement() => sealedFactoryImplement.Create();
[Benchmark]
public object AbstractFactoryImplement() => abstractFactoryImplement.Create();
[Benchmark]
public object SealedAbstractFactoryImplement() => sealedAbstractFactoryImplement.Create();
[Benchmark]
public object StaticDirect() => StaticFactory.Create();
[Benchmark]
public object StaticPointer() => staticFactory();
}
#pragma warning restore CA1822
#pragma warning disable CA1711
public delegate object ClassFactoryDelegate();
#pragma warning restore CA1711
public interface IFactory
{
public object Create();
}
public class FactoryImplement : IFactory
{
private readonly object value;
public FactoryImplement(object value)
{
this.value = value;
}
public object Create() => value;
}
public sealed class SealedFactoryImplement : IFactory
{
private readonly object value;
public SealedFactoryImplement(object value)
{
this.value = value;
}
public object Create() => value;
}
public abstract class AbstractFactory
{
public abstract object Create();
}
public class AbstractFactoryImplement : AbstractFactory
{
private readonly object value;
public AbstractFactoryImplement(object value)
{
this.value = value;
}
public override object Create() => value;
}
public sealed class SealedAbstractFactoryImplement : AbstractFactory
{
private readonly object value;
public SealedAbstractFactoryImplement(object value)
{
this.value = value;
}
public override object Create() => value;
}
public static class StaticFactory
{
private static object value = default!;
public static void SetValue(object obj)
{
value = obj;
}
public static object Create() => value;
}
namespace CallAbstractionBenchmark;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;
public static class Program
{
public static void Main()
{
BenchmarkRunner.Run<Benchmark>();
}
}
public class BenchmarkConfig : ManualConfig
{
public BenchmarkConfig()
{
AddExporter(MarkdownExporter.GitHub);
AddColumn(
StatisticColumn.Mean,
StatisticColumn.Min,
StatisticColumn.Max,
StatisticColumn.P90,
StatisticColumn.Error,
StatisticColumn.StdDev);
AddDiagnoser(MemoryDiagnoser.Default, new DisassemblyDiagnoser(new DisassemblyDiagnoserConfig(maxDepth: 3, printSource: true, printInstructionAddresses: true, exportDiff: true)));
}
}
#pragma warning disable CA1822
[Config(typeof(BenchmarkConfig))]
[MediumRunJob(RuntimeMoniker.Net60)]
[MediumRunJob(RuntimeMoniker.Net80)]
public unsafe class Benchmark
{
private const int N = 1000000;
private Func<object> factoryFunc = default!;
private ClassFactoryDelegate factoryDelegate = default!;
#pragma warning disable CA1859
private IFactory factoryImplement = default!;
private IFactory sealedFactoryImplement = default!;
#pragma warning restore CA1859
private AbstractFactory abstractFactoryImplement = default!;
private AbstractFactory sealedAbstractFactoryImplement = default!;
private delegate*<object> staticFactory = default!;
[GlobalSetup]
public void Setup()
{
var value = new object();
factoryFunc = () => value;
factoryDelegate = () => value;
factoryImplement = new FactoryImplement(value);
sealedFactoryImplement = new SealedFactoryImplement(value);
abstractFactoryImplement = new AbstractFactoryImplement(value);
sealedAbstractFactoryImplement = new SealedAbstractFactoryImplement(value);
StaticFactory.SetValue(value);
staticFactory = &StaticFactory.Create;
}
[Benchmark]
public object Func()
{
var ret = default(object)!;
for (var i = 0; i < N; i++)
{
ret = factoryFunc();
}
return ret;
}
[Benchmark]
public object Delegate()
{
var ret = default(object)!;
for (var i = 0; i < N; i++)
{
ret = factoryDelegate();
}
return ret;
}
[Benchmark]
public object FactoryImplement()
{
var ret = default(object)!;
for (var i = 0; i < N; i++)
{
ret = factoryImplement.Create();
}
return ret;
}
[Benchmark]
public object SealedFactoryImplement()
{
var ret = default(object)!;
for (var i = 0; i < N; i++)
{
ret = sealedFactoryImplement.Create();
}
return ret;
}
[Benchmark]
public object AbstractFactoryImplement()
{
var ret = default(object)!;
for (var i = 0; i < N; i++)
{
ret = abstractFactoryImplement.Create();
}
return ret;
}
[Benchmark]
public object SealedAbstractFactoryImplement()
{
var ret = default(object)!;
for (var i = 0; i < N; i++)
{
ret = sealedAbstractFactoryImplement.Create();
}
return ret;
}
[Benchmark]
public object StaticDirect()
{
var ret = default(object)!;
for (var i = 0; i < N; i++)
{
ret = StaticFactory.Create();
}
return ret;
}
[Benchmark]
public object StaticPointer()
{
var ret = default(object)!;
for (var i = 0; i < N; i++)
{
ret = staticFactory();
}
return ret;
}
}
#pragma warning restore CA1822
#pragma warning disable CA1711
public delegate object ClassFactoryDelegate();
#pragma warning restore CA1711
public interface IFactory
{
public object Create();
}
public class FactoryImplement : IFactory
{
private readonly object value;
public FactoryImplement(object value)
{
this.value = value;
}
public object Create() => value;
}
public sealed class SealedFactoryImplement : IFactory
{
private readonly object value;
public SealedFactoryImplement(object value)
{
this.value = value;
}
public object Create() => value;
}
public abstract class AbstractFactory
{
public abstract object Create();
}
public class AbstractFactoryImplement : AbstractFactory
{
private readonly object value;
public AbstractFactoryImplement(object value)
{
this.value = value;
}
public override object Create() => value;
}
public sealed class SealedAbstractFactoryImplement : AbstractFactory
{
private readonly object value;
public SealedAbstractFactoryImplement(object value)
{
this.value = value;
}
public override object Create() => value;
}
public static class StaticFactory
{
private static object value = default!;
public static void SetValue(object obj)
{
value = obj;
}
public static object Create() => value;
}
BenchmarkDotNet v0.13.9+228a464e8be6c580ad9408e98f18813f6407fb5a, Windows 11 (10.0.22631.2715)
AMD Ryzen 9 5900X, 1 CPU, 24 logical and 12 physical cores
.NET SDK 8.0.100
  [Host]             : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2
  MediumRun-.NET 6.0 : .NET 6.0.25 (6.0.2523.51912), X64 RyuJIT AVX2
  MediumRun-.NET 8.0 : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2

IterationCount=15  LaunchCount=2  WarmupCount=10  
Method Job Runtime Mean Error StdDev Median Min Max P90 Code Size Allocated
Func MediumRun-.NET 6.0 .NET 6.0 0.3937 ns 0.0392 ns 0.0587 ns 0.3813 ns 0.3200 ns 0.5111 ns 0.4832 ns 15 B -
Delegate MediumRun-.NET 6.0 .NET 6.0 0.4410 ns 0.0201 ns 0.0294 ns 0.4431 ns 0.3872 ns 0.4892 ns 0.4813 ns 15 B -
FactoryImplement MediumRun-.NET 6.0 .NET 6.0 0.5346 ns 0.0854 ns 0.1197 ns 0.4831 ns 0.3922 ns 0.7016 ns 0.6677 ns 26 B -
SealedFactoryImplement MediumRun-.NET 6.0 .NET 6.0 0.4027 ns 0.0142 ns 0.0213 ns 0.4039 ns 0.3674 ns 0.4505 ns 0.4293 ns 26 B -
AbstractFactoryImplement MediumRun-.NET 6.0 .NET 6.0 0.3296 ns 0.0961 ns 0.1409 ns 0.4361 ns 0.1592 ns 0.4906 ns 0.4765 ns 18 B -
SealedAbstractFactoryImplement MediumRun-.NET 6.0 .NET 6.0 0.1908 ns 0.0096 ns 0.0140 ns 0.1889 ns 0.1745 ns 0.2185 ns 0.2115 ns 18 B -
StaticDirect MediumRun-.NET 6.0 .NET 6.0 0.0131 ns 0.0129 ns 0.0193 ns 0.0000 ns 0.0000 ns 0.0595 ns 0.0431 ns 14 B -
StaticPointer MediumRun-.NET 6.0 .NET 6.0 0.4208 ns 0.0281 ns 0.0394 ns 0.4224 ns 0.3693 ns 0.4967 ns 0.4697 ns 7 B -
Func MediumRun-.NET 8.0 .NET 8.0 0.8483 ns 0.0162 ns 0.0233 ns 0.8542 ns 0.8105 ns 0.8940 ns 0.8721 ns 37 B -
Delegate MediumRun-.NET 8.0 .NET 8.0 0.8405 ns 0.0176 ns 0.0264 ns 0.8412 ns 0.8033 ns 0.9097 ns 0.8656 ns 37 B -
FactoryImplement MediumRun-.NET 8.0 .NET 8.0 0.6196 ns 0.0186 ns 0.0267 ns 0.6130 ns 0.5792 ns 0.6610 ns 0.6536 ns 37 B -
SealedFactoryImplement MediumRun-.NET 8.0 .NET 8.0 0.6136 ns 0.0196 ns 0.0294 ns 0.6230 ns 0.5762 ns 0.6722 ns 0.6434 ns 37 B -
AbstractFactoryImplement MediumRun-.NET 8.0 .NET 8.0 0.6392 ns 0.0298 ns 0.0445 ns 0.6246 ns 0.5812 ns 0.7800 ns 0.6919 ns 35 B -
SealedAbstractFactoryImplement MediumRun-.NET 8.0 .NET 8.0 0.6217 ns 0.0198 ns 0.0284 ns 0.6232 ns 0.5735 ns 0.6801 ns 0.6573 ns 35 B -
StaticDirect MediumRun-.NET 8.0 .NET 8.0 0.6570 ns 0.0149 ns 0.0218 ns 0.6560 ns 0.6231 ns 0.7010 ns 0.6860 ns 14 B -
StaticPointer MediumRun-.NET 8.0 .NET 8.0 1.0772 ns 0.0249 ns 0.0373 ns 1.0688 ns 1.0198 ns 1.1426 ns 1.1238 ns 4 B -

BenchmarkDotNet v0.13.9+228a464e8be6c580ad9408e98f18813f6407fb5a, Windows 11 (10.0.22631.2715)
AMD Ryzen 9 5900X, 1 CPU, 24 logical and 12 physical cores
.NET SDK 8.0.100
  [Host]             : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2
  MediumRun-.NET 6.0 : .NET 6.0.25 (6.0.2523.51912), X64 RyuJIT AVX2
  MediumRun-.NET 8.0 : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2

IterationCount=15  LaunchCount=2  WarmupCount=10  

Method Job Runtime Mean Error StdDev Median Min Max P90 Code Size Allocated
Func MediumRun-.NET 6.0 .NET 6.0 1,296.4 μs 19.11 μs 26.79 μs 1,286.6 μs 1,273.5 μs 1,362.9 μs 1,340.4 μs 39 B 1 B
Delegate MediumRun-.NET 6.0 .NET 6.0 1,283.1 μs 5.83 μs 8.36 μs 1,284.8 μs 1,269.0 μs 1,300.5 μs 1,291.5 μs 39 B 1 B
FactoryImplement MediumRun-.NET 6.0 .NET 6.0 1,494.2 μs 7.44 μs 10.67 μs 1,492.8 μs 1,478.7 μs 1,514.3 μs 1,507.4 μs 48 B 1 B
SealedFactoryImplement MediumRun-.NET 6.0 .NET 6.0 1,498.6 μs 9.98 μs 14.31 μs 1,495.5 μs 1,478.5 μs 1,532.8 μs 1,513.4 μs 48 B 1 B
AbstractFactoryImplement MediumRun-.NET 6.0 .NET 6.0 1,171.4 μs 72.20 μs 105.82 μs 1,085.7 μs 1,060.3 μs 1,293.8 μs 1,285.4 μs 42 B 1 B
SealedAbstractFactoryImplement MediumRun-.NET 6.0 .NET 6.0 1,185.4 μs 77.44 μs 108.56 μs 1,265.9 μs 1,058.2 μs 1,298.4 μs 1,290.8 μs 42 B 1 B
StaticDirect MediumRun-.NET 6.0 .NET 6.0 211.5 μs 1.70 μs 2.55 μs 211.4 μs 207.2 μs 217.3 μs 214.5 μs 31 B -
StaticPointer MediumRun-.NET 6.0 .NET 6.0 1,276.1 μs 5.72 μs 8.56 μs 1,273.4 μs 1,260.2 μs 1,291.9 μs 1,287.4 μs 34 B 1 B
Func MediumRun-.NET 8.0 .NET 8.0 431.6 μs 2.37 μs 3.48 μs 432.8 μs 424.8 μs 437.2 μs 435.5 μs 65 B -
Delegate MediumRun-.NET 8.0 .NET 8.0 432.3 μs 2.05 μs 2.94 μs 433.1 μs 426.5 μs 438.2 μs 435.1 μs 65 B -
FactoryImplement MediumRun-.NET 8.0 .NET 8.0 429.5 μs 2.86 μs 4.10 μs 429.9 μs 422.6 μs 438.9 μs 433.6 μs 68 B -
SealedFactoryImplement MediumRun-.NET 8.0 .NET 8.0 428.1 μs 2.79 μs 4.10 μs 429.2 μs 422.4 μs 435.8 μs 432.4 μs 68 B -
AbstractFactoryImplement MediumRun-.NET 8.0 .NET 8.0 429.1 μs 3.13 μs 4.59 μs 429.6 μs 422.6 μs 441.5 μs 432.8 μs 65 B -
SealedAbstractFactoryImplement MediumRun-.NET 8.0 .NET 8.0 429.2 μs 3.22 μs 4.82 μs 429.3 μs 422.6 μs 441.7 μs 434.8 μs 65 B -
StaticDirect MediumRun-.NET 8.0 .NET 8.0 211.1 μs 1.36 μs 2.00 μs 211.7 μs 207.7 μs 214.2 μs 213.5 μs 31 B -
StaticPointer MediumRun-.NET 8.0 .NET 8.0 1,281.6 μs 6.22 μs 9.31 μs 1,279.5 μs 1,268.5 μs 1,301.5 μs 1,292.4 μs 31 B 1 B
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment