LINQ Expressions as Fast Reflection Invoke
Lately, I’ve been working on a side project that generates a lot of LINQ expression trees (LET). They provide a fun new way to dynamically generate executable code at runtime. Dynamic code generation has been around for a long time, and .NET has had dynamic assembly generation since the very beginning. The original mechanism, System.Reflection.Emit can be a lot of work to generate something small. (Not to mention it has little IL validation, so it will happily generate broken code that crashes your program with all kinds of strange exceptions.) Yesterday, I asked myself why not use expression trees instead of reflection for dynamic invocation. It immediately struck me as a wonderful idea.
The reason LETs are so great is that they compile down to real IL, and then get JITed, so they run at native speeds. Using reflection to dynamically invoke a method is far slower. Here is the code to create generic accessor delegates using both LET and reflection:
static Func<T, U> CreateWithLET<T, U>(string property)
{
var t = Expression.Parameter(typeof(T), “t”);
var prop = Expression.Property(t, property);
return (Func<T, U>)Expression.Lambda(prop, t).Compile();
}
static Func<T, U> CreateWithReflectionFast<T, U>(string property)
{
Type type = typeof(T);
object[] args = new object[0];
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public;
PropertyInfo propinfo = type.GetProperty(property, flags);
MethodInfo getter = propinfo.GetGetMethod();
return (T t) => (U)getter.Invoke(t, args);
}
static Func<T, U> CreateWithReflectionSlow<T, U>(string property)
{
return (T t) => (U)typeof(T).InvokeMember(property,
BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.Public,
null, t, new object[0]);
}
I have focused on making the first two methods short, readable, and as efficient as possible. Both cases do as much work as possible in the create method. The third is to show that the reflection version can be written in one line, but if you plan to use it repeatedly, it will be much slower than the multi-line version above. This is a really simple yet very powerful example. It would be trivial to change this example to call a method and pass in arguments
I wrote four benchmarks. Each benchmark runs in a loop, the cost of the loop is subtracted from the total, and each delegate is given 100 warm up iterations to ignore the cost of just in time compilation. (I’ve included the benchmark driver at the bottom of this article.) The first benchmark calculates the amount of time it takes to create the delegates we are testing. The second benchmark times 100,000 iterations accessing an int property. The third benchmark times 100,000 iterations accessing a string property. The fourth benchmark times 100,000 iterations accessing two int properties and adding the results from within the generated LET .
I added the int tests because I thought the reflection tests would be adversely effected by needing to box and unbox the value of the property. In reality, reflection is so much slower that boxing accounts for about 2% of execution time.
| Create Delegate | Access int (100,000 times) |
Access string (100,000 times) |
Access 2 ints (100,000 times) |
|
| With LETs | 2.338ms | 0.45ms | 0.46ms | 0.53ms |
| With Fast Reflection | 0.46micros | 474ms | 462ms | 976ms |
| With Slow Reflection | 0.05micros | 988ms | 862ms | 1751ms |
I was surprised at just how much faster the LETs were than reflection. I expected them to be faster but they are about 1,000 times faster than the than fast reflection. The slow reflection approach takes twice as long as the fast reflection. I should point out that if you are only going to call the method once then reflection is still faster, but if you are going to use it in a loop, then LETs will generally win out. Another case were it makes sense to use the LET approach is when you want to precreate the accessor delegate and cache it so you can run it later when you need the speed.
Most of the 2ms it takes to compile the lambda expression is spent creating a dynamic assembly and various other things. So if you need to access multiple members dynamically and then do an operation on them, then LETs become even more appealing. It does not take significantly longer to to generate a lambda that access two properties than it does to access one. As you can see in the benchmark, a more complicated LET only takes a small amount of extra time, while using reflection is takes twice as long. This means that if you need to do some more complicated reflection, then LETs become more and more appealing.
Below is the benchmark function I wrote:
static void Run<T, U>(T t, Func<T, U> a)
{
// time loop
var sw1 = Stopwatch.StartNew();
int j;
for (int i = 0; i < 100000; i++)
j = i;
sw1.Stop();// warm up
for (int i = 0; i < 100; i++)
a(t);// run test
var sw2 = Stopwatch.StartNew();
for (int i = 0; i < 100000; i++)
a(t);
sw2.Stop();
Console.WriteLine(sw2.Elapsed - sw1.Elapsed);
}
