Showing posts with label Benchmarks. Show all posts
Showing posts with label Benchmarks. Show all posts

Nate Kohari informed me that he fixed the performance issue that I discovered in my last test, so I thought I rerun the benchmark against the trunk version (revision 62) of Ninject.

I have also included another new container named Autofac, written by Nicholas Blumhardt and Rinat Abdullin.

This container has nice list of features, here are a few:

  • Autowiring (with out any intrusive attributes)
  • XML Configuration Support
  • Nice C# registration API (including possibility to create components with expressions)
  • Module system (nice way to structure parts of your application)
  • Nested containers

The registration API is at first glance like any other IoC container except it also provides the ability to override the autowiring by defining how components are created by using lambda expressions. Here is an example:

// using autowiring
builder.Register<UserController>().FactoryScoped();
// using expressions
builder.Register(c => new UserController(c.Resolve<IUserRepository>(), c.Resolve<IAuthentificationService>()))
  .FactoryScoped();

The second method could potentially be a lot faster since in it you create a anonymous method that creates the object directly. There is a lot more code to write and if you change the constructor you need to update the registration code, so I am not sure you would want to use it to for most components. To be fair to the other containers in the test I tested Autofac with both autowiring registration and with expression based registration. For more detail on how the benchmark works please view the first benchmark post.

Another interesting feature of Autofac is it's support for nested containers with predicable component cleanup. 

var container = // ...
using (var context = container.CreateInnerContainer())
{
  var controller = context.Resolve<IController>();
  controller.Execute(); // use controller..
}

In the above example the controller and all it's dependencies that implements IDisposable will be disposed.

Here are the results:

IoCSingleton_Autofac

IoCTransient_Autofac

It is nice to see that the Ninject problem has been solved. When using Autofacs expression (lambda) based registration API the results are, not surprising, a lot quicker than the other containers. I would have almost through that the performance difference was going be bigger, seeing how fast the new operator was in my first benchmark (where I made a crude comparison with using the new operator directly to create all the instances).  

My conclusion is the same as after the first test, the performance difference is not significant to warrant consideration when choosing a IoC container. Unless you create a incredibly large amount of transient components (not recommended), then maybe use Autofac :)

It was fun to try out Autofac, it is looking like an interesting contender, and might even replace Castle Windsor as my favorite. I doubt it though, Castle Windsor probably has the largest user base and I am pretty familiar with it's codebase / extension points. But who knows, you shouldn't always stick to what you know :)

For the benchmark code: IoCBenchmark_ReRevisted.zip

Someone (Joshua) mentioned Ninject in the comments to in my IoC Benchmark. I hadn't heard of this project before but after checking out ninject.org and seeing the project slogan "lighning-fast dependency injection" I felt that it deserved be included in the test.

I am not sure what the slogan refers to though, lightning-fast usage and setup or runtime performance? Anyway it has a nice fluent registration API:

Bind<IUserRepository>()
  .To<LdapUserRepository>()
  .Using<SingletonBehavior>();
  
Bind<IAuthentificationService>()
  .To<DefaultAuthentificationService>()
  .Using<SingletonBehavior>();

Bind<UserController>()
  .ToSelf()
  .Using<SingletonBehavior>(); 

The tests were made with the release build of RC1 downloaded from the project homepage. I was kind of surprised by the results:

IoCSingleton_WithNinject
IoCTransient_WithNinject

I might be doing something wrong here but this is the result I got. For the transient case there seems to be a big memory leak. The Ninject kernel seems to keep references to transient objects. I tried the kernel's Release function but the memory leak was still evident.

Just to check that it was not something wrong with my code I did a profiling trace with JetBrains dotTrace:

NinjectTrace

It could still be me setting something up in the wrong way, but it looks like it's a bug in the Ninject Core.

Just a note: I think that doing premature optimization can result in bad design and architecture. The reason I did this and the previous test was not to find the fastest container, it was firstly to get a change to play with new (to me) containers, and secondly to see if there were any significant performance difference between them worthy of taking into account when choosing a container.

My conclusion in the previous test was that the difference in performance was not big enough to be relevant compared to other aspects of the containers, like how much you like the API or the features. The Ninject result for the transient test above is very significant, however it is probably caused by a memory bug in the release candidate and will likely be fixed in the next release. If you disregard this bug I actually kind of liked Ninject, it a had a nice API and a way of doing things, but it's slogan is currently a little misleading :)

For the complete code: IoCBenchmark_Revisited.zip

Updated (2008-05-05):

Nate Kohari has fixed the performance issue, the results are now inline with the other containers. For the new results (which also includes Autofac) please view this post.

There are a number of inversion of control containers out there so I thought it would be an interesting experiment to do a simple benchmark. There are different ways that one can instantiate a type in .NET, for example via the new operator, Activator, GetUninitializedObject and Dynamic Method. The performance difference between these methods are in some cases quite high, maybe the same is true for these IoC containers? Granted IoC containers do more than just create objects so other factors will probably play a big role in the results.

So here are the contestants:

I have been using Castle Windsor since 2005 and I think it is the best of the bunch, so I guess I am unconsciously biased toward Windsor.  However I will try to make this benchmark as objective as I can.

The scenario for this test:

  • Have each IoC container resolve a UserController 1000 000 times
  • The UserController will have two constructor dependencies
  • Run the test with transient (new instance for each resolve) and singleton components

The UserController looks like this:

public class UserController
{
    private IUserRepository repository;
    private IAuthentificationService authService;

    public UserController(IUserRepository repository, IAuthentificationService authService)
    {
        this.repository = repository;
        this.authService = authService;
    }
}

I have also a general container interface that the benchmark engine will use. Each container will implement this interface.

public interface IContainer
{
    string Name { get; }

    T Resolve<T>();
    
    void SetupForTransientTest();
    void SetupForSingletonTest();
}

All tests used the latest released version of each library. Before you interpret these charts please observe that the measurement is for one million component resolves which means the actual time difference between each container is actually very small.

Here are the results when all components were setup as singletons:

IoCSingleton

Here are the results when all components were setup as transient:

IoCTransient

So what does these charts tell us? Lets take the biggest difference in the transient case, Spring.NET took 44.149 seconds and Unity took 8.164 seconds, what is the actual difference when resolving a single instance?

  Spring.NET : 44.149 / 1000000 = 0.000044149 seconds
  Unity      :  8.164 / 1000000 = 0.000008164 seconds

So the actual difference is only about 36 microseconds. Another way to put these values into perspective is to compare against the new operator. I created a NewOperatorContainer with a resolve method that looks like this:

public T Resolve<T>()
{
    object o = new UserController(new LdapUserRepository(), new DefaultAuthentificationService());
    return (T) o;
}

OK, comparing the above with an inversion of control container is like comparing apples to oranges, an IoC handles so much more than just object creation. Also an IoC cannot use the new operator directly but must use one of the other methods. My guess is that all IoC containers in this test uses an approach which involve IL Generation which if cashed comes close to using the new operator directly. Anyway I think it will show just how small the difference between the real IoC containers are. In order to visualize this I needed to invert the values so that high means fast and low means slow.

IoCInversed

Update: The above chart can be very misleading. The x-axis is not seconds but 1/s. I hope it shows that the difference between the containers are very small compared to instantiating the objects manually.

OK, can we draw any conclusion from the test? Well I think we can say that performance should not be an issue when choosing one of these IoC containers. The difference is too small. When you choose which container to use you should consider other aspects, like how invasive the container is to they way you want to work.

For the complete code: IoCBenchmark.zip