X

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 please also read first two posts in this series.

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. :)

Liked this post? Empower your friends by sharing it!
Categories: .NET

View Comments (4)

  • I've read this and previous articles and was surprised by huge (2 times) difference between dynamic class and this method. I think the reason of this difference is late bound call of CopyProps in the generated class. You can add some interface to the class definition code and then cast generated class to this interface to avoid late binding. After this, I'm sure, result of generated code will be much closer to the LCG.

    But this method is interesting too.

Related Post