Building Functions vs. Building Expressions: Performance Comparison
Yesterday I posted an entry describing a method for building functions as opposed to building expressions to get a more succinct syntax, and in general a more functional approach. There was a touch of reflection in there though, so I wasn’t too sure what the performance would be like.
Even though the post was specifically about an alternative to building expressions, the commenter suggested that it was more efficient, based on his own benchmarks. I was shocked.
Not being the type to believe something without seeing it, I found myself compelled to write my own simple benchmark and the results were shocking.
Building functions is FASTER than building expressions. WAY faster. Let’s take a look:
The Code:
The function building method I created was this (again sorry for the formatting – I’m trying to make it fit):
1: public static Func<T, bool> BuildEqFuncFor<T>
2: (string prop, object val)
3: {
4: return t => t.GetType()
5: .InvokeMember(prop,
6: BindingFlags.GetProperty,
7: null,
8: t,
9: null) == val;
10: }
And the commenter suggested I use the approach where you build up an expression, and then compile it:
1: public static Func<T, bool> ExBuildEqFuncFor<T>
2: (string prop, object val)
3: {
4: var o = Expression.Parameter(typeof(T), "t");
5:
6: Expression<Func<T, bool>> expression =
7: Expression.Lambda<Func<T, bool>>(
8: Expression.Equal(
9: Expression.PropertyOrField(o, prop),
10: Expression.Constant(val)), o);
11:
12: return expression.Compile();
13: }
The Results:
I ran each method 1, 100, and 10,000 times and here were the results (in seconds):
It’s unbelievable how much faster the function building method ran, compared to creating an expression tree and compiling it. In fact, within 100 runs there was almost no change at all!
I looked into what was making the expression building function so long to create, and as I suspected it was the “Compile” method call that is used to turn the whole tree into a function. I changed the method to just return the expression and not compile it and re-ran the tests. It still took twice as long just to build the expression as it did to build an actual function.
If you think about it though, it makes sense. Compiling on the fly was never supposed to be considered a performant (yes I said “performant”) method of metaprogramming in C# and if you are going to use it, it would be wise to implement some caching to prevent the need to constantly build the same functions again and again.
Or, you can just build the functions directly.