Hiro 与其他 IoC 容器

发布于 2024-10-21 23:56:31 字数 409 浏览 9 评论 0原文

本文中( 2009 年 4 月 11 日),作者声称 Hiro 是:

“世界上最快的 IOC 容器...静态预编译的 IOC 容器,其执行速度与没有 IOC 容器的应用程序一样快”

它仍然是当今最快的 IOC 容器吗?准备好生产了吗?还有其他容器可以在编译时进行 IOC 吗?与其他 IOC 容器相比,它的主要优点和缺点是什么?

谢谢

In this article (11 Apr 2009), the author claims Hiro is:

"the World's Fastest IOC Container... a statically precompiled IOC container that performs as fast as an application without an IOC container".

Is it still the fastest IOC container today? Is it ready for production? Are there any other containers can do IOC at compile time? What are its major advantages and disadvantages over other IOC containers?

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

下壹個目標 2024-10-28 23:56:31

Hiro 声称是最快的容器。本声明基于作者给出的基准(参见此处对许多容器进行客观比较)。该基准是否现实取决于您的应用程序的大小。该基准测试似乎是用一组非常小的注册类型巧妙地设置的。当向基准添加更多注册时,性能开始下降(请参阅下面的示例基准)。仔细观察,我们可以发现 Hiro 的性能特征为 O(n)< /a>,而普通 DI 框架具有 O(1) 的特性,因此对于其他框架,性能随着注册类型的数量保持不变。

Hiro 的优点在于它可以动态生成新的程序集,并且只需一次接口调用即可解析新类型。这非常快。另一方面,最常见的 DI 框架在调用 GetInstance 期间始终必须进行字典查找。 Hiro 的 GetInstance 方法在内部基本上是一个大的 switch case 语句,以一堆 if 语句的形式实现。基于 if 的 switch case 语句在某种程度上是非常快的。 C# 编译器使用(我相信)18 个 case 语句的启发式作为转折点。低于该数字,编译器会生成一堆 if 语句。超过该数字,它会创建并存储静态字典并执行字典查找,因为超过 18 次查找的字典查找性能会更快。

好吧,你猜怎么着; Hiro 不像 C# 编译器那样使用优化。这就是为什么随着越来越多的类型添加到容器中,性能不断下降的原因。线性性能特征(简称 O(n))对于小型数据集来说是可以的,但是任何编写良好、大小通用、依赖友好的应用程序都具有许多类型注册/映射。在这种情况下,Hiro 的性能会迅速下降。

所以,回答你的问题:

它仍然是最快的 IOC 容器吗
今天?

对于非常小的应用程序,它是最快的。对于任何正常大小的应用程序来说,它从来都不是最快的。

准备好投入生产了吗?

很难说。它目前的状态是 alpha,它缺少其他 IOC 框架所具有的许多功能。 开发人员刚刚(请参阅下面的评论)发布了一个稳定版本。据开发商称,这意味着它已准备好投入生产。

它的主要优点是什么
与其他 IOC 容器相比有何缺点?

例如,看看这篇文章 (以及遵循up),它对 IOC 框架进行了很好的(功能)比较。文章作者对他认为重要的事情有一个愿景。你必须为自己列一个这样的清单。性能似乎在您的列表中很重要。但是,请注意,还有其他方法可以提高性能。例如,通过将类型注册为单例来防止创建许多类型。

这是与其他容器的一些比较。请注意,我没有编写 Hiro,所以我可能会遗漏一些东西,特别是因为似乎根本没有文档,但这里是:

  1. 您需要 4 个程序集才能运行,而不是大多数 IOC 框架的 1 或 2 个程序集。
  2. 当递归依赖关系得到解决时,它会引发堆栈溢出(顺便说一句,大多数框架都会这样做)。所以不存在循环依赖检测。
  3. 似乎不支持除瞬态之外的任何生活方式(作者确实这么说将会,但我目前没有看到对此的支持)。这实际上对性能不利,因为大多数服务通常会注册为单例。 据作者称,它支持多种生活方式。
  4. 不支持解析开放泛型类型。
  5. 不支持未注册类型解析。
  6. 我没有接受 XML(智能感知)文档的文档。

还有其他容器可以做吗
编译时 IOC?

定义“编译时间”。 Hiro 在运行时生成一次新的程序集。其他(例如 AutofacWindsorSimple Injector) 在以下环境下发出 IL 或编译委托覆盖。这并不比一次性编译完整的程序集慢(但是,存在调用委托的开销,这比进行接口调用慢一点)。

顺便说一句,我更改了基准测试以查看上述行为的出现。注册了 50 种额外类型后,您将看到 Hiro 的执行速度已经是初始基准测试的三倍。这是我使用的代码:

public interface IHandler<T> { }

public class Handler<T> : IHandler<T> { }

public class HiroUseCase : UseCase
{
    IMicroContainer container;

    private static void RegisterHandler<T>(DependencyMap map)
    {
        map.AddService(typeof(IHandler<T>), typeof(Handler<T>));
    }       

    public HiroUseCase()
    {
        var map = new DependencyMap();

        // *** My added registrations
        RegisterHandler<byte>(map);
        RegisterHandler<byte?>(map);
        RegisterHandler<short>(map);
        RegisterHandler<short?>(map);
        RegisterHandler<ushort>(map);
        RegisterHandler<ushort?>(map);
        RegisterHandler<int>(map);
        RegisterHandler<int?>(map);
        RegisterHandler<uint>(map);
        RegisterHandler<uint?>(map);

        RegisterHandler<long>(map);
        RegisterHandler<long?>(map);
        RegisterHandler<ulong>(map);
        RegisterHandler<ulong?>(map);
        RegisterHandler<float>(map);
        RegisterHandler<float?>(map);
        RegisterHandler<double>(map);
        RegisterHandler<double?>(map);
        RegisterHandler<decimal>(map);
        RegisterHandler<decimal?>(map);

        RegisterHandler<DateTime>(map);
        RegisterHandler<DateTime?>(map);
        RegisterHandler<char>(map);
        RegisterHandler<char?>(map);
        RegisterHandler<object>(map);
        RegisterHandler<string>(map);
        RegisterHandler<bool>(map);
        RegisterHandler<bool?>(map);
        RegisterHandler<Enum>(map);
        RegisterHandler<DateTimeKind>(map);

        RegisterHandler<DateTimeKind?>(map);
        RegisterHandler<DateTimeOffset>(map);
        RegisterHandler<DateTimeOffset?>(map);
        RegisterHandler<DayOfWeek>(map);
        RegisterHandler<DayOfWeek?>(map);
        RegisterHandler<DBNull>(map);
        RegisterHandler<Delegate>(map);
        RegisterHandler<DivideByZeroException>(map);
        RegisterHandler<DllNotFoundException>(map);
        RegisterHandler<Exception>(map);

        RegisterHandler<KeyNotFoundException>(map);
        RegisterHandler<InvalidOperationException>(map);
        RegisterHandler<InvalidCastException>(map);
        RegisterHandler<InvalidProgramException>(map);
        RegisterHandler<InvalidTimeZoneException>(map);
        RegisterHandler<IDisposable>(map);
        RegisterHandler<IComparable>(map);
        RegisterHandler<IEquatable<int>>(map);
        RegisterHandler<IEnumerable>(map);
        RegisterHandler<IEqualityComparer>(map);

        // *** Original benchmark setup
        map.AddService(typeof(IWebApp), typeof(WebApp));
        map.AddService(typeof(IAuthenticator), typeof(Authenticator));
        map.AddService(typeof(IStockQuote), typeof(StockQuote));
        map.AddService(typeof(IDatabase), typeof(Database));
        map.AddService(typeof(IErrorHandler), typeof(ErrorHandler));
        map.AddService(typeof(ILogger), typeof(Logger));

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

        var loadedAssembly = assembly.ToAssembly();
        var containerType = loadedAssembly.GetTypes()[0];
        container = (IMicroContainer)Activator
            .CreateInstance(containerType);
    }

    public override void Run()
    {
        var webApp = 
            (IWebApp)container.GetInstance(typeof(IWebApp), null);
        webApp.Run();
    }
}

Hiro claims to be the fastest container. This statement is based on the benchmark given by the author (see here for an objective comparison between many containers). Whether or not this benchmark is realistic depends upon the size of your application. The benchmark seems to be cleverly setup with a very small set of registered types. When adding more registrations to the benchmark, the performance starts dropping (see below for an example benchmark of this). When looking closely we can see that Hiro has a performance characteristic of O(n), while normal DI frameworks have a characteristic of O(1), thus with other frameworks the performance stays constant with the number of registered types.

What’s nice about Hiro is that it generates a new assembly on the fly and resolving a new type consists of just a single interface call. This is very fast. Most common DI frameworks on the other hand, will always have to do a dictionary lookup during a call to GetInstance. Hiro’s GetInstance method is basically a big switch case statement internally, implemented as a bunch of if-statements. If-based switch case statements are extremely fast up until a point. The C# compiler uses an heuristic of (I believe) 18 case statements as turning point. Below that number the compiler generates a bunch of if-statements. Above that number it creates and stores a static dictionary and does a dictionary lookup, because above 18 lookups performance of a dictionary lookup if simply faster.

Well guess what; Hiro doesn’t use the optimization as the C# compiler does. That's why the performance keeps dropping as more and more types are added to the container. A linear performance characteristic –or O(n) for short- is okay for small sets of data, but any well written and commonly sized, dependency-friendly application has many type registrations / mappings. And in that case the performance of Hiro drops quickly.

So, to answer your questions:

Is it still the fastest IOC container
today?

For very small applications it is the fastest. For any normal sized applications it has never been the fastest.

Is it ready for production?

Hard to say. Its current status is alpha and it misses a lot of features that other IOC framework have. The developer just (see comments below) published a stable release. This means that according to the developer it is ready for production.

What are its major advantages
and disadvantages over other IOC containers?

Take a look for instance at this article (and the follow up) which gives a good (feature) comparison of IOC frameworks. The writer of the article has a vision of what he thinks is important. You have to make such a list for yourself. Performance seems to be high on your list. However, please note that there are other ways to improve performance. For instance by registering types as singletons to prevent the creation of many types.

Here is a little comparison with other containers. Note that I didn't write Hiro, so I could be missing things, especially since there seems to be no documentation at all, but here goes:

  1. You need 4 assemblies to run, instead of 1 or 2 for most IOC frameworks.
  2. It throws a stack overflow when a recursive dependency is resolved (most frameworks do this btw). So there is no cyclic dependency detection.
  3. Doesn't seem to support any lifestyle other than transient (the writer does say it will, but I currently see no support for this). This actually is bad for performance, because most services would normally be registered as singletons. According to the author, it supports multiple lifestyles.
  4. Doesn't support resolving open generic types.
  5. Doesn't support unregistered type resolution.
  6. I has no documentation accept XML (intellisense) documentation.

Are there any other containers can do
IOC at compile time?

Define 'compile time'. Hiro generates a new assembly on the fly once during runtime. Others (like Autofac, Windsor and Simple Injector) emit IL or compile delegates under the covers. This isn't any slower than compiling the complete assembly in one go (however, there is the overhead of calling the delegate which is a tiny bit slower than making an interface call).

BTW, I changed the benchmark to see the behavior described above appear. With 50 extra types registered you will see that Hiro performs already three times as slow as with the initial benchmark. Here is the code I used:

public interface IHandler<T> { }

public class Handler<T> : IHandler<T> { }

public class HiroUseCase : UseCase
{
    IMicroContainer container;

    private static void RegisterHandler<T>(DependencyMap map)
    {
        map.AddService(typeof(IHandler<T>), typeof(Handler<T>));
    }       

    public HiroUseCase()
    {
        var map = new DependencyMap();

        // *** My added registrations
        RegisterHandler<byte>(map);
        RegisterHandler<byte?>(map);
        RegisterHandler<short>(map);
        RegisterHandler<short?>(map);
        RegisterHandler<ushort>(map);
        RegisterHandler<ushort?>(map);
        RegisterHandler<int>(map);
        RegisterHandler<int?>(map);
        RegisterHandler<uint>(map);
        RegisterHandler<uint?>(map);

        RegisterHandler<long>(map);
        RegisterHandler<long?>(map);
        RegisterHandler<ulong>(map);
        RegisterHandler<ulong?>(map);
        RegisterHandler<float>(map);
        RegisterHandler<float?>(map);
        RegisterHandler<double>(map);
        RegisterHandler<double?>(map);
        RegisterHandler<decimal>(map);
        RegisterHandler<decimal?>(map);

        RegisterHandler<DateTime>(map);
        RegisterHandler<DateTime?>(map);
        RegisterHandler<char>(map);
        RegisterHandler<char?>(map);
        RegisterHandler<object>(map);
        RegisterHandler<string>(map);
        RegisterHandler<bool>(map);
        RegisterHandler<bool?>(map);
        RegisterHandler<Enum>(map);
        RegisterHandler<DateTimeKind>(map);

        RegisterHandler<DateTimeKind?>(map);
        RegisterHandler<DateTimeOffset>(map);
        RegisterHandler<DateTimeOffset?>(map);
        RegisterHandler<DayOfWeek>(map);
        RegisterHandler<DayOfWeek?>(map);
        RegisterHandler<DBNull>(map);
        RegisterHandler<Delegate>(map);
        RegisterHandler<DivideByZeroException>(map);
        RegisterHandler<DllNotFoundException>(map);
        RegisterHandler<Exception>(map);

        RegisterHandler<KeyNotFoundException>(map);
        RegisterHandler<InvalidOperationException>(map);
        RegisterHandler<InvalidCastException>(map);
        RegisterHandler<InvalidProgramException>(map);
        RegisterHandler<InvalidTimeZoneException>(map);
        RegisterHandler<IDisposable>(map);
        RegisterHandler<IComparable>(map);
        RegisterHandler<IEquatable<int>>(map);
        RegisterHandler<IEnumerable>(map);
        RegisterHandler<IEqualityComparer>(map);

        // *** Original benchmark setup
        map.AddService(typeof(IWebApp), typeof(WebApp));
        map.AddService(typeof(IAuthenticator), typeof(Authenticator));
        map.AddService(typeof(IStockQuote), typeof(StockQuote));
        map.AddService(typeof(IDatabase), typeof(Database));
        map.AddService(typeof(IErrorHandler), typeof(ErrorHandler));
        map.AddService(typeof(ILogger), typeof(Logger));

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

        var loadedAssembly = assembly.ToAssembly();
        var containerType = loadedAssembly.GetTypes()[0];
        container = (IMicroContainer)Activator
            .CreateInstance(containerType);
    }

    public override void Run()
    {
        var webApp = 
            (IWebApp)container.GetInstance(typeof(IWebApp), null);
        webApp.Run();
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文