Доброго времени, уважаемые читатели!
Для смартфона оптимальность архитектуры приложений особенно важна, поэтому я решил провести сравнение по производительности бесплатных и открытых библиотек контейнеров, доступных для Windows Phone.
Удостоились внимания следующие библиотеки (самые последние версии на момент тестирования):
Autofac 2.5.2
Caliburn.Micro v1.3.1 (компонент SimpleContainer)
Funq 1.0
MicroIoc 1.0
Ninject 2.2.1
TinyIoC
Примечательно, что TinyIoC представляет из себя один файлик исходного кода на языке C# (VB на подходе). Его последняя версия требует внесения небольшого фикса для WP7, иначе будет выдавать ошибку компиляции
'System.Type' does not contain a definition for 'FindInterfaces' and no extension method 'FindInterfaces' accepting a first argument of type 'System.Type' could be found (are you missing a using directive or an assembly reference?)
Решается простой заменой строчки
if (!registerImplementation.FindInterfaces((t, o) => t.Name == registerType.Name, null).Any())
на строчку
if (registerImplementation.GetInterface(registerType.Name, false) == null)
Все тесты запускались на реальном устройстве HTC Titan под управлением Windows Phone 7.5 в release-конфигурации без подключенного дебаггера и с полным зарядом батареи.
Было объявлено три интерфейса и их соответствующие реализации. Они регистрировались в испытуемом контейнере: первый – как singleton (всегда один экземпляр), второй и третий – как transient (создается новый экземпляр при каждом обращении к контейнеру). Первые две реализации имеют конструкторы без параметров, третья – конструктор с инжекцией первых двух интерфейсов.
Каждый из интерфейсов запрашивался у контейнера 1 миллион раз. Время измерялось с помощью Stopwatch в миллисекундах.
Результаты
При тестировании Ninject оказалось, что время его отклика зависит экспоненциально от количества обращений к нему. Около 10 тысяч разрешений transient-объекта он делает сносно, а на полумиллионе практически умирает, просто подвесив приложение. Поэтому мы вынуждены были сократить число итераций в тесте для него до десяти тысяч и пересчитать полученный результат в расчете на один миллион для сравнения с другими библиотекам. Тем не менее, этот факт особенного поведения Ninject следует иметь ввиду при выборе контейнера.
Наиболее показательна последняя колонка, как самая часто встречающаяся на практике. В расчете на одно обращение к контейнеру Ninject показал наихудший результат – почти одна миллисекунда. Наилучший результат у Funq, что неудивительно, т.к. это почти тоже, что и вызов оператора new.
Несмотря на высокую скорость работы, у Funq есть недостаток: при регистрации все зависимости придется прописывать вручную, что крайне неудобно, т.к. придется переписывать код регистрации при обновлении конструктора объекта. Кстати, такую возможность регистрации предоставляет и Autofac, но она не исследовалась.
Конечно же, производительность – не единственный критерий выбора того или иного контейнера для вашего проекта, поэтому вот еще пара табличек с характеристиками исследуемых библиотек.
Выводы
Если Вам нужная широкая функциональность, то выбор однозначен – это Autofac. Вы получите хорошую производительность, но цена этому – сравнительно большой размер файла библиотеки.
Если же Вас устроит наличие простых методов регистрации объектов и получения их из контейнера, то стоит обратить внимание на TinyIoC. Это небольшой и быстрый контейнер со стандартным функционалом.
SimpleContainer из набора Caliburn.Micro также хорошо себя показал. Но использовать его только ради контейнера не стоит, придется заплатить за это размером дистрибутива. Однако же, если планируется использовать и другие компоненты библиотеки, то выбор очевиден.
Автор: astudent