LeoECS Proto - сортировка данных и оптимизация потребления памяти

ECS данные обычно рассматриваются как “база данных”, записи в которой не имеют четкого порядка. Но что если этот порядок необходим, например, для отрисовки пользовательского интерфейса?
ECS данных может быть очень много, но что если количество сущностей не превышает десятка тысяч, количество компонентов - нескольких тысяч, а надо запускаться в ограниченном по ресурсам окружении типа web-браузера?
Последние доработки LeoECS Proto были направлены на решение этих двух проблем.

Упорядочивание данных

Например, у нас есть список юнитов, у каждого из которых есть “время активации”, определяющее очередность их ходов. Для определения очередного активного юнита порядок сущностей в итераторе не важен, достаточно пробежаться по всем и найти минимальное время - это и будет нужная сущность. Но нам так же требуется отрисовать иконки юнитов на общей шкале времени и порядок отрисовки там уже будет обязательным. До последних изменений это можно было сделать путем вычитывания нужных данных в локальный кеш системы, его сортировки и отрисовки в нужном порядке. Теперь есть штатный механизм на базе кеширующих итераторов:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
struct Unit {
public float ActivationTime;
}

class UnitAspect: ProtoAspectInject {
public ProtoPool<Unit> Unit;
public ProtoItCached UnitItCached = new(It.List (new Unit ()));
}

class DrawUnitAvatar : IProtoRunSystem {
[DI] UnitAspect _units;

public void Run () {
// Собираем кеш сущностей, т.к сортировка возможна только в его пределах.
_units.UnitItCached.BeginCaching ();

// Запускаем сортировку отобранных сущностей.
_units.UnitItCached.Sort (SortUnits);

// Обрабатываем отсортированные данные.
foreach (var entity in _units.UnitItCached) {
ref var unit = ref _units.Unit.Get (entity);
// Обработка...
}

// Отпускаем кеш.
_units.UnitItCached.EndCaching ();
}

int SortUnits(ProtoEntity a, ProtoEntity b) {
// Сортируем сущности на основе времени активации, от меньшего к большему.
return _units.Unit.Get (a).ActivationTime - _units.Unit.Get (b).ActivationTime;
}
}

Для уменьшения аллокаций рекомендуется вместо лямбд использовать локальные функции или методы систем.

Оптимизация потребления памяти

Если количество сущностей в мире находится в пределах 64 тысяч (менее 65535, если быть точнее), то внутренняя инфраструктура ядра будет тратить часть оперативной памяти впустую. Теперь это можно оптимизировать через директиву препроцессора LEOECSPROTO_SMALL_WORLD:

  • Количество сущностей будет ограничено 65535 элементами.
  • Количество компонентных пулов останется прежним - 65536 элементов.

Сколько памяти выиграется? При грубом подсчете в случае полной нагрузки мира 64k-сущностями на каждом компоненте экономится порядка 240кБ. То есть на 400 компонентах это будет означать уменьшение потребления памяти на 96МБ, и это только на инфраструктуре, без пользовательских данных. В случае Web-игр это довольно существенная цифра.

Актуальные версии пакетов доступны в закрытом discord-сервере для cloudtips/boosty-подписчиков.
Вы можете оформить подписку:
Или просто сказать спасибо: