Reflection vs. compiled expressions vs. delegates - Performance comparison

 
2/4/2012
.NET, C#
10 Comments

I'm currently working on an application which uses reflection to create a generic UI. Therefore I was interested in the performance impact of reflection. In this post I will do a comparison between the various possibilities to access a property.

The test setup

The following methods are used to access a property called Name in a class Person:

Access the property directly:

string name = p.Name;

Use reflection:

var property = p.GetType().GetProperty("Name");
string name = (string)property.GetValue(p, null);

This test is executed twice. First the PropertyInfo is created in every iteration. In the second test one instance is reused.

Use compiled expression:

ParameterExpression arg = Expression.Parameter(p.GetType(), "x");
Expression expr = Expression.Property(arg, "Name");

var propertyResolver = Expression.Lambda<Func<Person, object>>(expr, arg).Compile();

string name = (string)propertyResolver(p);

This test is also executed twice. First the Expression is created in every iteration. In the second test one instance is reused.

Use delegates:

ParameterExpression arg = Expression.Parameter(p.GetType(), "x");
Expression expr = Expression.Property(arg, "Name");

var propertyResolver = Expression.Lambda(expr, arg).Compile();

string name = (string)propertyResolver(p);

This seems to be the same as the test with the compiled expression. But there is a subtle change: The lambda is created without type information.

Each test is executed 1.000.000 times and the overall time is measured.

The results

Overview

TestDuration [ms]
Regualar property31
Reflection1026
Reflection with cached PropertyInfo510
Compiled Expression286.879
Cached compiled Expression58
Delegate299.173
Cached delegate3008

Conclusion

Using the property is of course the fasted way to get the value of a (known) property. But also reflection is not that slow, especially when you reuse the PropertyInfo object.
Compiling an expression is a time consuming operation, but if you reuse the compiled expression the performance is equal to the regular property access. Compilation of expressions only makes sense, if you have to process a lot of objects.
Delegates are relatively slow, because type information is missing compared to compiled expressions.

Downloads

Feedly Feedly Tweet


Related posts


Comments


Timo

Timo

12/9/2016

Delegate.CreateDelegate() turns out to be even faster than all solutions mentioned (when cached, of course). It creates a Func or Action based on the given MethodInfo, which we might retrieve using PropertyInfo.GetSetMethod().


tomek

tomek

12/19/2014
http://www.fplorer.com

"Delegate" implementation is wrong. The line: sb.AppendLine(propertyResolver.DynamicInvoke(p).ToString()); Should be: sb.AppendLine(((Func<Person, object>)propertyResolver)(p).ToString());


Stanislav

Stanislav

7/23/2014
http://yarmonov.pro

I'm afraid delegate performance is wrong. You measured dynamic invocation, not delegate. Delegate itself is a signature, so it contains all required type information. BTW delegate is generic param for expression compilation. The more proper benchmark will be: public delegate string DelegateSignature(Person person); public static string DelegateImplementation(Person person) { return person.Name; } private static void CachedDelegate(Person p) { DelegateSignature d = Program.DelegateImplementation; StringBuilder sb = new StringBuilder(); for (int i = 0; i < 1000000; i++) { sb.AppendLine(d(p).ToString()); } }


co-logic

co-logic

4/24/2014

The Setter for completeness: // Base Schema Class (no types) var setProperty = new Dictionary<string, Action<object, object>>(StringComparer.OrdinalIgnoreCase); // Derived Class setProperty.Add("Name", (n, v) => ((Person)n).Name = (string)v); // Call setProperty["Name"](p, Value);


co-logic

co-logic

4/24/2014

Hey, thanks for this code, helped me alot. What about this line? I use it for my O/R, where I need fast execution, but yet a generic (object) accessor in the base class. It took about 60ms on my machine: // Dictionary for the base class. No type information needed! var d = new Dictionary<string, Func<object, object>>(StringComparer.OrdinalIgnoreCase); // Filling the dictionary in the derived class d.Add("Name", (per) => { return ((Person)per).Name; }); // Measuring (60ms!) for (int i = 0; i < 1000000; i++) { sb.AppendLine(d["Name"](p).ToString()); }


Yves

Yves

2/12/2014
http://dev.unclassified.de/

Thank you very much for this brilliant piece of code, both the getter and the setter for compiled expressions. I've been able to build a simple custom O/RM with model attributes around this in a day (~1000 loc) and by measurements with reading 45000 records it takes around 1.5 the time of assigning directly from DbDataReader to model properties. With much less code to write and to maintain!


Kim

Kim

9/24/2013

Thanks for this! If anyone is interested, here's the code for the compiled setter (my apologies if the formatting gets messed up): ParameterExpression arg = Expression.Parameter(typeof (Person)); Expression expr = Expression.Property(arg, propertyName); Expression<Func<Person, string>> get = Expression.Lambda<Func<Person, string>>(expr, arg); Getter = get.Compile(); var member = get.Body; var param = Expression.Parameter(typeof (string), "value"); Setter = Expression.Lambda<Action<Person, string>>(Expression.Assign(member, param), get.Parameters[0], param) .Compile();


Sebastiaan Dammann

Sebastiaan Dammann

6/7/2013
http://damsteen.nl/

For some reason the tests yields different results here. The "Cached delegate" is actually quite a bit faster than "Cached compiled Expression". The cached delegate takes around 50 ms.


Daniel

Daniel

5/24/2013

@Miguel: It is faster. I think you are misinterpreting the numbers. The '.' is not used as a decimal separator but as a grouping symbol.


Miguel Angelo

Miguel Angelo

5/23/2013

The cached delegate time of 3008ms makes no sense... it should be faster than the no-cache version of itself, that took 299.173ms... shouldn't it?