Тесты производительности создания новых экземпляров классов
Размещено
Для ECS фреймворка я попытался найти самый быстрый способ создания новых экземпляров любого класса. Стандартный вариант через Activator.CreateInstance работает как надо, но почему бы не попробовать другие способы и не выбрать самый быстрый вариант.
Тестовое окружение
Для тестов прогоним 10000 итераций вызовов события, повторять этот процесс 30 раз и затем возьмем средний результат. В каждом тесте будем измерять время создания нового экземпляра и присваивание его переменной. О дополнительных особенностях тестирования кода для Unity можно почитать в “Тесты производительности Event, ActionList, Observer, InterfaceObserver“, те же правила будет использоваться и в новых тестах. Т.к мы активно аллоцируем память в каждом тесте, мы должны вызывать сборку мусора перед следующим тестом.
Варианты для проверки:
Activator.CreateInstance (Type) Создает экземпляр и делает принудительный каст к целевому типу.
Activator.CreateInstance() Создает экземпляр в Generic-стиле без принудительного приведения к целевому типу.
new T () where T: new() Создает экземпляр в Generic-стиле с использованием ограничения new() для возможности вызова конструктора по умолчанию.
ConstructorInfo.Invoke Создает экземпляр путем прямого вызова конструктора через рефлексию и дальнейшего приведения к целевому типу.
CustomActivator Создает экземпляр путем вызова lambda-метода, внутри которого вызывается создание целевого типа.
public T CreateInstance () { if (_factory != null) { return _factory (); } else { returnnew T (); } } } }
Результаты теста
1 2 3 4 5 6 7 8
// unity 2017.3.0f3, fw3.5 profile, 100k iterations, average of 30 tests. // standalone macos 10.13.2, mono. Activator.CreateInstance(Type): 61.93333 Activator.CreateInstance<T> (): 65.23333 new T(): 65.2 Activator.CreateInstance(typeof(T)): 63.26667 Invoke(ConstructorInfo): 44.03333 CustomActivator: 3
Оба варианта Activator.CreateInstance очень близки по скорости. Generic-вариант немного медленнее, но не стоит забывать, что это синтетический тест на 100000 итераций без дополнительной обработки.
New T()-вариант равен по скорости Activator.CreateInstance<T>, т.к основывается на нем.
Activator.CreateInstance(typeof(T)) дает комбинацию скорости Activator.CreateInstance и защиту типов от Generic-варианта. Хорошая замена вместо использования Activator.CreateInstance<T> () и new T().
Invoke(ConstructorInfo)-вариант показывает очень хорошие результаты для вызовов, использующих рефлексию. Хорошая замена для всех перечисленных выше вариантов.
Чистая победа - у прямого создания экземпляра нужного типа через lambda-метод, 20-кратное ускорение относительно Activator.CreateInstance! Да, не стоит забывать, что это синтетический тест, в случае ECS фреймворка этот подход дал 2-кратное ускорение при создании новых экземпляров компонентов.