Performance: Using LCG to copy property values of two objects
Today I gave last performance boost to my property values copying mechanism. I would like to thank my readers Ron and David to remind me Lightweight Code Generation (LCG) and pointing me out aussie bloke blog entry Generic copy object using Lightweight Code Generation. In this posting I will show you last performance boost and put down a summary about my experiment this far.
To get better idea about what I have done this far read my blog postings Using LINQ and reflection to find matching properties of objects and Performance: Using dynamic code to copy property values of two objects.
Copying matching properties – Lightweight Code Generation (LCG)
The last performance boost was achieved by using LCG. I created static dictionary to cache generic delegates that invoke dynamically generated methods that perform actual copying. These methods are generated using Intermediate Language (IL) opcodes. Here is the code.
delegate void CopyPublicPropertiesDelegate<T,TU>
(T source, TU target);
static Dictionary<string, object> _del =
new Dictionary<string, object>();
static void GenerateCopyDelegate<T, TU>()
{
var className = GetClassName(typeof(T), typeof(TU));
var args = new[] {typeof(T), typeof(TU)};
var mod = typeof(Program).Module;
var dm = new DynamicMethod(className, null, args, mod);
var il = dm.GetILGenerator();
var maps = _maps[className];
foreach (var map in maps)
{
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldarg_0);
il.EmitCall(OpCodes.Callvirt,
map.SourceProperty.GetGetMethod(), null);
il.EmitCall(OpCodes.Callvirt,
map.TargetProperty.GetSetMethod(), null);
}
il.Emit(OpCodes.Ret);
var del = (CopyPublicPropertiesDelegate<T,TU>)dm.CreateDelegate(typeof(CopyPublicPropertiesDelegate<T,TU>));
_del.Add(className, del);
}
static void CopyUsingLcg<T, TU>(T source, TU target)
{
var sourceType = source.GetType();
var targetType = target.GetType();
var className = GetClassName(sourceType, targetType);
var del = (CopyPublicPropertiesDelegate<T, TU>)
_del[className];
del.Invoke(source, target);
}
The best result in my last posting was 0,0055 milliseconds (it was measured as average over 100.000 copy operations). LCG gives even better result: 0,0018 milliseconds! It is 3 times better than previous optimization.
Summary
Well… after four tries my results are as follows. Third column shows how many times performance grew compared to previous optimization. Fourth column show how much performance grew compared to first and unoptimized version.
Why I compare results to first one that is completely dumb and non-optimized version? Well… don’t forget that out there are people in hurry (by example, soldiers in death march projects). And there are also beginners who are not aware of these more complex optimization methods. These two cases are major dangers where performance problems are naturally coded. You can see here how much you can do for performance when using more complex optimization methods. I think performance raise ~22.4 times is not unnoticeable small thing.
Conclusion
Can we go any further from here? Yes, we can. The code you have seen so far sits in static class that is compiled to command line application. As a next thing I want to make this code reusable for different applications. I have some ideas but I have to think what is best solution in my scenario. But you will hear about this topic soon. :)