Наскільки ефективні системи кешування ефективні?


32

Останнім часом я багато читав на сутнісних системах, щоб впровадити в свій ігровий движок C ++ / OpenGL. Дві ключові переваги, які я постійно чую з похвалою щодо сутнісних систем, є

  1. легке побудова нових видів сутностей, що не пов'язане зі складними ієрархіями спадкування, та
  2. ефективність кешу, яку я маю проблеми з розумінням.

Теорія, звичайно, проста; кожен компонент постійно зберігається в блоці пам'яті, тому система, яка піклується про цей компонент, може просто перебирати весь список, не потребуючи стрибків у пам'яті та вбивання кешу. Проблема полягає в тому, що я не можу реально придумати ситуацію, коли це насправді практично.


Спочатку розглянемо, як зберігаються компоненти та як вони посилаються один на одного. Системи повинні бути здатні працювати з більш ніж одним компонентом, тобто і система візуалізації, і фізика повинні мати доступ до компонента перетворення. Я бачив низку можливих реалізацій, які вирішують це питання, і жодна з них не робить це добре.

Ви можете мати вказівники для зберігання компонентів на інші компоненти або покажчики на об'єкти, які зберігають вказівники на компоненти. Однак, як тільки ви кидаєте покажчики в суміш, ви вже вбиваєте ефективність кешу. Ви можете гарантувати, що кожен компонентний масив є «n» великим, де «n» - кількість сутностей, що живуть у системі, але такий підхід жахливо витрачає пам’ять; це ускладнює додавання нових типів компонентів до двигуна, але все-таки викидає ефективність кешу, оскільки ви переходите з одного масиву в інший. Ви можете переплутати ваш масив об'єктів, замість того, щоб зберігати окремі масиви, але ви все ще витрачаєте пам'ять; додавання нових компонентів або систем стає надмірно дорогим, але тепер з додатковою перевагою вимкнення всіх старих рівнів та збереження файлів.

Це все припускаючи, що об'єкти обробляються лінійно в списку, кожному кадрі або галочці. Насправді це не так часто. Скажімо, ви використовуєте рендерінг сектору / порталу або октат, щоб здійснити відключення оклюзії. Можливо, ви зможете постійно зберігати об’єкти в секторі / вузлі, але ви будете стрибати навколо, сподобалось вам це чи ні. Тоді у вас є інші системи, які можуть віддати перевагу об'єктам, що зберігаються в іншому порядку. II може бути гаразд із зберіганням об'єктів у великому списку, поки ви не почнете працювати з AI LOD; тоді ви захочете розділити цей список відповідно до відстані до гравця чи іншої метрики LOD. Фізика захоче скористатися цією октрею. Сценарії байдуже, їх потрібно запускати, незважаючи ні на що.

Я міг бачити розділення компонентів між "логікою" (наприклад, ai, скриптами тощо) та "світом" (наприклад, візуалізація, фізика, аудіо та ін.) Та керувати кожним списком окремо, але ці списки все одно повинні взаємодіяти один з одним. AI є безглуздим, якщо він не може вплинути на стан перетворення або анімації, що використовується для надання суб'єкта.


Як системи сутності "ефективні в кеші" в ігровому двигуні реального світу? Можливо, є гібридний підхід, яким користуються всі, але не говорять про них, як зберігання сутностей у масиві глобально та посилання на нього в межах октриси?


Зауважте, що у вас є багатоядерний процесор і кеш-пам'ять, більший за один рядок. Навіть якщо вам потрібна інформація про доступ з двох систем, вони, ймовірно, вмістяться в обох. Також зауважте, графічне відображення часто відокремлюється - саме для того, що ви заявили (дерева, сцени, ..)
wondra

2
Системи entity не завжди ефективні з кешу, але це може бути перевагою деяких реалізацій (над іншими способами досягнення подібних речей).
Джош

Відповіді:


43

Дві основні переваги, які я постійно чую з похвалою щодо сутнісних систем, - це 1) легка побудова нових типів сутностей через те, що не потрібно заплутуватися зі складною ієрархією успадкування, і 2) ефективність кешу.

Зауважте, що (1) є перевагою дизайну на основі компонентів , а не лише ES / ECS. Ви можете використовувати компоненти багатьма способами, які не мають частини "системи", і вони працюють чудово (і в багатьох архівах як Інді, так і AAA використовуються такі архітектури).

Стандартна об'єктна модель Unity (з використанням GameObjectта MonoBehaviourоб'єктами) не є ECS, а є компонентною конструкцією. Більш нова функція Unity ECS - це, звичайно, фактична ECS.

Системи повинні мати можливість працювати з більш ніж одним компонентом, тобто і система візуалізації, і фізика повинні мати доступ до компонента перетворення.

Деякі ECS сортують контейнери для компонентів за ідентифікаційним кодом Entity, що означає, що відповідні компоненти в кожній групі будуть в одному порядку.

Це означає, що якщо ви лінійно повторюєте графічний компонент, ви також лінійно повторюєте відповідні компоненти перетворення. Можливо, ви пропускаєте деякі з перетворень (оскільки у вас можуть бути обсяги фізичних тригерів, які ви не рендеруєте чи подібні), але оскільки ви завжди проскакуєте вперед у пам'яті (і, як правило, не на величезних відстанях), ви все одно рухаєтесь мати підвищення ефективності.

Це аналогічно тому, як у вас є рекомендований підхід для HPC «Структура масивів» (SOA). Процесор і кеш можуть працювати з декількома лінійними масивами майже так само, як і з одним лінійним масивом, і набагато краще, ніж вони можуть мати доступ до випадкової пам'яті.

Ще одна стратегія, що використовується в деяких впровадженнях ECS - включаючи Unity ECS - полягає у розподілі компонентів на основі архетипу відповідної сутності. Тобто, все Сутності з точно набором компонентів ( PhysicsBody, Transform) будуть виділені окремо від осіб з різними компонентами (наприклад PhysicsBody, Transform, і Renderable ).

Системи в таких проектах працюють, спочатку знаходячи всі архетипи, які відповідають їхнім вимогам (які мають необхідний набір компонентів), ітерацію цього списку архетипів та ітерацію компонентів, що зберігаються в кожному відповідному архетипі. Це дозволяє отримати повністю лінійний та справжній доступ до компонентів O (1) в межах архетипу і дозволяє системам знаходити сумісні об'єкти з дуже низькими накладними витратами (шляхом пошуку невеликого списку архетипів, а не пошуку потенційно сотень тисяч сутностей).

Ви можете мати вказівники для зберігання компонентів на інші компоненти або покажчики на об'єкти, які зберігають вказівники на компоненти.

Компоненти, що посилаються на інші компоненти того ж об'єкта, нічого не потрібно зберігати. Для посилання на компоненти на інші об'єкти просто збережіть ідентифікатор особи.

Якщо компоненту дозволено існувати більше одного разу для однієї сутності, і вам потрібно посилатися на певний екземпляр, зберігайте ідентифікатор іншої сутності та індекс компонента для цієї сутності. Багато реалізацій ECS не допускають цього випадку, однак саме тому, що робить ці операції менш ефективними.

Ви можете переконатися, що кожен компонентний масив 'n' великий, де 'n' - кількість об'єктів, що живуть у системі

Використовуйте ручки (наприклад, індекси + маркери генерації), а не покажчики, і тоді ви можете змінювати розмір масивів, не боячись порушити посилання на об'єкт.

Ви також можете скористатися підходом "нарізаний масив" (масив масивів), подібним до багатьох поширених std::dequeреалізацій (хоча без жалюгідного невеликого розміру згаданих реалізацій), якщо ви хочете дозволити покажчики з якихось причин або якщо ви виміряли проблеми з змінити розмір продуктивності масиву.

По-друге, це все припускаючи, що сутності обробляються лінійно в списку кожен кадр / галочку, але насправді це не часто так

Це залежить від сутності. Так, для багатьох випадків використання це неправда. Дійсно, саме тому я так сильно наголошую на різниці між компонентним дизайном (хорошим) та сутнісною системою (конкретна форма КБР).

Деякі ваші компоненти, безумовно, будуть легко обробляти лінійно. Навіть у звичайно «важких для дерева» випадках використання ми, безумовно, спостерігали підвищення продуктивності від використання щільно упакованих масивів (переважно у випадках, в яких N максимум декілька сотень, як агенти AI у типовій грі).

Деякі розробники також встановили, що переваги продуктивності використання лінійно-розподілених структур даних, орієнтованих на дані, переважають за перевагою продуктивності використання "розумніших" деревних структур. Все, звичайно, залежить від гри та конкретних випадків використання.

Скажімо, ви використовуєте рендерінг сектору / порталу або октат для відключення оклюзії. Можливо, ви зможете постійно зберігати об'єкти в секторі / вузлі, але ви будете стрибати, подобається вам це чи ні.

Ви здивуєтеся, наскільки масив все ще допомагає. Ви стрибаєте в набагато меншій області пам’яті, ніж «куди завгодно», і навіть при всіх стрибках ви все ще набагато частіше опинитесь у чомусь у кеші. З деревом певного розміру чи меншим, ви навіть зможете заздалегідь встановити всю річ у кеш і ніколи не матимете пропуску кешу на цьому дереві.

Є також дерева структури, які побудовані для проживання в щільно укупованих масивах. Наприклад, у своєму октрисі ви можете використовувати структуру, що нагадує купу (батьки перед дітьми, брати і сестри поруч один з одним) і гарантувати, що навіть коли ви "вибиваєте" дерево, ви завжди ітератуєте вперед у масиві, що допомагає процесор оптимізує доступ до пам'яті / пошук кешу.

Що важливо зробити. Процесор x86 - це складний звір. Процесор ефективно працює з оптимізатором мікрокоду на вашому машинному коді, розбиваючи його на менший мікрокод і впорядковуючи інструкції, прогнозуючи схеми доступу до пам’яті і т.д. як працюють процесор чи кеш-пам'ять.

Тоді у вас є інші системи, які можуть віддати перевагу об'єктам, що зберігаються в іншому порядку.

Ви можете зберігати їх кілька разів. Після того, як ви знімете масиви до мінімальних деталей, ви можете виявити, що ви фактично економите пам’ять (оскільки ви видалили свої 64-бітні покажчики і можете використовувати менші індекси) при такому підході.

Ви можете переплутати ваш масив об'єктів замість того, щоб зберігати окремі масиви, але ви все одно витрачаєте пам’ять

Це протилежно до хорошого використання кешу. Якщо все, що вас цікавить, - це перетворення та графічні дані, навіщо змушувати машину витрачати час на те, щоб перетягувати всі ті інші дані для фізики та AI, а також введення та налагодження тощо?

Це звичайно на користь системи ECS проти монолітних ігрових об'єктів (хоча це не реально застосовується при порівнянні з іншими архітектурами, що базуються на компонентах).

Для чого це варто, більшість "виробничих" ECS-реалізацій, про які я знаю, використовують переплетене сховище. Популярний підхід, який я згадував раніше (наприклад, використовується в Unity ECS), дуже чітко побудований для використання переплетеного сховища для компонентів, пов'язаних з архетипом.

AI є безглуздим, якщо він не може вплинути на стан перетворення або анімації, що використовується для надання суб'єкта.

Тільки тому, що AI не може ефективно отримати доступ до даних перетворення лінійно, не означає, що жодна інша система не може ефективно використовувати цю оптимізацію макета даних. Ви можете використовувати запакований масив для трансформації даних, не зупиняючи ігрових логічних систем від того, щоб робити те, що спеціальні логічні ігри ігрових систем зазвичай роблять.

Ви також забули кеш-код . Коли ви використовуєте системний підхід ECS (на відміну від більш наївної архітектури компонентів), ви гарантуєте, що ви використовуєте той самий маленький цикл коду і не переходите назад і назад через віртуальні таблиці функцій до асортименту випадкових Updateфункцій, розповсюджених по всьому ваш бінарний. Отже, у випадку AI, ви дійсно хочете зберегти всі ваші різні компоненти AI (адже, безумовно, у вас є більше одного, щоб ви могли складати поведінку!) В окремих відрах і обробляти кожен список окремо, щоб отримати найкраще використання кеш-коду.

Із затримкою черги подій (де система генерує список подій, але не відправляє їх, поки система не закінчить обробку всіх об'єктів), ви можете переконатися, що кеш коду добре використовується, зберігаючи події.

Використовуючи підхід, коли кожна система знає, які черги подій читати за кадр, ви навіть можете зробити події читання швидкими. Або швидше, ніж без, принаймні.

Пам'ятайте, продуктивність не є абсолютною. Вам не потрібно усунути кожен останній пропущений кеш, щоб почати бачити продуктивність хорошого дизайну, орієнтованого на дані.

Наразі триває активне дослідження щодо того, щоб багато ігрових систем краще працювали з архітектурою ECS та моделями дизайну, орієнтованого на дані. Подібно до деяких дивовижних речей, які ми спостерігали за допомогою SIMD в останні роки (наприклад, JSON-аналізатори), ми спостерігаємо все більше і більше речей, зроблених з архітектурою ECS, яка не здається інтуїтивно зрозумілою для класичних ігрових архітектур, але пропонує ряд переваги (швидкість, багатопотоковість, тестуваність тощо).

Або, можливо, є гібридний підхід, який використовують усі, але ніхто не говорить про це

Про це я виступав раніше, особливо для людей, які скептично ставляться до архітектури ECS: використовуйте хороші підходи, орієнтовані на дані, до компонентів, де продуктивність є критичною. Використовуйте більш просту архітектуру, де простота покращує час розробки. Не взувайте будь-який компонент у чітке завищене визначення компонентності, як пропонує ECS. Розробіть архітектуру компонентів таким чином, щоб ви могли легко використовувати підходи, схожі на ECS, де вони мають сенс і використовувати простішу структуру компонентів, де підхід, подібний ECS, не має сенсу (або має менший сенс, ніж структура дерева тощо) .

Я особисто відносно недавно перейшов на справжню силу ECS. Хоча для мене вирішальним фактором було щось, що рідко згадується про ECS: це робить тести для написання ігрових систем та логіки майже тривіальними порівняно з жорстко поєднаними логічними компонентами, що базуються на компонентах, з якими я працював у минулому. Оскільки архітектури ECS застосовують всю логіку в Системах, які просто споживають Компоненти та виробляють оновлення компонентів, створити "макетний" набір компонентів для перевірки поведінки системи досить просто; оскільки більшість логічних ігор повинні жити виключно в Системах, це фактично означає, що тестування всіх ваших систем забезпечить досить високе покриття коду вашої логіки гри. Системи можуть використовувати макетні залежності (наприклад, інтерфейси GPU) для тестів із значно меншою складністю або впливом на продуктивність, ніж ви '

Як осторонь, ви можете відзначити, що багато людей говорять про ECS, не розуміючи, що це таке. Я бачу класичний Unity, який називають ECS з пригнічувальною частотою, ілюструючи, що занадто багато розробників ігор порівнюють "ECS" з "Components" і майже повністю ігнорують частину "Entity System". Ви бачите, що в Інтернеті багато кохання накопичується, коли велика частина людей дійсно просто виступає за компонентний дизайн, а не власне ECS. На даний момент сперечатися це майже безглуздо; Система ECS була зіпсована з її початкового значення в загальний термін, і ви можете також прийняти, що "ECS" не означає те саме, що "ECS, орієнтоване на дані". : /


1
Було б корисно визначити (або посилання на), що ви маєте на увазі під системою ECS, якщо ви збираєтесь порівнювати / протиставляти це загальному компонентному дизайну. Мені одно не зрозуміло, в чому полягає відмінність. :)
Натан Рід

Велике спасибі за відповідь, схоже, у мене ще дуже багато досліджень щодо цього питання. Чи є книги, на які ви могли б вказати мені?
Гайдн В. Харач

3
@NathanReed: ECS задокументовано в таких місцях, як ent-systems.wikidot.com/es-terminology . Компонентний дизайн - це просто регулярне об'єднання-надбанство, але з акцентом на динамічну композицію, корисну для ігрового дизайну. Ви можете писати двигуни на основі компонентів, які не використовують Системи або Субстанції (в значенні термінології ECS), і ви можете використовувати компоненти в ігровому двигуні набагато більше, ніж просто ігрові об'єкти / сутності, тому я наголошую на різниці.
Шон Міддлічч

2
Це одне з найкращих дописів про ECS, які я коли-небудь читав, незважаючи на всю літературу в Інтернеті. Мега великі пальці вгору. Отже, Шон, зрештою, який ваш загальний підхід до розробки ігор (досить складних)? Чистий ECS? Змішаний підхід між компонентами та ECS? Я хотів би дізнатися більше про ваші дизайни! Це занадто багато просити вас влаштуватись у Skype чи щось інше для обговорення цього?
Грімшо

2
@Grimshaw: gamedev.net - це гідне місце для більш відкритих дискусій, як це було б reddit.com/r/gamedev (я вважаю, що я сам не перетворювач). Я часто буваю на gamedev.net, як і багато інших яскравих людей. Я взагалі не веду розмов один на один; Я досить зайнятий і віддаю перевагу моєму простою (тобто складанню) витрачатись на допомогу багатьом, а не кільком. :)
Шон Міддлічч
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.