So, how expensive is it to let Castle Windsor / Spring.NET make my object?

09.12.2007 - 16:43 (2 years, 9 months, 1 day ago)

Filed under programming, .NET, TrivadisContent, software architecture, castle windsor, dependency injection

Lately I was getting increasingly curious about the overhead of using a Dependency Injection-container to provide me with instances of my objects. With overhead I mean if and how much longer it takes for the DI container to provide me with the desired instance.

Indeed, this is not an overly interesting number. After all, how often do you instantiate an object in your Application's lifecycle and even if you do it very often, how does it compare to other activities of your Software like doing a Database Query?

Even so, I was curious, so here we go. First I needed a simple example. I came up with the usual BankAccount stuff that depends on a Exchange rate facility in order to work correctly. The following image gives you an overview:

System overview Bankaccount

I decided for the possibility of constructor-based injection, such that the normal usage of the BankAccount class would be like that:

BankAccount b = new BankAccount(new SimpleExchangeRateEngine());
b.Deposit(new MonetaryValue("EUR", 1000.23M));
The first DI container I used was the Castle Project Windsor container, which we have started to use on a customer's project. Necessary configuration aspects can be covered in the app.config and the configuration for this specific example looks like this:
<configuration>
  <configSections>
    <section name="castle"
      type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />
  </configSections>
  <castle>
    <components>
      <component id="ExchangeEngine" service="TheBank.IExchangeRateEngine, TheBank"
        type="TheBank.SimpleExchangeRateEngine, TheBank"
        lifestyle="transient"  />
      <component id="BankAccount" type="TheBank.BankAccount, TheBank" lifestyle="transient" />
    </components>
  </castle>
</configuration>

As you can see, the two important classes that we will be using are registered in the configuration. Interesting in this framework is the fact that you can explicitly state the "service" (i.e. the interface) which a class implements. You can then obtain a reference to an underlying implementation simply by passing an interface to the "Kernel", the part of the DI container that you need to talk to to get the desired object instance. In terms of the given framework this looks like that:

IWindsorContainer c = new WindsorContainer(new XmlInterpreter());
BankAccount b = c.Resolve();

How did the dependency to the IExchangeRateEngine service go into the BankAccount? This is a feature of the framework. It looks at the BankAccount constructor and will find that for the necessary service to be provided there is an entry in the configuration. It instantiates the dependency and passes it to the BankAccount instance.

For testing the construction speed, I wrote a program with two loops: The outer one ten times, the inner ones 10000 times the creation of a BankAccount object in the following ways:

  1. Simple Instantiation with new as already shown
  2. Instantiation with the Activator: Activator.CreateInstance(typeof(BankAccount), new SimpleExchangeRateEngine());
  3. DI based creation with Castle Windsor as already shown
  4. DI based creation with Spring.NET

Just for completeness I had a quick look at Spring.NET and paced it through pretty much the same routine. The major difference here is that for constructor-based dependency injection you need to explicitly state the constructor argument and what object of the registry should be passed in.

<configuration>
  <configSections>
    <sectionGroup name="spring">
      <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
      <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
    </sectionGroup>
  </configSections>
  <spring>
    <context>
      <resource uri="config://spring/objects"/>
    </context>
    <objects xmlns="http://www.springframework.net">
      <object id="ExchangeEngine" type="TheBank.SimpleExchangeRateEngine, TheBank" singleton="false" />
      <object id="BankAccount" type="TheBank.BankAccount, TheBank" singleton="false">
        <constructor-arg name="engine" ref="ExchangeEngine"/>
      </object>
    </objects>
  </spring>
</configuration>

A call to this framework looks like this:

IApplicationContext ctx = ContextRegistry.GetContext();
BankAccount b = (BankAccount)ctx.GetObject("BankAccount");

Anyway, I used the StopWatch object to time the 10000 instantiation loops and calculated an average. Here are the findings (average min/max values out of the ten times that the whole cycle was repeated, Release build running outside vshost) in milliseconds:

  • Normal construction: 0.0001 / 0.0002
  • Activator construction: 0.0069 / 0.0071
  • Container construction (Castle Windsor): 0.1014 / 0.1068
  • Container construction (Spring.NET): 0.069 / 0.0722

It should be noted that the very first instantiation is the most costly one in any scenario and that I have taken out the first instantiation in the Container case because that one is significantly slower than anything else:

  • Container construction (Castle Windsor): 55 milliseconds
  • Container construction (Spring.NET): 45 milliseconds

, a bl... eternity...

However, the Container seems to exist as some kind of singleton, since the recreation of the container did not affect that number (for both containers).

Well, yeah, one can state dramatically that the creation of an object via DI takes about 1000 times longer than by writing a simple "new". But since I am not a very dramatic person, and since us Engineers always have to live with conflicting options, I will not elaborate further.

kick it on DotNetKicks.com

Comments

Christoph ( 11.12.2007 - 21:25 UTC )
Two things to notice here: 1. Creation of ordinary objects doesn't costs you anything. It's fruitless to try to use things like object pooling to reduce the object creations except for special cases with really extensive initialization. 2. Activator needs some reflection and container based creation quite a lot. Reflection is still costly. Consequences: Don't use DI for fine grained objects but use facades instead to bundle the objects to reasonable sized components.
Vijay Santhanam ( 05.04.2008 - 03:27 UTC )
If you're in an environment where instantiation performance is causing you problems, IOC isn't for you there. Anything you put in the container should be the big important components. Real small lightweight objects should still be built using Factories (which the IOC can contain).
Jeremy D. Miller ( 05.04.2008 - 15:45 UTC )
You might give a shot at recreating those tests with StructureMap instead of Spring.Net or Windsor. I don't know that the activation time is necessarily that big of a deal, but StructureMap does object construction with emitted code instead of reflection for a bit of speed boost.
Majordomus Bloggia ( 05.04.2008 - 17:55 UTC )
Interesting information. Indeed, Structure Map has been on my to-do list for some time, as it feels quite different to the other two as well. Let's see when I get around to it!
Nate Kohari ( 06.04.2008 - 20:24 UTC )
Interesting article. Was there a reason why you registered the IExchangeRateEngine as transient? While it's admittedly then not a fair comparison vs. manually creating the objects, one of the major advantages of a DI framework is the ability to transparently re-use dependencies between instances. I'd also like to know what your benchmark does for my DI framework, Ninject. It relies on lightweight code generation to avoid the expense of reflection (particularly when the calls are repeated many times.) Let me know if you try it out. :)
singletonFalse ( 06.05.2008 - 03:03 UTC )
Hi you shouldn't be using spring to create new objects. Almost all my contexts do not have singleton=false in them. This is true even in web service and web apps. You should still create new objects with the new keyword. If you design your application correctly, you should have a wiring of objects that are thread safe where objects pass through the containers. For example, the PersonRepository would be spring wired and exist within the lifecycle of the application.. There is no need to recreate this object each time. The method PersonRepository.GetPerson would obtain the person from the database, create the POCO (not using Spring) object, and pass it to the service caller. The method call exists on your stack. The whole point is once the IOC is wired, the IOC implementation will not create another object unless you want it to. What you are doing is at the start is creating a full pipeline with logic in them. That pipeline stays the intact and pieces of the pipe do not get thrown into the garbage collector. The pipeline logic will create objects using new and pass them through to the next section. In fact, the memory footprint of your application decreases and the performance is quick because you are not working with the heap as much since most of the code lies in your stack. Thanks for the article.
singletonFalse ( 06.05.2008 - 03:21 UTC )
Hi try your test with this BankAccount only has fields with setters and getters (simple POCO) IBankAccountRepository with Deposit method and property IExchangeRateEngine and inject with SimpleExchangeRateEngine. MonetaryValue is fine because its already a POCO object. In your IBankAccountRepository implementation, do not use IOC to create a new BankAccount, i.e., use new(). So the only objects that are in your context is IBankAccountRepository and IExchangeRayeEngine implementations and do not use singleton=false. You can now setup the caller to obtain IBankAccountRepository impl from the context, create a new BankAccount object (usinng new()), and call IBankAccountRepository.Deposit (bankaccount, monetaryValue); Note that in a web app, its all autowired, and you never use IApplicationContext ctx = ContextRegistry.GetContext() to wire the app. Yes, the first time, its slower but after that, its instant because the singleton object resides in the context.

Post a comment