Производительность - основная цель моего ECS фреймворка и я попытался найти самый быстрый способ создания новых инстансов любого класса. Стандартный вариант через Activator.CreateInstance
работает как надо, но я слышал много раз, что это можно сделать быстрее. Давайте попробуем найти самый быстрый вариант.
Тестовое окружение
Мы будем запускать 100000 итераций вызовов события, повторять этот процесс 30 раз и затем брать среднее время. В каждом тесте будем измерять время создания нового инстанса и присваивание его переменной. О дополнительных особенностях тестирования кода для Unity
можно почитать в “Тесты производительности Event, ActionList, Observer, InterfaceObserver“, те же правила будет использоваться и в новых тестах. Т.к мы активно аллоцируем память в каждом тесте, мы должны вызывать сборку мусора перед следующим тестом.
Варианты для проверки:
Activator.CreateInstance (Type)
Создает инстанс и делает принудительный каст к целевому типу.Activator.CreateInstance
()
Создает инстанс вGeneric
-стиле без принудительного каста к целевому типу.new T () where T: new()
Создает инстанс вGeneric
-стиле с использованием ограниченияnew()
для возможности вызова конструктора по умолчанию.ConstructorInfo.Invoke
Создает инстанс путем прямого вызова конструктора черезReflection
и дальнейшего каста к целевому типу.CustomActivator
Создает инстанс путем вызова lambda-метода, внутри которого вызывается создание целевого типа.
Тестовый код
1 | class InstanceCreationPerformance : MonoBehaviour { |
Результаты теста
1 | // unity 2017.3.0f3, fw3.5 profile, 100k iterations, average of 30 tests. |
Оба варианта Activator.CreateInstance
очень близки по скорости. Generic-вариант немного медленнее, но не стоит забывать, что это синтетический тест на 100000 итераций без дополнительной обработки.
New T()
-вариант равен по скорости Activator.CreateInstance<T>
, т.к основывается на нем.
Activator.CreateInstance(typeof(T))
- данная комбинация дает скорость Activator.CreateInstance
и защиту типов от Generic-варианта. Хорошая замена вместо использования Activator.CreateInstance<T> ()
и new T()
.
Invoke(ConstructorInfo)
-вариант показывает очень хорошие результаты для вызовов, использующих Reflection
. Хорошая замена для всех перечисленных выше вариантов.
Чистая победа - у прямого создания инстанса нужного типа через lambda-метод, 20-кратное ускорение относительно Activator.CreateInstance
! Да, не стоит забывать, что это синтетический тест, но даже на реальном проекте этот подход дает 2-кратное ускорение при создании новых инстансов компонентов.