Hiro против других контейнеров IoC

В в этой статье (11 апреля 2009 г.) автор утверждает Hiro:

"Всемирный самый быстрый контейнер IOC... статически предварительно скомпилированный контейнер IOC, который работает так же быстро, как приложение без контейнера IOC".

Является ли это еще самым быстрым контейнером IOC сегодня? Готово ли оно к производству? Существуют ли какие-либо другие контейнеры для МОК во время компиляции? Каковы его основные преимущества и недостатки по сравнению с другими контейнерами МОК?

Спасибо

Ответ 1

Хиро утверждает, что это самый быстрый контейнер. Это утверждение основано на тесте, заданном автором (см. здесь для объективного сравнения между многими контейнерами). Независимо от того, реалистичен ли этот эталон, зависит от размера вашего приложения. Тесты, похоже, продуманно настроены с очень небольшим набором зарегистрированных типов. При добавлении большего количества регистраций к эталону производительность начинает снижаться (см. Ниже примерный пример). При внимательном рассмотрении мы видим, что Hiro обладает характеристикой производительности O (n), в то время как нормальные рамки DI имеют характеристика O (1), поэтому с другими структурами производительность остается постоянной с количеством зарегистрированных типов.

Что хорошего в Hiro, так это то, что он генерирует новую сборку "на лету", а разрешение нового типа состоит всего из одного вызова интерфейса. Это очень быстро. С другой стороны, наиболее распространенные рамки DI всегда будут делать поиск по словарю во время вызова GetInstance. Метод Hiros GetInstance в основном представляет собой большой оператор case switch, реализованный как куча if-утверждений. Операторы case-case, основанные на базе данных, чрезвычайно быстро растут до точки. Компилятор С# использует эвристику (по моему мнению) 18 аргументов в качестве поворотного момента. Ниже этого числа компилятор генерирует кучу if-statement. Над этим номером он создает и хранит статический словарь и выполняет поиск по словарю, потому что более 18 поисковых запросов выполняют поиск словаря, если просто быстрее.

Угадайте, что; Hiro не использует оптимизацию, как это делает компилятор С#. Именно поэтому производительность продолжает падать, поскольку в контейнер добавляются все больше и больше типов. Линейная характеристика производительности или O (n) для короткого замыкания подходит для небольших наборов данных, но любое хорошо написанное и обычно используемое приложение, зависимое от зависимостей, имеет множество регистраций/сопоставлений типов. И в этом случае производительность Hiro быстро снижается.

Итак, чтобы ответить на ваши вопросы:

Является ли это еще самым быстрым контейнером IOC сегодня?

Для очень маленьких приложений это самый быстрый. Для любых приложений нормального размера он никогда не был самым быстрым.

Готово ли оно для производства?

Трудно сказать. Его текущий статус - альфа, и он пропускает множество функций, которые имеют другие структуры IOC. Разработчик просто (см. Комментарии ниже) опубликовал стабильный выпуск. Это означает, что, по словам разработчика, он готов к производству.

Каковы его основные преимущества и недостатки по сравнению с другими контейнерами МОК?

Посмотрите, например, на в этой статьеfollow up), который дает хорошее (функциональное) сравнение структур МОК. Писатель статьи имеет видение того, что он считает важным. Вы должны составить такой список для себя. В вашем списке высокая производительность. Однако учтите, что есть другие способы повышения производительности. Например, регистрируя типы как одиночные, чтобы предотвратить создание многих типов.

Ниже приведено небольшое сравнение с другими контейнерами. Обратите внимание, что я не писал Hiro, поэтому я мог бы пропускать вещи, тем более, что, похоже, нет никакой документации, но здесь говорится:

  • Для большинства рамок IOC вам нужно 4 сборки, вместо 1 или 2.
  • Он выдает переполнение стека, когда рекурсивная зависимость разрешена (большинство фреймворков делает это. Таким образом, не существует обнаружения циклической зависимости.
  • Кажется, что он не поддерживает какой-либо образ жизни, кроме переходного (автор говорит, что будет, но в настоящее время я не вижу поддержки для этого). Это действительно плохо для производительности, потому что большинство сервисов обычно регистрируются как одиночные. По словам автора, он поддерживает множественные образы жизни.
  • Не поддерживает разрешение открытых родовых типов.
  • Не поддерживает разрешение незарегистрированного типа.
  • У меня нет документации, подтверждающей документацию XML (intellisense).

Существуют ли другие контейнеры IOC во время компиляции?

Определите время компиляции. Hiro генерирует новую сборку "на лету" один раз во время выполнения. Другие (например, Autofac, Windsor и Простой инжектор) испускают 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();
    }
}

Ответ 2

Я использовал следующую инфраструктуру IOC Container для своих проектов.

https://marcusts.com/2018/12/26/smart-di-container/

Это чрезвычайно мощный, гибкий, абсолютно бесплатный, стабильный и простой в использовании. Вы можете создавать DI-контейнеры там, где они вам нужны, не беспокоясь о централизации. Это нестатический контейнер, поэтому может храниться как в вложенном, так и в частном порядке. Есть много полезных расширений, которые сделают вашу жизнь проще. Вы даже можете зарегистрироваться и решить одновременно.

PS. Я не связан с компанией, которая написала это. Я просто пользователь.