Created
January 14, 2020 12:12
-
-
Save IEVin/3045cfa67aef200d8c207cfb68bdf72c to your computer and use it in GitHub Desktop.
Perfomance check to invoke operator+ from C# generic
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Project Sdk="Microsoft.NET.Sdk"> | |
<PropertyGroup> | |
<OutputType>Exe</OutputType> | |
<TargetFrameworks>netcoreapp2.2;netcoreapp3.1;net461;net472</TargetFrameworks> | |
</PropertyGroup> | |
<ItemGroup> | |
<PackageReference Include="BenchmarkDotNet" Version="0.12.0" /> | |
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.7.0" /> | |
</ItemGroup> | |
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq.Expressions; | |
using System.Runtime.CompilerServices; | |
using BenchmarkDotNet.Attributes; | |
using BenchmarkDotNet.Jobs; | |
namespace GenericTest | |
{ | |
[SimpleJob(RuntimeMoniker.NetCoreApp31, baseline: true)] | |
[SimpleJob(RuntimeMoniker.CoreRt31)] | |
[SimpleJob(RuntimeMoniker.NetCoreApp22)] | |
[SimpleJob(RuntimeMoniker.CoreRt22)] | |
[SimpleJob(RuntimeMoniker.Net472)] | |
[SimpleJob(RuntimeMoniker.Net461)] | |
public class Test | |
{ | |
[Params(1000,100000)] | |
public int N; | |
[Benchmark] | |
public int Sum_Operator() | |
{ | |
int s = 0; | |
int i = N; | |
while(--i > 0) | |
s += i; | |
return s; | |
} | |
[Benchmark] | |
public int Sum_Unsafe() | |
{ | |
int s = 0; | |
int i = N; | |
while(--i > 0) | |
s = NumHelperUnsafe.Add(s, i); | |
return s; | |
} | |
[Benchmark] | |
public int Sum_Object() | |
{ | |
int s = 0; | |
int i = N; | |
while(--i > 0) | |
s = NumHelperObject.Add(s, i); | |
return s; | |
} | |
[Benchmark] | |
public int Sum_Cast() | |
{ | |
int s = 0; | |
int i = N; | |
while(--i > 0) | |
s = NumHelperCast.Add(s, i); | |
return s; | |
} | |
[Benchmark] | |
public int Sum_Expression() | |
{ | |
int s = 0; | |
int i = N; | |
while(--i > 0) | |
s = NumHelperExpression.Add(s, i); | |
return s; | |
} | |
[Benchmark] | |
public int Sum_Expression_NoDict() | |
{ | |
int s = 0; | |
int i = N; | |
while(--i > 0) | |
s = NumHelperExpressionNoDict.Add(s, i); | |
return s; | |
} | |
} | |
static class NumHelperCast | |
{ | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public static T Add<T>(T left, T right) | |
where T : unmanaged | |
{ | |
if(left is int i32Left && right is int i32Right) | |
{ | |
var sum = i32Left + i32Right; | |
if(sum is T res) | |
return res; | |
} | |
throw new NotSupportedException(); | |
} | |
} | |
static class NumHelperUnsafe | |
{ | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public static T Add<T>(T left, T right) | |
where T : unmanaged | |
{ | |
if (typeof(T) == typeof(int)) | |
{ | |
int sum = Unsafe.As<T, int>(ref left) + Unsafe.As<T, int>(ref right); | |
return Unsafe.As<int, T>(ref sum); | |
} | |
throw new NotSupportedException(); | |
} | |
} | |
static class NumHelperObject | |
{ | |
[MethodImpl(MethodImplOptions.NoInlining)] | |
private static T ThrowInvalidOperation<T>() => throw new InvalidOperationException("Not support type"); | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public static T Add<T>(T left, T right) | |
where T : unmanaged | |
{ | |
if (typeof(T) == typeof(int)) | |
{ | |
return (T)(object)((int)(object)left + (int)(object)right); | |
} | |
return ThrowInvalidOperation<T>(); | |
} | |
} | |
static class NumHelperExpression | |
{ | |
static readonly Dictionary<(Type Type, string Op), Delegate> Cache = | |
new Dictionary<(Type Type, string Op), Delegate>(); | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public static T Add<T>(T left, T right) | |
where T : unmanaged | |
{ | |
var t = typeof(T); | |
// If op is cached by type and function name, use cached version | |
if (Cache.TryGetValue((t, nameof(Add)), out var del)) | |
return del is Func<T, T, T> specificFunc | |
? specificFunc(left, right) | |
: throw new InvalidOperationException(nameof(Add)); | |
var leftPar = Expression.Parameter(t, nameof(left)); | |
var rightPar = Expression.Parameter(t, nameof(right)); | |
var body = Expression.Add(leftPar, rightPar); | |
var func = Expression.Lambda<Func<T, T, T>>(body, leftPar, rightPar).Compile(); | |
Cache[(t, nameof(Add))] = func; | |
return func(left, right); | |
} | |
} | |
static class NumHelperExpressionNoDict | |
{ | |
static class Cache<T> | |
{ | |
public static Func<T, T, T> AddFunc; | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public static T Add<T>(T left, T right) | |
where T : unmanaged | |
{ | |
if (Cache<T>.AddFunc == null) | |
Cache<T>.AddFunc = Build_Add_Delegate<T>(); | |
return Cache<T>.AddFunc(left, right); | |
} | |
[MethodImpl(MethodImplOptions.NoInlining)] | |
static Func<T,T,T> Build_Add_Delegate<T>() | |
{ | |
var leftPar = Expression.Parameter(typeof(T), "left"); | |
var rightPar = Expression.Parameter(typeof(T), "right"); | |
var body = Expression.Add(leftPar, rightPar); | |
return Expression.Lambda<Func<T, T, T>>(body, leftPar, rightPar).Compile(); | |
} | |
} | |
public class Program | |
{ | |
public static void Main() => BenchmarkDotNet.Running.BenchmarkRunner.Run<Test>(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
> dotnet run -c Release -f netcoreapp3.1 | |
// * Summary * | |
BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18363 | |
Intel Core i7-7700 CPU 3.60GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores | |
.NET Core SDK=3.1.100 | |
[Host] : .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT | |
Job-TIFUJX : .NET Framework 4.8 (4.8.4042.0), X64 RyuJIT | |
Job-SCHUGI : .NET Framework 4.8 (4.8.4042.0), X64 RyuJIT | |
Job-NYZWUO : .NET Core 2.2.0 (CoreCLR 4.6.27110.04, CoreFX 4.6.27110.04), X64 RyuJIT | |
Job-HWNFWQ : .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT | |
Job-JODAGG : .NET CoreRT 1.0.28514.01 @BuiltBy: dlab14-DDVSOWINAGE101 @Branch: master @Commit: 42f1bc077327d6e3647861bec00cfe0458187071, X64 AOT | |
Job-OJKGRE : .NET CoreRT 1.0.28514.01 @BuiltBy: dlab14-DDVSOWINAGE101 @Branch: master @Commit: 42f1bc077327d6e3647861bec00cfe0458187071, X64 AOT | |
| Method | Runtime | N | Mean | Error | StdDev | Median | Ratio | RatioSD | | |
|---------------------- |-------------- |------- |----------------:|--------------:|--------------:|----------------:|------:|--------:| | |
| Sum_Operator | .NET 4.6.1 | 1000 | 257.7 ns | 3.83 ns | 3.58 ns | 255.6 ns | 1.01 | 0.01 | | |
| Sum_Operator | .NET 4.7.2 | 1000 | 254.3 ns | 0.88 ns | 0.78 ns | 254.2 ns | 1.00 | 0.01 | | |
| Sum_Operator | .NET Core 2.2 | 1000 | 254.2 ns | 0.53 ns | 0.44 ns | 254.1 ns | 1.00 | 0.01 | | |
| Sum_Operator | .NET Core 3.1 | 1000 | 254.5 ns | 1.35 ns | 1.20 ns | 253.9 ns | 1.00 | 0.00 | | |
| Sum_Operator | CoreRt 2.2 | 1000 | 254.1 ns | 1.60 ns | 1.34 ns | 253.5 ns | 1.00 | 0.01 | | |
| Sum_Operator | CoreRt 3.1 | 1000 | 253.8 ns | 0.55 ns | 0.46 ns | 253.7 ns | 1.00 | 0.00 | | |
| | | | | | | | | | | |
| Sum_Unsafe | .NET 4.6.1 | 1000 | 254.5 ns | 0.77 ns | 0.68 ns | 254.2 ns | 0.99 | 0.01 | | |
| Sum_Unsafe | .NET 4.7.2 | 1000 | 256.9 ns | 3.98 ns | 3.72 ns | 254.9 ns | 1.00 | 0.02 | | |
| Sum_Unsafe | .NET Core 2.2 | 1000 | 257.3 ns | 3.72 ns | 2.91 ns | 258.0 ns | 1.00 | 0.02 | | |
| Sum_Unsafe | .NET Core 3.1 | 1000 | 256.6 ns | 3.27 ns | 3.06 ns | 255.9 ns | 1.00 | 0.00 | | |
| Sum_Unsafe | CoreRt 2.2 | 1000 | 255.0 ns | 2.01 ns | 1.88 ns | 254.7 ns | 0.99 | 0.01 | | |
| Sum_Unsafe | CoreRt 3.1 | 1000 | 255.8 ns | 3.36 ns | 3.14 ns | 254.0 ns | 1.00 | 0.02 | | |
| | | | | | | | | | | |
| Sum_Object | .NET 4.6.1 | 1000 | 255.7 ns | 3.38 ns | 3.00 ns | 254.3 ns | 1.00 | 0.02 | | |
| Sum_Object | .NET 4.7.2 | 1000 | 258.4 ns | 3.52 ns | 2.94 ns | 259.4 ns | 1.01 | 0.02 | | |
| Sum_Object | .NET Core 2.2 | 1000 | 254.3 ns | 0.50 ns | 0.44 ns | 254.3 ns | 0.99 | 0.01 | | |
| Sum_Object | .NET Core 3.1 | 1000 | 256.5 ns | 2.86 ns | 2.67 ns | 255.4 ns | 1.00 | 0.00 | | |
| Sum_Object | CoreRt 2.2 | 1000 | 256.5 ns | 4.56 ns | 4.04 ns | 255.2 ns | 1.00 | 0.01 | | |
| Sum_Object | CoreRt 3.1 | 1000 | 257.3 ns | 4.40 ns | 3.90 ns | 256.5 ns | 1.00 | 0.01 | | |
| | | | | | | | | | | |
| Sum_Cast | .NET 4.6.1 | 1000 | 16,972.8 ns | 269.92 ns | 252.48 ns | 17,076.4 ns | 1.97 | 0.04 | | |
| Sum_Cast | .NET 4.7.2 | 1000 | 16,779.3 ns | 52.81 ns | 49.40 ns | 16,774.1 ns | 1.94 | 0.03 | | |
| Sum_Cast | .NET Core 2.2 | 1000 | 9,047.8 ns | 27.16 ns | 22.68 ns | 9,050.5 ns | 1.05 | 0.01 | | |
| Sum_Cast | .NET Core 3.1 | 1000 | 8,633.2 ns | 128.14 ns | 119.86 ns | 8,596.5 ns | 1.00 | 0.00 | | |
| Sum_Cast | CoreRt 2.2 | 1000 | 7,958.4 ns | 129.17 ns | 120.83 ns | 7,917.4 ns | 0.92 | 0.02 | | |
| Sum_Cast | CoreRt 3.1 | 1000 | 7,914.6 ns | 155.58 ns | 152.80 ns | 7,874.4 ns | 0.92 | 0.03 | | |
| | | | | | | | | | | |
| Sum_Expression | .NET 4.6.1 | 1000 | 65,420.5 ns | 246.68 ns | 205.99 ns | 65,337.5 ns | 1.16 | 0.01 | | |
| Sum_Expression | .NET 4.7.2 | 1000 | 82,839.0 ns | 226.74 ns | 201.00 ns | 82,825.2 ns | 1.47 | 0.01 | | |
| Sum_Expression | .NET Core 2.2 | 1000 | 66,329.4 ns | 719.60 ns | 637.91 ns | 66,071.0 ns | 1.18 | 0.01 | | |
| Sum_Expression | .NET Core 3.1 | 1000 | 56,396.9 ns | 565.17 ns | 528.66 ns | 56,163.1 ns | 1.00 | 0.00 | | |
| Sum_Expression | CoreRt 2.2 | 1000 | 220,401.6 ns | 574.99 ns | 480.14 ns | 220,320.9 ns | 3.90 | 0.04 | | |
| Sum_Expression | CoreRt 3.1 | 1000 | 215,887.3 ns | 475.28 ns | 421.32 ns | 215,961.0 ns | 3.83 | 0.04 | | |
| | | | | | | | | | | |
| Sum_Expression_NoDict | .NET 4.6.1 | 1000 | 2,462.0 ns | 8.31 ns | 7.37 ns | 2,459.3 ns | 0.95 | 0.00 | | |
| Sum_Expression_NoDict | .NET 4.7.2 | 1000 | 2,223.2 ns | 15.10 ns | 13.38 ns | 2,218.8 ns | 0.85 | 0.01 | | |
| Sum_Expression_NoDict | .NET Core 2.2 | 1000 | 2,342.4 ns | 7.57 ns | 7.08 ns | 2,342.7 ns | 0.90 | 0.00 | | |
| Sum_Expression_NoDict | .NET Core 3.1 | 1000 | 2,602.0 ns | 12.92 ns | 10.79 ns | 2,603.3 ns | 1.00 | 0.00 | | |
| Sum_Expression_NoDict | CoreRt 2.2 | 1000 | 128,665.7 ns | 2,830.54 ns | 8,256.82 ns | 123,459.1 ns | 49.17 | 3.48 | | |
| Sum_Expression_NoDict | CoreRt 3.1 | 1000 | 125,284.1 ns | 5,474.24 ns | 9,587.69 ns | 120,077.6 ns | 49.86 | 4.23 | | |
| | | | | | | | | | | |
| Sum_Operator | .NET 4.6.1 | 100000 | 24,702.0 ns | 54.07 ns | 45.15 ns | 24,681.9 ns | 1.00 | 0.00 | | |
| Sum_Operator | .NET 4.7.2 | 100000 | 24,722.9 ns | 74.59 ns | 62.29 ns | 24,707.5 ns | 1.00 | 0.00 | | |
| Sum_Operator | .NET Core 2.2 | 100000 | 24,760.5 ns | 103.70 ns | 97.00 ns | 24,738.8 ns | 1.00 | 0.00 | | |
| Sum_Operator | .NET Core 3.1 | 100000 | 24,687.8 ns | 45.52 ns | 35.54 ns | 24,702.1 ns | 1.00 | 0.00 | | |
| Sum_Operator | CoreRt 2.2 | 100000 | 24,777.1 ns | 192.64 ns | 170.77 ns | 24,706.2 ns | 1.00 | 0.01 | | |
| Sum_Operator | CoreRt 3.1 | 100000 | 24,833.9 ns | 258.02 ns | 228.73 ns | 24,704.7 ns | 1.01 | 0.01 | | |
| | | | | | | | | | | |
| Sum_Unsafe | .NET 4.6.1 | 100000 | 24,841.8 ns | 369.33 ns | 327.41 ns | 24,692.1 ns | 1.01 | 0.01 | | |
| Sum_Unsafe | .NET 4.7.2 | 100000 | 24,767.4 ns | 96.90 ns | 75.65 ns | 24,743.4 ns | 1.00 | 0.00 | | |
| Sum_Unsafe | .NET Core 2.2 | 100000 | 24,749.5 ns | 52.50 ns | 49.11 ns | 24,750.6 ns | 1.00 | 0.00 | | |
| Sum_Unsafe | .NET Core 3.1 | 100000 | 24,686.5 ns | 78.30 ns | 65.39 ns | 24,665.0 ns | 1.00 | 0.00 | | |
| Sum_Unsafe | CoreRt 2.2 | 100000 | 24,688.1 ns | 32.07 ns | 29.99 ns | 24,697.1 ns | 1.00 | 0.00 | | |
| Sum_Unsafe | CoreRt 3.1 | 100000 | 24,668.0 ns | 20.58 ns | 17.18 ns | 24,666.2 ns | 1.00 | 0.00 | | |
| | | | | | | | | | | |
| Sum_Object | .NET 4.6.1 | 100000 | 24,787.4 ns | 102.09 ns | 90.50 ns | 24,795.0 ns | 1.00 | 0.01 | | |
| Sum_Object | .NET 4.7.2 | 100000 | 24,901.4 ns | 263.32 ns | 233.43 ns | 24,803.0 ns | 1.00 | 0.01 | | |
| Sum_Object | .NET Core 2.2 | 100000 | 24,741.5 ns | 79.86 ns | 70.79 ns | 24,750.7 ns | 1.00 | 0.01 | | |
| Sum_Object | .NET Core 3.1 | 100000 | 24,785.7 ns | 220.07 ns | 195.09 ns | 24,694.5 ns | 1.00 | 0.00 | | |
| Sum_Object | CoreRt 2.2 | 100000 | 25,286.9 ns | 367.15 ns | 343.44 ns | 25,394.8 ns | 1.02 | 0.01 | | |
| Sum_Object | CoreRt 3.1 | 100000 | 24,690.4 ns | 35.01 ns | 31.04 ns | 24,694.0 ns | 1.00 | 0.01 | | |
| | | | | | | | | | | |
| Sum_Cast | .NET 4.6.1 | 100000 | 1,652,707.2 ns | 6,028.82 ns | 5,034.33 ns | 1,654,539.1 ns | 1.52 | 0.02 | | |
| Sum_Cast | .NET 4.7.2 | 100000 | 1,664,312.5 ns | 7,826.80 ns | 7,321.19 ns | 1,661,624.2 ns | 1.54 | 0.02 | | |
| Sum_Cast | .NET Core 2.2 | 100000 | 907,366.1 ns | 1,957.60 ns | 1,735.36 ns | 907,544.9 ns | 0.84 | 0.01 | | |
| Sum_Cast | .NET Core 3.1 | 100000 | 1,081,171.7 ns | 17,976.53 ns | 16,815.26 ns | 1,074,136.1 ns | 1.00 | 0.00 | | |
| Sum_Cast | CoreRt 2.2 | 100000 | 792,101.2 ns | 14,186.87 ns | 12,576.29 ns | 787,770.9 ns | 0.73 | 0.02 | | |
| Sum_Cast | CoreRt 3.1 | 100000 | 799,300.3 ns | 13,187.41 ns | 11,690.29 ns | 798,088.5 ns | 0.74 | 0.01 | | |
| | | | | | | | | | | |
| Sum_Expression | .NET 4.6.1 | 100000 | 6,575,780.3 ns | 23,228.01 ns | 18,134.90 ns | 6,583,162.1 ns | 1.12 | 0.00 | | |
| Sum_Expression | .NET 4.7.2 | 100000 | 8,308,984.2 ns | 30,022.00 ns | 28,082.60 ns | 8,299,875.0 ns | 1.42 | 0.00 | | |
| Sum_Expression | .NET Core 2.2 | 100000 | 6,640,131.8 ns | 30,789.51 ns | 25,710.63 ns | 6,637,887.5 ns | 1.13 | 0.00 | | |
| Sum_Expression | .NET Core 3.1 | 100000 | 5,869,053.8 ns | 8,064.09 ns | 7,543.16 ns | 5,865,239.8 ns | 1.00 | 0.00 | | |
| Sum_Expression | CoreRt 2.2 | 100000 | 21,457,534.8 ns | 50,772.21 ns | 45,008.24 ns | 21,468,464.1 ns | 3.66 | 0.01 | | |
| Sum_Expression | CoreRt 3.1 | 100000 | 21,525,873.5 ns | 195,601.94 ns | 182,966.18 ns | 21,441,962.5 ns | 3.67 | 0.03 | | |
| | | | | | | | | | | |
| Sum_Expression_NoDict | .NET 4.6.1 | 100000 | 238,786.1 ns | 664.45 ns | 589.02 ns | 238,900.3 ns | 0.88 | 0.02 | | |
| Sum_Expression_NoDict | .NET 4.7.2 | 100000 | 222,201.8 ns | 551.43 ns | 515.80 ns | 222,291.8 ns | 0.82 | 0.01 | | |
| Sum_Expression_NoDict | .NET Core 2.2 | 100000 | 235,319.4 ns | 419.77 ns | 327.73 ns | 235,285.7 ns | 0.87 | 0.02 | | |
| Sum_Expression_NoDict | .NET Core 3.1 | 100000 | 270,398.5 ns | 5,057.83 ns | 4,731.10 ns | 269,102.2 ns | 1.00 | 0.00 | | |
| Sum_Expression_NoDict | CoreRt 2.2 | 100000 | 12,274,687.0 ns | 92,672.68 ns | 72,352.73 ns | 12,259,372.7 ns | 45.48 | 0.90 | | |
| Sum_Expression_NoDict | CoreRt 3.1 | 100000 | 11,974,665.6 ns | 61,162.26 ns | 47,751.47 ns | 11,975,140.6 ns | 44.37 | 0.91 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment