Иногда нам нужно вызвать несколько методов как реакцию на событие - что мы должны использовать в этом случае, чтобы уменьшить потери в производительности? Ответ не совсем очевиден.
Тестовое окружение
Мы будем запускать 10000 итераций вызовов события, повторять этот процесс 10 раз и затем брать среднее время. Т.к мы это будем делать в Unity
, то мы должны знать следующее:
- Мы не можем использовать
Time.realtimeSinceStartup
для изменений - точность таких данных будет очень низкой. Вместо этого мы будем использовать стандартный классSystem.Diagnostics.Stopwatch
. - Мы не можем начинать измерение прямо на старте -
Unity
требуется какое-то время для полной инициализации (подгрузки кода, графических ресурсов и т.п), мы должны выждать какое-то время, пока аппаратные ресурсы не освободятся. 3 секунд будет достаточно для этого теста. - Мы не можем доверять измерениям внутри редактора
Unity
, т.к код в этом случае будет работать вDEBUG
режиме. Для более корректного измерения нам необходимRELEASE
режим - мы его можем получить, собрав билд приложения и запуская его вне редактора. Для фиксирования замеров будем использовать стандартныйDebug.Log
метод и смотреть сформировавшиеся логи.
Event
Первый вариант - встроенный в C# event
механизм обработки можественной подписки на события:
1 | class EventTest : MonoBehaviour { |
ActionList
Второй вариант - сохранение подписчиков в List<System.Action>
с ручным вызовом в цикле:
1 | class ActionListTest : MonoBehaviour { |
Observer
Третий вариант - использование паттерна Observer
: сохранение инстансов классов с нужным методом вместо сохранения ссылки на сам метод:
1 | class ObserverTest : MonoBehaviour { |
InterfaceObserver
Четвертый вариант - как третий, но с использованием интерфейса:
1 | class InterfaceObserverTest : MonoBehaviour { |
Результаты
1 | // unity 2017.3.0f3, fw3.5 profile, 10k iterations, average of 10 tests. |
Очень странные результаты на iPhone6s / iPadPro13”: Observer
-вариант на первом устройствезначительно быстрее, чем InterfaceObserver
-вариант, на втором же - наоборот.
Остальные результаты достаточно стабильны на всех устройствах: ActionList
-вариант быстрее или сравним с другими вариантами.
Event
-вариант самый медленный во всех тестах, но поддерживает очень важный механизм - мы можем модифицировать коллекцию подписчиков прямо внутри обработки события без получения ошибок, все остальные варианты требуют дополнительной работы над изоляцией итераторов (или копированием данных во временный буфер на момент вызова).