Comparing execution speed of .NET dynamic proxy frameworks

A recent com­ment on Twit­ter from Tim Barcz started me think­ing about alter­na­tive proxy frame­works (alter­na­tive to Cas­tle Dynamic Proxy that is). Recently LinFu Dynamic Proxy started gain­ing some pop­u­lar­ity, after it was included as one of stan­dard byte­code providers in NHibernate.

Disclaimer:

To make things clear. I'm a long time user of Cas­tle Dynamic proxy and I may be biased towards it. I also hap­pen to know how to use it, con­trary to all the other frame­works, that’s why the fol­low­ing test may not be real­iz­ing their full poten­tial. If you spot a non-optimal use of any of the frame­works in the fol­low­ing test, let me know and I’ll update it.

Hav­ing said that, I put together a small sam­ple test project, where I gen­er­ate a one-interceptor proxy for a sim­ple inter­face with each frame­work and call a method on this proxy 100,000 times.

The inter­face is as sim­ple as it can be:

public interface IMyInterface1
{
    string Method1(int i);
}

Each inter­cep­tor looks roughly the same, and it only sets a return value for the method:

internal class SpringInterceptor : AopAlliance.Intercept.IMethodInterceptor
{
    public object Invoke(AopAlliance.Intercept.IMethodInvocation invocation)
    {
        //Debug.WriteLine("Hey, I intercepted the call! - Spring");
        return 5.ToString();
    }
}

I tested four frame­works. Cas­tle Dynamic Proxy 2.1 RC1, LinFu Dynamic Proxy 1.01, Spring.NET 1.2, which is a big frame­work in itself, but it has some pretty pow­er­ful prox­y­ing capa­bil­i­ties, and Microsoft Unity 1.2 which has now some prox­y­ing capa­bil­i­ties as well.

Here’s the result of the test on my 4 year old, 2GHz one-core PC:

proxy_frameworks_1

The first pass was a warm up, to let the jit­ter kick in, so the really impor­tant stuff, is the sec­ond pass.

Cas­tle Dynamic Proxy was the fastest one, which is not really a sur­prise for me. It’s designed to be fast and light­weight, and also has some pretty pow­er­ful capa­bil­i­ties that allow you to cus­tomize the proxy type being cre­ated so that you don’t waste cycles inter­cept­ing meth­ods you don’t want to inter­cept, or call­ing inter­cep­tors you don’t need called for a par­tic­u­lar method. (for an in-depth overview of Cas­tle Dynamic Proxy see my tuto­r­ial series).

The sec­ond one was Spring.NET which took over 3 times longer. This is how­ever still pretty fast, and con­sid­er­ing that it’s a pretty exten­si­ble and con­fig­urable frame­work as well, I’m sure in real life sce­nario you can squeeze very good per­for­mance out of it.

Third one, Unity was an order of mag­ni­tude slower than Cas­tle. This is not a sur­prise con­sid­er­ing how bloated and over engi­neered it is. To be fair how­ever, this may as well be due to strong tie between Unity IoC con­tainer and proxy frame­work. The proxy frame­work does not seem to be oper­a­ble out­side of the con­tainer, so the per­for­mance hit, may be par­tially due to some con­tainer overhead.

LinFu was two orders of mag­ni­tude slower than Cas­tle. This was really sur­pris­ing. Or at least until I noticed that it gath­ers a call stack for each and every invo­ca­tion, and as I men­tioned, this can (as clearly vis­i­ble) be a per­for­mance overkill. I looked for a way to turn it off, but it seems there isn’t any. Also the only way of call­ing the inter­cepted method is via MethodInfo.Invoke, which is also a lot slower than direct invo­ca­tion (or invo­ca­tion via a delegate).

Note that this is an iso­lated case of a sin­gle per­for­mance test. I thought it’s the most impor­tant one, but it’s just my opin­ion. I also didn’t talk about capa­bil­i­ties, which should be your major con­cern when select­ing a frame­work. – Can it sup­port my sce­nario is the ulti­mate test. Frankly, the fastest two, are also the most mature and powerful.

You can check the com­plete code here.

UPDATE:

Here’s a screen­shot from a pro­filer show­ing the LinFu’s proxy call tree. As you can clearly see, the major per­for­mance hit (over 90% of the time!) is in Stack­Trace con­struc­tor. It also obtains a Method­Info dynam­i­cally instead of caching it, like other frame­works do, which is an expen­sive oper­a­tion as well.

LinFu

UPDATE 2:

LinFu has been updated and it no longer col­lects stack trace infor­ma­tion: I re-ran the test and you can see the results here.

 

Tech­no­rati Tags: , , , ,

  • http://devlicio.us/blogs/tim_barcz/ Tim Barcz

    Krzysztof,

    I am suprised to see LinFu's per­for­mance degrade so badly.…especially con­sid­er­ing how LinFu was rep­re­sented on Twitter.

    Any the­o­ries on why such a slow per­for­mance for LinFu? Age? Implementation?

  • http://www.jonorossi.com/blog/ Jonathon Rossi

    Inter­est­ing, I had this task on my TODO list to see if we could improve Cas­tle Dynam­icProxy for com­mon cases, because I was hear­ing so many sto­ries of peo­ple say­ing how light­weight and fast LinFu is. In this case it is def­i­nitely not.

  • http://kozmic.pl/Default.aspx Krzysztof Koźmic

    @Jono

    By Light­weight, peo­ple may mean it's small. It has really small API with no explicit dif­fer­en­ti­a­tion between kinds of prox­ies (inter­face proxy, class proxy, inter­face proxy with­out tar­get etc). It also lets you have only one inter­cep­tor per proxy, it does not pro­vide hooks to decide what to inter­cept and what not.
    It may be suf­fi­cient for triv­ial cases, but I sim­ply view it as an one-man v1.0 prod­uct. It has made its goal of being small, but at a cost of fea­tures and run­time speed.

  • http://www.jonorossi.com/blog/ Jonathon Rossi

    LinFu DP sounds more basic than I thought it was going to be, but as you said it was prob­a­bly designed to be small not fea­ture packed.

  • Philip Lau­re­ano

    Hmm. I think the rea­son why you might have got­ten some really slow results is because call­ing MethodInfo.Invoke is sev­eral orders of mag­ni­tude slower than doing a native del­e­gate call. If you call MethodInfo.Invoke(), there really is no con­test here.

    If you did a Dynam­icMethod call on the method, how­ever, I think you might just get more accu­rate results…

  • http://kozmic.pl/Default.aspx Krzysztof Koźmic

    @Philip

    I'm not call­ing MethodInfo.Invoke, take a look at the code.
    Your arti­cle on Code­Pro­ject, shows that it's the way to go to invoke inter­cepted method, and I only noted that it's slower than what other frame­works do.

    // Note: If you wanted to call the orig­i­nal
    // imple­men­ta­tion, uncom­ment the fol­low­ing line:
    //result = info.TargetMethod.Invoke(_target, info.Arguments);

    That's the piece of your arti­cle. If there's a bet­ter way of han­dling method invo­ca­tion now, please let me know :)
    But again, this was not a fac­tor in my test.

  • Philip Lau­re­ano

    Hi Krzysztof,

    Thanks for point­ing the stack trace out–that was the last place I would have looked! Any­way, I went ahead and patched LinFu.DynamicProxy so that the stack trace has been removed. :) Take a look at v1.02, and let me know if there's a change in performance.

    Thanks for the feedback!

    Regards,

    Philip Lau­re­ano

  • http://neweril.com/tukang-nggame/ Tukang Nggame

    this is great infor­ma­tion that i know a lot of peo­ple are inter­ested in.
    thanks for the great offer, this really nice idea.

  • http://www.ronaldwidha.net/ Ronald Widha

    Didn't know unity comes with a dynamic proxy abil­ity. Let me check that out.
    Thanks!

  • http://kozmic.pl/Default.aspx Krzysztof Koźmic

    @Ronald,

    Yes, it does, although I found it… not very friendly to use, but that's my impres­sion of Unity in general.