Thursday, April 16, 2009

Introducing Hiro, the World's Fastest IOC Container, Part II: The Little Feature Set That Could

The Art of Lean

Now that everyone knows just how fast Hiro can be, the next question you might be asking yourself is, "What features will it support?"

The simplest answer that I can give is that Hiro is that it will implement as little features as possible--in fact, Hiro will implement so little features that it might turn away your traditional "dynamic" IOC user. Here's the features that Hiro will implement:

-Named/Anonymous Static Component Registration. This means that you'll be able to register your services using a service name, service type, and the implementing type.

-Convention Over Configuration for Registration and Injection.. In addition to its support for programmatic registration, Hiro will be able to scan your assemblies and automatically infer:
  1. The list of available services
  2. The list of concrete types that implement those services
  3. The list of properties that can be injected
  4. The constructors that will be used for constructor injection
  5. The services that will be injected into each parameter during a constructor injection call.
The most interesting part about this is the amount of work that will take to do this sort of registration. Take a look at this example:


var loader = new DependencyMapLoader();
var map = loader.LoadFrom(AppDomain.CurrentDomain.BaseDirectory, "Yourlibrary.dll");

IContainerCompiler compiler = new ContainerCompiler();
var microContainer = compiler.Compile(map);

// Do something with the container here

Believe it or not, this is the only code that you need to use to configure the dependencies for any assembly of any given size. What makes this even more interesting is the fact that all of this is done statically at (container) compile time, not runtime. This means that unlike the current generation of IOC containers, Hiro will not waste any time trying to rediscover your configuration, and based on the benchmark results from my previous post, the performance numbers are nothing short of being dramatically one-sided.

-Transient/Singleton instances. Aside from performance, however, Hiro has to be able to create both Transient (that is, plain old object instances created with the New operator) and Singleton instances.

-Property/Constructor Injection. Hiro will implement both property and constructor injection by default. What makes this even more interesting is that like everything else with Hiro, all property and constructor injection calls will all be precompiled into IL, meaning that there will be no performance issues when using Hiro.

-Stateless. Each Hiro-compiled container instance will not have any private local data (or even shared data) that will distinguish it from another compiled container of the same type. This means that you can scale Hiro's compiled containers across multiple cores AND multiple threads without using a single semaphore, mutex, or lock statement (in C#).

"But wait, your container isn't a REAL container until you implement feature X!"

Some would argue that Hiro would have to implement a minimum "baseline" feature set in order to be considered a "commercial" quality IOC container. However, my counterargument there is that it's this same "fat feature" mindset that got these IOC containers (including LinFu) into this speed problem in the first place. Secondly, if you're experienced enough about IOC to understand the significance of what Hiro does, then it's safe to assume that you're a user that falls into at least one of the following categories:

  1. You've probably written at least one IOC container framework, or
  2. You already are comfortable with an existing IOC container framework (such as Castle, Ninject, AutoFac, Unity, StructureMap, LinFu, etc) and you know enough to customize it to your needs.


Assuming that you're an IOC container author, it would be pointless for me to implement something in Hiro that you've probably rolled into your own framework, and given that you're skilled enough to write your own framework, it would be practically trivial for you to plug Hiro into your own framework and reap the performance benefits, and there's clearly no reason to reinvent the wheel here if you somehow did a better job than I did in implementing "feature X".

Now, if you think you're a user that falls into the second category, there's a good chance that you've pretty much decided to stick to the favorite framework of your choice, and like the other IOC container authors, there's really nothing that I can do for you unless you decide to plug in Hiro into your favorite container.

So between these two types of users, who do you think Hiro is written for?

Here's my answer: Neither one of them. Hiro is written for the average developer who wants to get started with an IOC container and doesn't have time to "geek out" over the latest and greatest features of an IOC container framework. Given that there are probably far better IOC container developers than myself, I've pretty much decided to skip the "my container is better than your container" religious wars and focus on what really matters: the end users.

The Pareto Pleasure Principle

Hiro doesn't need to implement 80% of the expected features of an IOC container in order to be useful--instead, it only has to implement the other 20% of the overall features that (in my opinion) people will actually use. In the end, if I can help those people get their jobs done in the simplest possible way without forcing them to wade through the "awesomeness" of my framework, then I would call Hiro a success, and at the end of the day, that's really all that matters.

16 comments:

  1. Hi there!
    I'm very interested in Hiro and I've checked out the newest source from google code, but here is a problem, I don't know how to use it without a basic example.
    I downloaded the performance.zip, but seems the usage code of hiro in it is out of date.
    At least I cannot find the ContainerCompiler in hiro newest version.

    Can you add a basic example to hiro project hosted in google code?

    Thanks!

    ReplyDelete
  2. Hi Allen,

    The reason why you're having problems with the ContainerCompiler in the trunk version is because it hasn't been written yet :)

    In the meantime, you can play with the latest version of the prototype from this SVN URL:

    http://hiro.googlecode.com/svn/branches/development

    For now, the development branch contains the only working copy of Hiro--I'm still in the process of rewriting it in the Trunk version so that the whole thing is fully covered with unit tests.

    As for the examples, I'll post an example of how to use Hiro once I get the trunk version up and running. Sorry for the inconvenience!

    Regards,

    Philip Laureano

    ReplyDelete
  3. Hi !

    I totally agree about keeping the IOC as simple as possible and as much uncoupled as possible with your own application code.

    Going further, I would like to be able to discover classes in external assemblies without the need to decorate them with the "Implements" attribute. So that they will be completely unaware of the IOC at all.

    ie, I'd love to write something like :

    container.LoadFrom[IExternalInterface](assembly, LifecycleType.Singleton);

    that will search for the classes implementing the the interface type.

    And then something like :

    var o = container.GetService[IExternalInterface]();

    or if they are many possible implementations :

    var o = container.GetService[IExternalInterface]("ServiceNameEqualToMyAssemblyClassName");

    PS : replace any brackets with greater and less signs ;-)
    PS bis : Hiro is my new Hero :-D

    ReplyDelete
  4. Hi Philippe,

    Actually, the prototype version of Hiro doesn't require any attribute declarations at all--it can literally "discover" your entire object model and determine how to construct every type in your assembly, provided that all of its dependencies can be satisfied.

    I'm going to continue this approach in the trunk version of Hiro, but I'll take it a step further--Hiro will only have two (and only two) modes of registration:

    1) Programmatic registration--this is where you manually list all the dependencies that you want the container to build

    2) Convention over configuration--This is where you have Hiro scan an entire assembly and have it infer which types can be constructed.

    Aside from speed, of course, I'm going to do something that no other IOC container framework (including LinFu) has done before--I'm going going to implement the features that I mentioned above, and that will be the end of the Hiro project.

    Everyone will still be able to extend Hiro ad infinitum, but for the most part, I won't be adding any more features to Hiro once it's completed.

    ReplyDelete
  5. I am confused.

    You have LinFu 1, LinFu 2, and Hiro.

    LinFu 1 seems like a nice product and you got it included with NHibernate.

    You seem to be upgrading version 1, and stopped upgrading version 2, and now there is Hiro.

    Which is it? What should someone choose? All these versions make me nervous to choose any of them.

    ReplyDelete
  6. Hi Trevor,

    LinFu 2.0 and Hiro are not mutually exclusive, and I have definitely *not* stopped upgrading LinFu--there's quite a few things that LinFu can do that Hiro cannot, and vice versa. For example, LinFu can easily intercept methods and extend (and even intercept) itself ad infinitum, while Hiro builds the fastest IOC containers in exchange for a lack of a feature set.

    I've got more than a few more features lined up for LinFu (such as AOP, DBC integration without attributes, etc), so I won't be abandoning any time soon. :)

    ReplyDelete
  7. Hi Philip,

    I'm really impressed with this project, i was searching for something like this, simple and fast, btw, i have already used spring.net, castle, linfu and ninject, but thisseems to be what i needed all this time =D.

    Hiro which programmatic support will have hiro? Will it support factory method like regisstration? it will support different lifecycles?

    Thanks and congrats for your work!!!

    TrentCioran

    ReplyDelete
  8. Hi Trent,

    Thanks for the kind words. :) Hiro will support only two lifecycles--transient, and singleton.

    And if you're interested in how Hiro will support programmatic service registration,
    take a look at this example:

    http://paste2.org/p/196078

    Right now, I don't have any plans on making Hiro support factory registration since I want to keep Hiro more simple and ultra-lightweight rather than making it flexible. The other IOC container frameworks (including LinFu) do an excellent job of implementing factory registration, and it should be pretty effortless to plug Hiro into each one of those containers to reap the same flexibility and performance benefits.

    ReplyDelete
  9. And what about LinFu's feature on Generic types? what if i have a generic repository RepositoryBase of T, and i want to every entity (in a DDD fashion) to have a repository, now i did this via a factory, well when i was using ninject, now i'm using linfu's feature on generics, will Hiro support some functionality like that?

    Thanks Philip.

    P.S. sorry for my english =P

    ReplyDelete
  10. @TrentCorian

    I suppose that it can be done as an extension, but I think that's a bit out of scope for what Hiro was intended to do.

    One thing to remember with Hiro is that it is a statically configured container by design, so you won't be able to dynamically add instances to the container or do things like convert a RepositoryBase<T> into a running Repository<YourType>.

    In order to convert a RepositoryBase<T> to a concrete type, you'll need to do a dynamic service lookup, and Hiro (by default) doesn't support that behavior.

    So the short answer is that yes, it can be done, but I'm trying to avoid going the dynamic route since other 1) containers already do that well and 2) I'm stopping development on Hiro once the features that I described above are complete.

    IMHO, the best way to keep an "ultra-thin" feature set is to stop adding features once all the required features are in place, and I think Hiro has enough features to get the job done for 80% of the time.

    p.s. I'll definitely keep that in mind when I start implementing the hiro-contrib project, though

    ReplyDelete
  11. Dear Friend,
    I don't know this is correct place to put this message, anyhow, I'm just writing, if you are think is it's wrong please ignore.You are articles are nice and excellent concept. I would like to invite to newly launched .NET Programming website the codegain.com on 1st of this June 2009. Currently CodeGain has more than 450 articles within the a month under the followings categories C#, VB.NET,ASP.NET,WPF,WCF,WFF,LINQ,SilverLight, AJAX, JQuery, JavaScript, Sql Servers , Oracle and more. To more list of categories visit the http://www.codegain.com. I have seen you are writing greatest article to web portal, I’m kindly asking you publish your article in codegain.com also and support to grow the CodeGain share this with your friends also. I am expecting good response from you. You can contact me using info@codegain.com.

    Thank you
    RRaveen
    codegain.com

    ReplyDelete
  12. Hi,

    Does Hiro supports mono ?

    thnaks,

    ReplyDelete
  13. @Anonymous:

    Yes, Hiro will run on anything that runs the CLR v2.0, including Mono.

    Regards,

    Philip Laureano

    ReplyDelete
  14. @Philip Laureano

    Perfect, thank you very much :)

    Serdar

    ReplyDelete
  15. Firstly, very impressive work!

    A quick question though, how can each Hiro-compiled container instance be stateless AND provide singleton scoped registrations?
    If I resolved the same singleton scoped component twice, would I get the same object instance both times? How about if I resolved the same component from two separate instances of identical Hiro-compiled containers, would I retrieve the same object instance?

    Tyson.

    ReplyDelete
  16. Hi Tyson,

    Each container is can provide statelessly scoped singletons because the onus is on the end-user to dispose of each Singleton. If you create two separate container instances of the same compiled container type and make two calls to create the same singleton type, you will always get one and only one instance.

    If you want to get an idea on the type of singletons that Hiro creates, take a look at this article:

    http://www.yoda.arachsys.com/csharp/singleton.html

    If you scroll all the way down to the bottom of the article and look at the "Fifth version - fully lazy instantiation" singleton implementation, that's the exactly the kind of singleton that Hiro will construct for you using IL. It's incredibly fast since the singleton code requires absolutely no locks, and it's completely lazy so if you never call the container to instantiate the singleton itself, then you will never even create that singleton instance.

    The best part about all this is that Hiro hides all the gory details for you, and you can use it like any other object instance. HTH :)

    -Phil

    ReplyDelete

Ratings by outbrain