【问题标题】:Hiro vs other IoC containersHiro 与其他 IoC 容器
【发布时间】:2011-07-14 23:17:12
【问题描述】:

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

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

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

谢谢

【问题讨论】:

  • 您是否真的需要来自 IoC 容器的绝对最高速度,在 99% 的应用程序中不是遇到过瓶颈吗?
  • 简单服务定位器声称也非常快:simpleservicelocator.codeplex.com/discussions/236029。我敢打赌它会胜过 Hiro。
  • @Mauricio Scheffer,是的,我愿意 :) 在我当前的项目中,我需要每 0.1 秒创建大约 10,000 多个对象。使用 Ninject,这是不可能的(Ninject 在我的机器上每 0.1 秒只能创建 2,000 个对象)。我目前正在使用 Autofac,它比 Ninject 快 4 倍。我对它的速度很满意(对于我目前的项目)。但我的下一个项目可能需要处理更多对象。
  • @caveman:我会重新考虑这样做,我认为您的应用程序会在 GC 中花费一些不平凡的时间。
  • @caveman 您每秒创建 10,000 次的组件 - 如果它是如此热门的路径,为什么不回退到使用“新”呢?通常优化意味着对 99% 的代码使用最干净的工具/技术/模式,然后对那 1% 的热门代码使用最快的...

标签: .net performance inversion-of-control ioc-container


【解决方案1】:

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

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

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

所以,回答你的问题:

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

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

准备好生产了吗?

很难说。它目前的状态是 alpha,它错过了其他 IOC 框架具有的许多功能。 开发人员刚刚(参见下面的 cmets)发布了一个稳定版本。这意味着根据开发人员的说法,它已准备好投入生产。

它的主要优点是什么 以及与其他 IOC 容器相比的劣势?

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

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

  1. 您需要运行 4 个程序集,而不是大多数 IOC 框架的 1 或 2 个。
  2. 当递归依赖被解决时,它会引发堆栈溢出(顺便说一句,大多数框架都会这样做)。所以没有循环依赖检测。
  3. 似乎不支持除了瞬态之外的任何生活方式(作者确实说过will,但我目前认为没有支持)。这实际上对性能不利,因为大多数服务通常会注册为单例。据作者说,它支持多种生活方式。
  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();
    }
}

【讨论】:

  • @Steven,非常有趣的分析...我会假设 Hiro 的代码生成会更像 new A(new B(new C()), new D())... 似乎就像错过的机会。再说一次,许多容器,例如Windsor 确实会发出几乎等效的 IL。
  • 很好的分析,但你错过了几点,Steven:1) Hiro 使用 ILMerge,因此发布版本只有一个程序集,2) 如果存在 is 循环 DI您使依赖项变得懒惰并在运行时解决它,3) Hiro 确实支持其他生活方式(包括 Singleton、Transient 和 Once-per-web session),以及 4&5) 是的,你让我到了那里——我故意离开了 Open Generics从 Hiro 出来,因为那是在 LinFu 中,并且 6) 它有很多 XML 文档,使用非常简单。所见即所得:)
  • @Steve:来,给个赞。看来我有一些工作要做。 :)
  • 已修复:我修改了 Hiro 以使用 OpCodes.Switch 指令在 O(1) 时间内解析服务:is.gd/62qV7T --再次,Steve,感谢您的反馈!
  • @Steve:作为记录,我确实测试了我的软件,但如果您在生产版本完全不同的情况下尝试针对 Hiro 的 alpha 版本使用基准测试,我无法帮助您. IContainerCompiler 类在生产版本中是公共的,因此显然存在版本控制问题,而不是测试问题。
猜你喜欢
  • 2023-03-20
  • 2010-09-18
  • 1970-01-01
  • 2015-02-15
  • 2010-10-20
  • 1970-01-01
  • 2014-04-29
  • 2011-01-02
  • 1970-01-01
相关资源
最近更新 更多