Hiro против других контейнеров IoC - PullRequest
12 голосов
/ 14 марта 2011

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

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

Это все еще самый быстрый контейнер IOC на сегодняшний день?Готов ли он к производству?Есть ли другие контейнеры, способные сделать IOC во время компиляции?Каковы его основные преимущества и недостатки перед другими контейнерами МОК?

Спасибо

1 Ответ

20 голосов
/ 15 марта 2011

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

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

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

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

Это все еще самый быстрый контейнер МОК сегодня

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

Готов ли он к производству?

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

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

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

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

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

Есть ли другие контейнеры, которые можно сделать МОК во время компиляции?

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