Pull to refresh

Comments 7

Было бы интересно посмотреть бенчмарки "плохого случая" - когда есть скажем 10к энтитей, у 5000 есть компонент А, у 5000 есть компонент B, но только у 100 есть и компонент и А и В. Насколько быстрой в этом случае будет итерация по сущностям содержащим и А и В. entt в этом случае предлагает связанные группы, другие неархетипные ecs на этом случае ломаются или вводят фильтры содержащие список сущностей.

Привет! Добавлю такой кейс и поисследую возможные оптимизации

у меня итерация идёт по первому компоненту из списка в for, и в таком кейсе будет 5000 итераций

Второй компонент может быть nullptr, это можно проверить простым ифом

у меня итерация идёт по первому компоненту из списка в for, и в таком кейсе будет 5000 итераций

Стандартная оптимизация - итерироваться по тому из компонентов, которых меньше. Тогда если в мире будет 5000 компонентов А и 200 компонентов B, то придется делать всего 200 итераций.
Но для "плохого случая" она не поможет, да.

std::map<Transform> t;

главное чтобы вы понимали, что в таком виде оно не очень ecs. std::map представляет собой бинарное дерево, то есть ноды дерева расположены где-то в случайном месте памяти. ECS же старается сделать так чтобы одинаковые компоненты лежали как можно ближе друг дргу для cache locality. В классическом SoA оно выглядело бы как-то так

Transform t[];
PhysicsComp p[];
IsRenderable r[];
Mesh m[];

То есть некоторые линейные массивы, последовательная обработка которых отлично предсказывалась процессорным branch predictor. В случае map такого не будет происходить, т.к. обход бинарного дерева не будет проходить по последовательной памяти, а по веткам дерева. std::unordered_map или std::flat_map могут исправить эту ситуацию.

Ну и отдельно стоит заметить, что любой std::*map обычно принимает два шаблонных параметра - тип ключа и тип собственно элемента, так что ваш пример не соберётся.

Ну и обновление в ECS обычно происходит на уровне систем, а не на уровне энтитей.

for (auto [_,tran]: t) tran.tick(); // update transform system
for (auto [_,phys]: p) phys.tick(); // update physics system
/*the rest of systems*/

Привет!

Абсолютно верное уточнение что map это не array, сразу после блока с кодом я об этом пишу :)

Этот пример был призван проиллюстрировать реальный способ поиска компонента по его id, но естественно это не оптимальный подход.

Это cpp подобный псевдокод, он не должен собираться, он лишь показывает концепцию.

В случае с ecs нам часто нужно несколько компонентов сразу в одной системе, системой может являться обычная функция, для наглядности псевдокод обновляет данные через системы передавая их в них напрямую.

В следующий раз добавлю пометку "псевдокод", спасибо за фидбек!

А что есть почитать по теме, как делать правильно?

Я бы не брался говорить что есть прям правильный и неправильный путь

Есть оптимальные и нет.

Главное, на мой взгляд, в ecs- это организовать быструю итерацию по компонентам и быстрый доступ к данным.

Система это просто функция которая обновляет данные в компонентах. Сущность - просто хэндл, вокруг которого компоненты объединены.

Дальше начинаются варианты реализации хранения: массивы, архетипы, соа/аос, свои контейнеры и т.п. А то как организованы системы у каждого может быть своё виденье в зависимости от решаемых задач.

Можно почитать документацию по entt или flecs, например, что бы получить какое то представление. У моей ecss тоже есть документация, ее можно найти в гитхабе. Это даст представление.

Sign up to leave a comment.

Articles