2000% faster using dynamic method calls

As you all know, the performance of invoking a method using reflection is not best. Or as I would say – it’s beneath contempt.

Due to a performance problem that I had the last days I had to dig into the problem and found a way to improve the method invocation at about 2000%. A MSDN article offered by my collegue Alexander Jung gave me the idea.

The idea behind is to create a generic DynamicMethod at runtime to set and get the value of a property. The dynamic methods will be invoked trough a generic delegate that can be created for that dynamic methods at runtime. The delegate looks like:

public delegate void GenericSetter(object target, object value);
public delegate object GenericGetter(object target);

The target parameter defines the instance of an object of that the property will set. The example code for my performance messure program looks like:


/*
* Create a dynamic method and a delegate for it
*/
PropertyInfo testPropertyInfo = typeof(BusinessEntitiy).GetProperty("Counter");
setter = CreateSetMethod(testPropertyInfo);
getter = CreateGetMethod(testPropertyInfo);

/*
* Now repeat it 1 million times and Set/Get the value
*/
DateTime startTime = DateTime.Now;
for (int x = 0; x < REPEATS; x++)
{
   setter(be, x);
   int check = (int)getter(be);


   Debug.Assert(check == x, "Setter call failed.");
}

The magic behind that code are the methods CreateSetMethod and CreateGetMethod. The implementation of those methods can be downloaded at the end of that posting.

These methods are using the DynamicMethod class and the ILGenerator in order to create a dynamic method at runtime that calls the getter and setter property without reflection. The performance boost is unbelievable. I tested the raw set and get functionality with about 1 million property calls. The standard reflection method invocation took about 4,6 seconds; the dynamic method call took only about 0,22 seconds on my machine.

1 million method calls
Click image to enlarge.

If you are interessted in that solution you can download the complete example using the following link:
2000% faster using dynamic method calls

Appendix:


///
/// Creates a dynamic setter for the property
///
private static GenericSetter CreateSetMethod(PropertyInfo propertyInfo)
{
   /*
   * If there's no setter return null
   */
   MethodInfo setMethod = propertyInfo.GetSetMethod();
   if (setMethod == null)
     return null;

   /*
   * Create the dynamic method
   */
   Type[] arguments = new Type[2];
   arguments[0] = arguments[1] = typeof(object);

   DynamicMethod setter = new DynamicMethod(
     String.Concat("_Set", propertyInfo.Name, "_"),
     typeof(void), arguments, propertyInfo.DeclaringType);
   ILGenerator generator = setter.GetILGenerator();
   generator.Emit(OpCodes.Ldarg_0);
   generator.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
   generator.Emit(OpCodes.Ldarg_1);

   if (propertyInfo.PropertyType.IsClass)
     generator.Emit(OpCodes.Castclass, propertyInfo.PropertyType);
   else
     generator.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);

   generator.EmitCall(OpCodes.Callvirt, setMethod, null);
   generator.Emit(OpCodes.Ret);

   /*
   * Create the delegate and return it
   */
   return (GenericSetter)setter.CreateDelegate(typeof(GenericSetter));
}

///
/// Creates a dynamic getter for the property
///
private static GenericGetter CreateGetMethod(PropertyInfo propertyInfo)
{
   /*
   * If there's no getter return null
   */
   MethodInfo getMethod = propertyInfo.GetGetMethod();
   if (getMethod == null)
     return null;

   /*
   * Create the dynamic method
   */
   Type[] arguments = new Type[1];
   arguments[0] = typeof(object);

   DynamicMethod getter = new DynamicMethod(
     String.Concat("_Get", propertyInfo.Name, "_"),
     typeof(object), arguments, propertyInfo.DeclaringType);
   ILGenerator generator = getter.GetILGenerator();
   generator.DeclareLocal(typeof(object));
   generator.Emit(OpCodes.Ldarg_0);
   generator.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
   generator.EmitCall(OpCodes.Callvirt, getMethod, null);

   if (!propertyInfo.PropertyType.IsClass)
     generator.Emit(OpCodes.Box, propertyInfo.PropertyType);

   generator.Emit(OpCodes.Ret);

   /*
   * Create the delegate and return it
   */
   return (GenericGetter)getter.CreateDelegate(typeof(GenericGetter));
}

About these ads

14 Responses to “2000% faster using dynamic method calls”

  1. Olmo Says:

    Thanks for the idea! we are using this tecnique in our opensource ORM as well: http://www.signumframework.com/Reflection.ashx

    The credit goes to you :)

  2. Dror Says:

    Its getting even better with Linq Expressions. The syntax is a better easier (but completely different) than IL code.
    The method below will get either property or field:

    static Func CreateGetPropValue(Type containerType, string propName)
    {
    var param = Expression.Parameter(typeof(object), “container”);
    var func = Expression.Lambda(
    Expression.Convert(
    Expression.PropertyOrField(
    Expression.Convert(
    param,
    containerType
    ),
    propName
    ),
    typeof(object)
    ),
    param
    );
    return (Func)func.Compile();
    }

  3. Light-weight Code Gen « Blog for JsonExSerializer Project Says:

    [...] LCG for short, is accomplished via the DynamicMethod class. I found the perfect example to follow here. However I needed slightly different code to handle calling getters on a struct. It took me a while [...]

  4. Hugh Says:

    This is superb, I have recently been delving into IL and I think this and other techniques are very under used by developers.
    We recently exploited DynamicMethod to execute dynamically defined IL that enables us to get the offset of fields within managed structs, many people assume Marshal.OffsetOf does this but it doesn’t (it only gives offsets that fields will have after being marshaled).
    I’m now looking to code equivalent logic to yours but for field access rather than property (not sure what kind of performance gains to expect here).
    Anyway, thanks for a very useful article.

  5. Shea Says:

    Thanks for this article – I had been trying to do what you demonstrated for the past 3 hours and was ready to punch a hole through my wall.

  6. Fast (dynamic) DataTable load « Mads Klinkby’s home Says:

    [...] a combination of these, using reflection just once then a fast mapping code from then on based on Gerhard Stephan’s code emitting method. It is used with a DataTable like this: dt.Columns.AddRange(TableMapper<MyDTO>.Columns); : [...]

  7. Bruno Chappe Says:

    Great ! This is EXACTLY what I was searching for the last 48h… Actually Dror’s answer was also very useful since I wanted to run comparative performance test of my own using the three methods (GetValue, dynamic methods, LinQ Expressions). If any post on my blog come out of this, a link to your post is definitly gonna be in the the top of it !

  8. TamusJRoyce Says:

    #If VBC_VER AndAlso VBC_VER >= 9.0 Then
    ‘ Use Dror’s method
    #Else
    ‘ Use Gerhard’s IL method
    #End If

    Thanks you. This really worked in all instances I needed it in.

  9. VR Says:

    Thanks for a greate idea, but, unfortunately, the code does not work at all for value type….
    It eiither returns some garbage values, or fails with an exception.
    For instance, you can test with a System.Drawing.Point type.
    Any ideas for a workaround?

    • VR Says:

      Dror ‘s method seems to work alright, the problem is that I have to target NET 2 at the moment, and migration to NET 4+ is nothing sure

    • VR Says:

      I found the solution. The problem was that the default argument of reference type properties was not being unboxed correctly. Instead of being downcast, explicit unboxing should have been applied, as shown here bellow:

      // Loading the first (and only) argument to the execution stack and downcasting it
      // Note that a specific downcasting method is required for a reference-type object property
      generator.Emit(OpCodes.Ldarg_0);
      if ( propertyInfo.ReflectedType.IsClass || propertyInfo.ReflectedType.IsInterface )
      generator.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
      else
      generator.Emit(OpCodes.Unbox, propertyInfo.ReflectedType);

      This solution I found here:
      http://stackoverflow.com/questions/1354368/dynamicmethod-returns-incorrect-value-when-property-type-is-int64

      Important: Using ‘OpCodes.Unbox_Any’ would NOT do the trick (I checked it myself).

      Note that checking ‘propertyInfo.ReflectedType.IsInterface’ might prove redundant.

      Footnote: sorry if my post lacks any explanation — I am not yet knowledgeable in the ways of MSIL magik.

  10. CodeRed Says:

    I had a hard time getting the Set method to work in my VB program, I realized the issue was because my method needed to accept values of type Object, and would need to handle type conversion myself. I found that by default VB uses its own internal conversion functions to ensure the incoming argument is the correct type. Here is my updated If block for the Set method:

    If PropInfo.PropertyType.IsClass Then
    generator.Emit(OpCodes.Ldarg_1)
    generator.Emit(OpCodes.Castclass, PropInfo.PropertyType)

    ElseIf PropInfo.PropertyType.IsValueType Then

    Dim Assembly = From x In System.AppDomain.CurrentDomain.GetAssemblies
    Where x.FullName.Split(“,”)(0) = “Microsoft.VisualBasic”
    Take 1

    Dim Ass = Assembly.FirstOrDefault()
    Dim Typ = Ass.GetType(“Microsoft.VisualBasic.CompilerServices.Conversions”)

    Dim meths = Typ.GetMethods()
    Dim Meth = From x In meths Where x.ReturnType Is PropInfo.PropertyType And
    x.GetParameters.Count > 0 AndAlso
    x.GetParameters(0).ParameterType Is GetType(Object)

    If Meth.Count = 0 Then
    generator.Emit(OpCodes.Ldarg_1)
    generator.Emit(OpCodes.Unbox_Any, PropInfo.PropertyType)
    Else
    generator.Emit(OpCodes.Ldarg_1)
    generator.Emit(OpCodes.Call, Meth.First)
    End If

    End If

    generator.Emit(OpCodes.Callvirt, setMethod)
    generator.Emit(OpCodes.Ret)


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 106 other followers

%d bloggers like this: