Что лучше работает в Unity? MonoBehaviour, Jobs или Entities?

Что лучше работает в Unity? MonoBehaviour, Jobs или Entities?

В начале 2023 года мы начали разработку новой версии фреймворка AppCore, позволяющий разрабатывать приложения без написания кода, используя лишь нодовый редактор. В новой версии нам предстояло добавить поддержку ECS в наш фреймворк. Еще тогда наш фреймворк был полностью зависим от Unity и нам предстояло разобраться, подойдет ли Entities для интеграции с AppCore или же нам предстоит использовать только Jobs для повышения скорости работы фреймворка.

В Unity существует несколько путей решения одной и той же задачи. Пару лет назад разработчики движка анонсировали D.O.T.S. – стек технологий, который призван ускорить работу игр на этом самом движке. Данный стек разрабатывается неравномерно и некоторые его части работают хорошо, а некоторые не очень хорошо.

Изначально у нас появилась потребность в том, чтобы изучить то, как работать с каждым из вариантов, найти их слабые и сильные стороны, а после сделать синтетический замер скорости и узнать, что же лучше.

Подготовка

Что лучше работает в Unity? MonoBehaviour, Jobs или Entities?

Сделать игру - это хорошо. Однако мы решили, что за основу мы возьмем самый простой и доступный алгоритм - Boids. Он имеет набор правил, которые можно было бы удобно раздробить в будущем, а также он хорошо подходит для того, чтобы показать, насколько сильно может проседать производительность при росте количества сущностей. Код во всех трех реализациях был максимально одинаковый, дабы сделать замеры “как есть”.

Производительность работы алгоритма будем измерять следующим образом: главная сцена загружает сцену с алгоритмом, передает количество сущностей и через 30 секунд выгружает сцену, замеряя количество отрисованных кадров, после чего снова загружает сцену и передает другое количество сущностей. Конечно, это может дать свои погрешности, но при длине замера в 30 секунд они будут незначительны.

Всего 5 замеров на одну реализацию: 100, 1000, 5000, 10000, 20000 сущностей. Каждая реализация была собрана по отдельности, а тесты производился по несколько раз и были взяты средние показатели.

Важное уточнение: biods - это больше синтетический тест. Если делать полноценный проект, то значения могут отличаться и иногда даже значительно.

Сцена

На сцене для замеров не было ничего, кроме скрипта для управления сценами, источника света с отключенными тенями и камеры, которая отрисовывала только сплошной цвет и сущности. В качестве рендера использовался URP, так как он является наиболее популярным и универсальным рендером на данный момент.

Алгоритм

Источник: https://github.com/hecomi/UnityECSBoidsSimulation
Источник: https://github.com/hecomi/UnityECSBoidsSimulation

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

Реализация алгоритма Boids сама по себе очень проста - это по сути просто 3 или более правила, которые применяются к каждой сущности. Самое “тяжелое” правило - выравнивание скорости относительно друг друга. В нем был использован цикл в цикле, что является чем-то ужасным в программировании, но так даже интересней, потому что программисты не всегда пишут красивый и аккуратный код. Конечно, если избавиться от цикла в цикле, то результаты станут чуть более линейными.

Реализация алгоритма на MonoBehaviour

С самого начала появления игрового движка Unity практически весь код был завязан на использовании MonoBehaviour. Мы как программисты просто пишем компоненты, которые что-то выполняют, а после добавляем эти компоненты на любой объект. В наше время такой подход можно сравнить с YOLO программированием, что особенно хорошо подходит играм, которые разрабатываются одним человеком за короткий срок. Разработка в таком случае становится критически быстрой. Однако огромное количество компонентов неизбежно приведет к падению производительности и придется искать более оптимальные варианты, как например использование одного компонента MonoBehaviour на весь проект. Но даже при такой реализации производительность может быть не особо большой. Однако в данной статье удобство при написании с использованием MonoBehaviour мы будем считать эталонным.

Реализация алгоритма с Jobs System

Логичным шагом для повышения производительности стало бы использование Jobs System. Главная его особенность - возможность использования потоков. При этом его можно использовать и вместе с компилятором Burst, который может добавить еще немного производительности. Однако такой подход заставляет больше думать и самостоятельно управлять не только потоками, но и памятью. Подобное программирование можно сравнить с программированием на языке без сборщика мусора. Но как только проходит привыкание к новым типам и требованиям к построению кода, то единственной проблемой остается лишь ужасный вывод ошибок, который сообщает о проблеме очень расплывчато.

Тем не менее взамен некоторым трудностям мы получаем действительно ощутимый прирост производительности. При том что для нашего алгоритма можно каждое правило выделить в отдельный “джоб”.

Реализация алгоритма с Entities

D.O.T.S. состоит не только из Jobs System и Burst, но так же и из собственной реализации ECS - Entities, которая построена на архетипах и в сочетании с остальным стеком обещает какую-то нереальную производительность. Но мы для тестов будем использовать лишь малую часть - Entities и Burst. Мы решили не использовать Jobs, так как итог будет очевидным - результат будет или на 5-10% лучше или наравне с реализацией, использующей только Jobs+Burst.

При использовании ECS мы получаем примерно такую же возможность распределить наши правила на несколько систем, что очень удобно. Но взамен мы должны сделать много сторонних действий, которые кажутся неоправданными.

Во время проведения тестов в релиз вышла версия Entities 1.0. Тогда Entities не имел актуальной документации, некоторые вещи имели другие названия (по сравнению с Entities 0.50). Создавать тест было очень сложно. Сейчас есть уже Entities 1.2, который, по слухам, имеет адекватную документацию и более комфортен в работе.

Помимо того, что пакеты нужно было ставить вручную (они не доступны в пакетном менеджере напрямую), уживаться с некоторыми ограничениями и особенностями, так еще и стабильность данной ecs могла варьироваться от версии к версии. Дебаггинг так же сложнее, чем при использовании Jobs. А если вы использовали для написания кода не Rider или Visual Studio, то вам придется выбирать между этими двумя вариантами, так как Entities использует кодогенерацию, которая работает только с ReSharper.

Тесты на ПК

Для тестов использовалась следующая система:

OS: Windows 10 Pro

CPU: Intel Core i7-6800K 3.40GHz

GPU: NVIDIA GeForce GTX 1060 3GB

RAM: 2x8Gb DDR4 2666MHz

Что лучше работает в Unity? MonoBehaviour, Jobs или Entities?
Горизонтальная - количество сущностей, вертикальная - количество кадров за 30 секунд.Вертикальная шкала является логарифмической.
Горизонтальная - количество сущностей, вертикальная - количество кадров за 30 секунд.Вертикальная шкала является логарифмической.

Тесты на мобильном устройстве

Для замеров на мобильном устройстве был использован достаточно бюджетный смартфон Redmi 9A.

Что лучше работает в Unity? MonoBehaviour, Jobs или Entities?
Горизонтальная - количество сущностей, вертикальная - количество кадров за 30 секунд.Вертикальная шкала является логарифмической.
Горизонтальная - количество сущностей, вертикальная - количество кадров за 30 секунд.Вертикальная шкала является логарифмической.

Вывод

Если учесть логарифмическое построение диаграмм и тот факт, что у нас используется цикл в цикле, то мы получим весьма интересные и практически линейные результаты. Как мы можем заметить реализация с использованием Jobs System действительно очень производительная, стандартная реализация (на MonoBehaviour) оказалась самой медленной, а вот Entities расположилась между ними.

Казалось бы, если нам нужно чуть больше производительности, то стоит выбирать Entities? Можно было бы ответить положительно, но обратите внимание на то, как ведет себя Entities на Android. И это не случайно. Все дело в том, что версия 1.0 сломана и работает крайне нестабильно. В ходе тестов приложение могло закрыться с ошибкой, графика отрисовываться неправильно или вовсе не отрисовываться, а также текущая версия ecs отказывается работать с IL2CPP без дополнительных манипуляций, информацию о которых очень сложно найти.

В сравнении с эталонной реализацией (в плане удобства) на MonoBehaviour реализация на Entities выглядит очень нелогичной, сложной и многословной. Реализация на Jobs System также выглядит сложной, но данная сложность обоснована и при написании многопоточного кода не возникает ощущения того, что тут что-то не так.

Объективная сравнительная таблица

Что лучше работает в Unity? MonoBehaviour, Jobs или Entities?

Стандартная реализация по прежнему является одним из лучших выборов при разработке, но из-за низкого порога входа разработчики могут создавать архитектуры, которые крайне сложно поддерживать. ECS стал популярен как раз за счет того, что при его использовании в первую очередь повышается качество кода, а архитектура проекта выстраивается чуть ли не самостоятельно. Однако реализация ECS от разработчиков движка выглядит как антипаттерн. Jobs же наоборот показали себя только с лучшей стороны.

Если вы заинтересованы в использовании ECS - рассмотрите альтернативные варианты от сторонних разработчиков. Если вам нужна реализация, которая такая же быстрая, как Entities+Jobs - рассмотрите Morpeh, который куда удобней и приятней в работе. Он поддерживает работу с Jobs, а его производительность ничуть не уступает производительности ecs от разработчиков движка.

Что же вошло в AppCore?

Сейчас, в январе 2024 года новая версия AppCore находится на стадии раннего тестирования, мы используем собственную архитектуру ECSA, которая легко может быть интегрирована с Jobs. Однако само ядро AppCore больше не зависит от Unity.

Ссылки

Видео о AppCore

Фреймворк

Адаптер для Юнити

Пример использования

77
6 комментариев

Честно говоря, я не понял для кого эта статья, много воды без конкретики - как выглядит алгоритм Boids хотя бы кратко псевдокодом, как это реализовывались на MB, с чем работала JobsSystem - тоже с кучей монобехов или 1 монобех через джобы манипулировал геймобжектами? Какие системы использовались при реализации через Entities? Вы пишете, что считаете ECS от юнити антипаттерном - почему? Для статьи для обывателя - слишком много воды, для статьи для программистов слишком мало конкретики. Простите, если моя обратная связь неуместна

3
Ответить

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

1
Ответить

Статья хорошая, спасибо, но с последней таблицей не согласен совершенно, потому что к объективным данным примешались субъективные

1
Ответить

Спасибо. Статья писалась практически год назад и только добралась до публикации без каких-либо переделок. Сейчас я смело могу сказать, что Entities стал куда удобней, а Jobs с учетом всех неудобств спокойно займет первое место в таблице.

1
Ответить

Когда сам ковырял, остались те же ощущения. Про джобы не помню толком, вроде не очень удобно, но с большего, просто расширение обычного подхода. А вот их ECS - это учить новый язык программирования. Причем во многих местах реализация вышла сложнее чем что-то похожее на плюсах. Что полностью убивает ту часть пользователей дивжка, которые "Я художник, но готов пописать немного говоноскрипта, чтобы выпустить игру своей мечты.".

1
Ответить

ECS для извращенцев) А Jobs для велосипедистов)

Для довольно узкой задачи подойдёт, но в большинстве случаев оно не надо. Тесты как всегда... Как часто один пустой объект выполняет то же самое хотя бы 100 раз? Как часто в сцене бывает хотя бы тысяча объектов с нестандартным кодом(скриптов)? Я видел такое крайне редко даже на больших сценах.

А джобы - аналог тасков, но с кучей ограничений. Забавно было, как-то видел как они по производительности проигрывали чистым C# Task на одинаковой задаче и при, по сути, одинаковом коде. Конечно, сейчас могли и допилить, но это всё равно велосипед с ограниченным функционалом.

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

PS: Сам ECS то норм... к реализации всегда вопросы. Даже к стандартным монобехам то вопросов не мало.

Ответить