Last active
December 14, 2020 11:38
-
-
Save AlseinX/e8bebe279dfdf5ebcae97221a774c298 to your computer and use it in GitHub Desktop.
Property Access 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
using System; | |
using System.Diagnostics; | |
using System.Linq.Expressions; | |
using System.Reflection; | |
using System.Reflection.Emit; | |
namespace PropertyAccessTest | |
{ | |
internal static class Program | |
{ | |
private static void Benchmark(object obj, PropertyInfo prop, Func<object, object> accessor, string name) | |
{ | |
var stopWatch = Stopwatch.StartNew(); | |
for (var i = 0; i < 10000000; i++) | |
{ | |
var result = accessor(obj); | |
} | |
stopWatch.Stop(); | |
Console.WriteLine($"{name}: {stopWatch.ElapsedMilliseconds}ms."); | |
} | |
private static Func<object, object> ByReflection(PropertyInfo prop) | |
=> o => prop.GetGetMethod().Invoke(o, new object[] { }); | |
private static Func<object, object> ByCompile(PropertyInfo prop) | |
{ | |
var param = Expression.Parameter(typeof(object)); | |
var exp = Expression.Lambda<Func<object, object>>( | |
Expression.Convert( | |
Expression.Property( | |
Expression.Convert(param, prop.DeclaringType), | |
prop | |
), | |
typeof(object) | |
), | |
param | |
); | |
return exp.Compile(); | |
} | |
private static Func<object, object> Converter<I, O>(Delegate func) | |
=> o => ((Func<I, O>)func)((I)o); | |
private static Func<object, object> ByCreateDelegate(PropertyInfo prop) | |
{ | |
var converter = typeof(Program) | |
.GetMethod(nameof(Converter), BindingFlags.Static | BindingFlags.NonPublic) | |
.MakeGenericMethod(prop.DeclaringType, prop.PropertyType) | |
.CreateDelegate<Func<Delegate, Func<object, object>>>(); | |
var accessor = prop | |
.GetGetMethod() | |
.CreateDelegate(typeof(Func<,>).MakeGenericType(prop.DeclaringType, prop.PropertyType)); | |
return converter(accessor); | |
} | |
private static Func<object, object> ByEmit(PropertyInfo prop) | |
{ | |
var method = new DynamicMethod("GetProperty", typeof(object), new[] { typeof(object) }); | |
var il = method.GetILGenerator(); | |
var get = prop.GetGetMethod(); | |
il.Emit(OpCodes.Ldarg_0); | |
il.Emit(get.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, get); | |
il.Emit(OpCodes.Ret); | |
return method.CreateDelegate<Func<object, object>>(); | |
} | |
private record Testee(int Value); | |
internal static void Main(string[] args) | |
{ | |
var testee = new Testee(1); | |
var prop = typeof(Testee).GetProperty(nameof(Testee.Value)); | |
Benchmark(testee, prop, ByReflection(prop), "By Reflection"); | |
Benchmark(testee, prop, ByCompile(prop), "By Compile"); | |
Benchmark(testee, prop, ByCreateDelegate(prop), "By CreateDelegate"); | |
Benchmark(testee, prop, ByEmit(prop), "By Emit"); | |
} | |
} | |
} |
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> | |
<TargetFramework>net5.0</TargetFramework> | |
</PropertyGroup> | |
<ItemGroup> | |
<PackageReference Include="System.Reflection.Emit.Lightweight" 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
$ dotnet run -c Release | |
By Reflection: 993ms. | |
By Compile: 40ms. | |
By CreateDelegate: 148ms. | |
By Emit: 23ms. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment