Увеличение производительности реактивных систем в Entity Component System

Реактивные системы в ECS могут быть достаточно полезны для обработки определенных реакций на изменения компонентов. Но как мы можем быть уверен, что отфильтрованные сущности будут обработаны только один раз и без увеличения накладных расходов на эти проверки?

Стандартное решение при добавлении новых сущностей в данные реактивной системы - проверка, что сущность уже существует в коллекции. Текущий тип этой коллекции - List<int>, это означает, что мы должны написать проверки примерно так:

1
2
3
4
5
6
void AddNewEntity (int entity) {
if (_entities.IndexOf(entity) == -1) {
// new entity, we can add it
_entities.Add(entity);
}
}

Работает как надо, но очень медленно на нескольких тысячах сущностей. Что насчет использования коллекций типа HashSet или Dictionary?

1
2
3
4
5
6
7
8
// HashSet<int>
void AddNewEntity (int entity) {
if (!_entityHashes.Contains(entity)) {
// new entity, we can add it
_entityHashes.Add(entity);
_entities.Add(entity);
}
}
1
2
3
4
5
6
7
8
// Dictionary<int, bool>
void AddNewEntity (int entity) {
if (!_entityHashes.ContainsKey(entity)) {
// new entity, we can add it
_entityHashes[entity] = true;
_entities.Add(entity);
}
}

Теперь код работает значительно быстрее, но потребляет больше памяти (мы не можем убрать старую List<int>-коллекцию, она будет использована позже). Странно, но Dictionary работает быстрее, чем HashSet, возможно это особенности реализации в Mono.
Итак, как мы можем еще ускорить наш код? Мы можем написать кастомную коллекцию, основанную наDictionary, удалив все неиспользуемые части и упростив часть проверок (т.к нам нужен только один тип - int).

Конечная реализация кастомного HashSet аналога была создана для увеличения производительности в ECS фреймворке: она работает быстрее, чем штатные коллекции, потребляет меньше памяти и настроена на поддержку инлайнинга с FW4.6. Общая производительность UpdateComponent / React-систем с OnUpdate обработкой увеличилась на 15%, ECS фреймворк теперь обрабатывает отфильтрованные сущности с помощью реактивных систем с той же скоростью, что Entitas, но без кодогенерации.
Разумеется, нет смысла выдумывать подобные велосипеды в общем случае, но, к сожалению, в Unity до сих пор используется старая версия рантайма и специализированная коллекция в данном конкретном случае работает быстрее. Как только рантайм обновится до свежих версий .Net - велосипед можно будет выкинуть.

Оформить подписку можно здесь: