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.
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));
}
July 2, 2007 at 3:00 pm
[...] Check out Gerhard’s post here [...]
March 13, 2009 at 12:18 pm
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
March 27, 2009 at 1:59 pm
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();
}
July 5, 2009 at 12:37 am
[...] 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 [...]