Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Improve performance of obtaining MethodInfo from MethodCallExpression

Our UI system can generate a form from a MethodInfo. Before System.Linq.Expressions, we were getting the MethodInfo using reflection (method 1):

MethodInfo info = typeof(ExtensionTestClass).GetMethod("InstanceMethod", BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(string) }, null);

The bad part about this is that if we changed the signature or name of InstanceMethod, the code would still compile.

Enter expressions. Now we do this (method 2):

 MethodInfo info = GetMethod<ExtensionTestClass>(x => x.InstanceMethod("defaultValue", "defaultValue"));

or this (method 3):

MethodInfo info = GetMethod<ExtensionTestClass, string, string>(x => x.InstanceMethod);

The syntax is "better", we get intellisense, and we get compilation errors if the method doesn't exist or the signature doesn't match. However, method 2 and method 3 are about 10 to 20 times slower than reflection.

Some numbers (measured with StopWatch):

Single Call: Method 1: .0000565 Method 2: .0004272 Method 3: .0019222

100000 Calls: Method 1: .1171071 Method 2: 1.5648544 Method 3: 2.0602607

We don't actually compile the expression or execute it, and I'm interested if anyone has an explanation for the difference in performance.

UPDATE: The GetMethod<> code:

Method 2:

public static MethodInfo GetMethod<T>(Expression<Action<T>> target)
{
    MethodCallExpression exp = target.Body as MethodCallExpression;
    if (exp != null)
    {
        return exp.Method;
    }
    return null;
}

Method 3:

public static MethodInfo GetMethod<T, A1, A2>(Expression<Func<T, Action<A1, A2>>> expression)
{
    var lambdaExpression = (LambdaExpression)expression;
    var unaryExpression = (UnaryExpression)lambdaExpression.Body;
    var methodCallExpression = (MethodCallExpression)unaryExpression.Operand;
    var methodInfoExpression = (ConstantExpression)methodCallExpression.Arguments.Last();
    return (MethodInfo)methodInfoExpression.Value;
}
like image 274
Joe Enzminger Avatar asked Nov 14 '22 06:11

Joe Enzminger


1 Answers

My guess is that it's slower because the expression versions do the same reflection (although they might use the IL shortcut methodof which has no analogue in C#) in order to create the expression trees, in addition to the overhead of creating the trees themselves for every call (I don't think they are cached by the code that the compiler emits); plus you then have to read those trees to get the method back out.

Reflection might be 'slow' but in actual fact it's pretty darned fast; especially since I believe the data, behind the scenes, is cached as well. Thus once you've called GetMethod the second call will be faster. That provides another compelling proof for why the subsequent expression tree versions are slower - since they are actually doing more work.

If you're comfortable with IL, compile a version with all three and then analyse the compiled image with ILSpy or Reflector (in C# mode both will be clever and re-consitute the expression code back into C#, which is no good; so switch to IL mode) - take a look at the code that's emitted to produce the expression trees and you'll see what I mean.

like image 129
Andras Zoltan Avatar answered Nov 16 '22 04:11

Andras Zoltan