Unity interception performance 2

The past two years or so I’ve been working with a rather large team of developers on the latest incarnation of our bespoke web site platform here at Thomas Cook NE. We decided early on to use Unity and the Interception Extension at pretty much all layers across the entire site. One page view will trigger tens or even hundreds of object buildups. I never gave the process much thought, but when the load test told us that the front page of our beta site (now live at http://beta.ving.se) peaked at 4-5 requests per second with 100% CPU usage, I got worried.

Looking a little bit closer at the Unity documentation, the first thing I did was to replace the TransparentProxyInterceptor with a combination of the InterfaceInterceptor and the VirtualMethodInterceptor. Unfortunately, that wasn’t such an easy task since only the TransparentProxyInterceptor handles events and delegates properly and the same goes for ref/out parameters. After a bit of refactoring though, everything was working again. But the performance was still not very satisfactory.

I had to dig myself quite deep into the mysteries of Unity until I found what I was looking for. Now, Unity is designed primarily to be flexible and extensible. It turns out that this flexibility was the big problem. The interception mechanism allows you to add, remove or change policies at runtime which means that the build up phase in the interception extension needs to check all policies and matching rules each time an object is built. That means that for every object that is create, all interceptable methods needs to be checked against all matching rules. On top of that, most of the matching rules make heavy use of reflection (looking for custom attributes, method signatures, etc).

Now, we use the interception mechanism quite extensively for tracing, error handling, logging and most importantly for caching but we don’t really have any use for that much flexibility. All our interception configuration happens in a config file that is read on application startup so we are quite fine with having to restart the application in order to change a matching rule or add a new call handler. Luckily, the source code for Unity and Enterprise Library is easily downloaded from CodePlex, so I decided to try to change the behavior to fit our needs.

So, what did I do? Well, I simply added a Dictionary to hold a HandlerPipeline for each MethodImplementationInfo that the interceptor tries to intercept. The snippet below is from the TypeInterceptionStrategy class and should give you an idea. The InstanceInterceptionStrategy got pretty much the same treatment.

   1: public class TypeInterceptionStrategy : BuilderStrategy

   2: {

   3:     [...]

   4:     private static Dictionary<MethodImplementationInfo, HandlerPipeline> _pipelineCache = new Dictionary<MethodImplementationInfo, HandlerPipeline>();

   5:     [...]

   6:  

   7:     public override void PostBuildUp(IBuilderContext context)

   8:     {

   9:         Guard.ArgumentNotNull(context, "context");

  10:     

  11:         IInterceptingProxy proxy = context.Existing as IInterceptingProxy;

  12:         if(proxy == null) return;

  13:     

  14:         ITypeInterceptionPolicy interceptionPolicy = GetInterceptionPolicy(context);

  15:     

  16:         Type typeToIntercept = BuildKey.GetType(context.BuildKey);

  17:         PolicySet interceptionPolicies = new PolicySet(BuilderContext.NewBuildUp<InjectionPolicy[]>(context));

  18:         IUnityContainer currentContainer = BuilderContext.NewBuildUp<IUnityContainer>(context);

  19:     

  20:         foreach (MethodImplementationInfo item in interceptionPolicy.Interceptor.GetInterceptableMethods(typeToIntercept, typeToIntercept))

  21:         {

  22:                 HandlerPipeline pipeline;

  23:                 if (!_pipelineCache.TryGetValue(item, out pipeline))

  24:                 {

  25:                     pipeline = new HandlerPipeline(

  26:                 interceptionPolicies.GetHandlersFor(item, currentContainer));

  27:                     _pipelineCache[item] = pipeline;

  28:                 }

  29:             proxy.SetPipeline(interceptionPolicy.Interceptor.MethodInfoForPipeline(item), pipeline);

  30:         }

  31:     }

  32:  

  33:     [...]   

  34: }

This, of course, is only a rather ugly quick-fix. I tried to find a way to easily incorporate something similar without having to recompile Unity, but I wasn’t successful. I thought about implementing my own Interception Strategies, but the Interception Extension is pretty wired to using only the ones that are found “in the box”, and building my own Interception Extension meant also building my own configuration classes and.. well, I don’t think it would have been worth it. But I might be missing something obvious; if you have a better idea on how to make sure that Unity doesn’t go through unnecessary work on each buildup I would be very happy to see it!

Anyway, what about the results? Well, with my new Unity build applied, the bandwidth of the load test rig got exhausted at 80 page views per second with the CPU at a comfortable 80%; I call that a pretty decent improvement. 🙂

For me, the big question now is whether or not there is support for my scenario in Unity 2.0 (which I believe is well under development). If anyone has information about this, please let me know! 

2 thoughts on “Unity interception performance

  1. Marco Dec 21,2010 15:35

    Hi Anders,
    this post is interesting, congrats! Have you had the opportunity to do some test with Enterprise Library 5.0 with Unit 2.0 ?
    I’m thinking about to use that kind of approch but before to put it in real world I’m doing software selection or maybe think about changing direction. I can’t pay all of this effort in term of complexity, timing and performance.
    Any feedback is appreciated
    –marco

    • anders Dec 21,2010 15:43

      The same issue seems to be present in Unity 2.0 as well, but I would advice you to do your own benchmark tests with something that is relevant to your own scenario. We have quite specific needs where this turned out to be a problem, but for most scenarios I think Unity performs reasonably well.

Comments are closed.