Skip to content

Instantly share code, notes, and snippets.

@AlseinX
Last active December 14, 2020 11:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AlseinX/e8bebe279dfdf5ebcae97221a774c298 to your computer and use it in GitHub Desktop.
Save AlseinX/e8bebe279dfdf5ebcae97221a774c298 to your computer and use it in GitHub Desktop.
Property Access Test
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");
}
}
}
<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>
$ 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