Увеличение производительности реактивных систем в Entity Component System
Реактивные системы в ECS могут быть достаточно полезны для обработки определенных реакций на изменения компонентов. Но как мы можем быть уверен, что отфильтрованные сущности будут обработаны только один раз и без увеличения накладных расходов на эти проверки?
Стандартное решение при добавлении новых сущностей в данные реактивной системы - проверка, что сущность уже существует в коллекции. Текущий тип этой коллекции - List<int>
, это означает, что мы должны написать проверки примерно так:
1 | void AddNewEntity (int entity) { |
Работает как надо, но очень медленно на нескольких тысячах сущностей. Что насчет использования коллекций типа HashSet
или Dictionary
?
1 | // HashSet<int> |
1 | // Dictionary<int, bool> |
Теперь код работает значительно быстрее, но потребляет больше памяти (мы не можем убрать старую List<int>
-коллекцию, она будет использована позже). Странно, но Dictionary
работает быстрее, чем HashSet
, возможно это особенности реализации в Mono
.
Итак, как мы можем еще ускорить наш код? Мы можем написать кастомную коллекцию, основанную наDictionary
, удалив все неиспользуемые части и упростив часть проверок (т.к нам нужен только один тип - int
).
Конечная реализация кастомного HashSet
аналога была создана для увеличения производительности в ECS фреймворке: она работает быстрее, чем штатные коллекции, потребляет меньше памяти и настроена на поддержку инлайнинга с FW4.6
. Общая производительность UpdateComponent
/ React-систем с OnUpdate
обработкой увеличилась на 15%, ECS фреймворк теперь обрабатывает отфильтрованные сущности с помощью реактивных систем с той же скоростью, что Entitas
, но без кодогенерации.
Разумеется, нет смысла выдумывать подобные велосипеды в общем случае, но, к сожалению, в Unity
до сих пор используется старая версия рантайма и специализированная коллекция в данном конкретном случае работает быстрее. Как только рантайм обновится до свежих версий .Net - велосипед можно будет выкинуть.