AIR vs. Unity3D. Кто быстрее? (Обновление 1)

Обновление 1: вылил немного сорцов (см. в конце статьи).

Привет!
Да, давно не писал, блаблабла.. К делу! =)

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

В последнее время я все чаще замечаю как выходцы из мира Flash-разработки начинают интересоваться Unity3D, да и сам я в последнее время работаю с Unity всё больше и всё плотнее.
И как по мне — так это очень здорово — расширять свой кругозор, изучать новые языки (надеюсь, большинство пришедших из Flash-разработки делают правильный выбор в пользу C#), вливаться в новое комьюнити, решать новые задачи и т.д.!
Однако, многие нынешние флэшеры всё ещё сомневаются — стоит ли пробовать Unity3D, тратить время на изучение технологии… Зачастую, эти сомнения основаны на неясности относительно разницы в производительности приложений на Unity3D по сравнению с AIR, вот о ней я и расскажу в этой статье, чтобы помочь им определиться.

Примеры в статье я буду компилировать с помощью AIR 3.6 и последней Unity3D 4.1 с максимально приближённым функционалом и внешним видом.
Я сравню их производительность на относительно медленном GalaxyTab 10.1, и некоторые тесты проведу в десктопном режиме.

Интро

Итак, начнем с пустых проектов.
В Юнити проекте для измерения FPS будем использовать самописный fps-ометр, который рисует количество FPS на GUIText.
В AIR проекте будем использовать разные fpsометры — в основном будем брать статсы, встроенные в используемые фреймворки для максимальной совместимости.
Собираем релизные версии apk (во FlashDevelop’е собираем captive-runtime вариант).
Выливаем обе apk на девайс, смотрим:

FPS: в обеих стабильные 60 (разрешение у Galaxy Tab 10.1 — 1280×800).
RAM: померить полный объем потребляемой памяти приложением на AIR с помощью стандартных API насколько я помню не выйдет (поправьте, если я не прав), в Unity3D есть возможность обращаться к встроенному профайлеру для этих целей. Для AIR можно использовать какой-нибудь внешний профайлер, но т.к. они будут разные с разным оверхедом, то нам это не подходит — нас интересуют наиболее близкие условия окружения.
Объем файлов:
AIR — 9223 кб
Unity3D — 7406 кб
Если Stripping Level в Unity3D поднять до Strip Bytecode, получим вообще 6946 кб.

Скорость кода

Теперь добавим текстовое поле в AIR приложение и OnGUI текстовое поле в Unity3D — для вывода времени выполнения ряда следующих тестов.
Начнем с цикличного создания и заполнения массивов случайными числами.
В случае AIR это будет фиксированный вектор заданной длины, т.к. это самый быстрый полноценный массив в as3.
Будем сравнивать его со стандартным массивом в C#, также наиболее быстрым.

Код теста на as3 такой:

private const VECTOR_LENGTH:int = 100000;
//...
private function callFewTests():void
{
var timeBegin:int = getTimer();
for (var i:int = 0; i < 100; i++)
{
makeACake();
}
outputTextField.text = String(getTimer() - timeBegin) + " ms";
}

private function makeACake():void
{
var v:Vector.<int> = new Vector.<int>(VECTOR_LENGTH, true);

for (var i:int = 0; i < VECTOR_LENGTH; i++)
{
v[i] = int(Math.random());
}
}

Код на C# для Unity3D:

private const int ARRAY_LENGTH = 100000;
private float totalTime = 0;

void Start ()
{
CallFewTests();
}

void OnGUI()
{
if (totalTime > 0)
{
GUILayout.Label(totalTime.ToString("0."));
}
}

private void CallFewTests()
{
float timeBegin = Time.realtimeSinceStartup;

for (int i = 0; i < 100; i++)
{
MakeACake();
}

totalTime = (Time.realtimeSinceStartup - timeBegin)*1000;
}

private void MakeACake()
{
int[] a = new int[ARRAY_LENGTH];

for (int i = 0; i < ARRAY_LENGTH; i++)
{
a[i] = (int)Random.value;
}
}

Как можно заметить из кода, мы 100 раз создаем целочисленный массив из 100000 элементов и заполняем его в цикле случайными значениями, используя для этого встроенные средства для получения случайных чисел.

После установки APK, приложения запускались несколько раз, чтобы исключить возможные тормоза первого запуска.
Средние результаты:

AIR — 1965 мс
Unity3D — 1600 мс

Результат оказался практически одинаковым, Unity3D выполняет задачу немного быстрее.
Попробуем заменить получение случайного числа на вызов метода, который возвращает сумму пары чисел, дабы исключить неизвестную нам разницу в производительности рандомов:

private int GetTotalGeeksCount()
{
int a = 23;
int b = 41;
return a + b;
}

Я удостоверился (с помощью декомпиляторов), что этот метод честно компилируется без оптимизаций на обоих испытуемых, вылил на Galaxy. Вот результаты:

AIR — 837 мс
Unity3D — 224 мс

То есть на довольно простых операциях, таких как сложение целых чисел, создание и заполнение одномерных массивов, вызов методов, выполнение цикла for и т.п. — разница в производительности более чем в 3 раза!

Попробуйте сравнить сами, может вы получите другие результаты (буду рад об этом узнать в комментариях) — см. файлы
Random*, Clean*

Окей, а что если попробовать выгрузить из Unity3D флэшку с этими вычислениями и сравнить скорость на десктопе?) Давайте посмотрим: увеличиваем количество итераций с 100000 до 1000000, компилируем, выгружаем из Unity3D под FP 11.4, берём swf из папки bin нашего мобильного FlashDevelop проекта, и запускаем их в последней (на данный момент — 11.6) релизной версии Projector проигрывателя.

AIR — 573 мс
Unity3D — 2580 мс (!)

Ещё в процессе обнаружился баг в SWF, выгруженном из Unity — после такой строки myFloat.ToString(«0»); код просто переставал работать. Забавно, что экспортер в состоянии компилировать в swf более-менее сложные проекты, а такое простое выражение вызывает проблемы.

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

Для порядка, надо бы сравнить производительность выгруженного проекта из Unity3D под десктопный WebPlayer с производительностью нашего swf в Projector проигрывателе.

AIR — 573 мс (измеряли ранее)
Unity3D — 102 мс

Видим превосходство выгруженного из Unity3D в более чем 5 раз. Неплохо!
Попробуем выгрузить из Unity3D десктопные (exe) варианты:

x86 — 103 мс
x64 — 124 мс

Ситуация почти не поменялась. Интересно, что x64 вариант работает немного медленней.

Теперь в коде лишь циклы for, да вызовы методов — уберём всё, что касается массивов. Посмотрим с какой скоростью выполнятся этот код на мобильном устройстве:

AIR — 4181 мс (http://pastebin.com/yNr3aVyc)
Unity3D — 281 мс (http://pastebin.com/btz405XS)

Я попробовал собрать AIR проект с помощью ASC2 в надежде увидеть значительные изменения, но в итоге время сократилось лишь до 3991 мс.

Резюмируя, можно заметить, что производительность as3 кода всё ещё остается ахиллесовой пятой AIR, как и много лет назад. Надеюсь, в будущем ситуация изменится к лучшему.
Unity3D Calculations*, Simple Code*

2D

Ок, перейдём к рендеру, начнем с 2D. Делаем простой тест — создаем и крутим вокруг своей оси 3000 простых квадов небольших размеров (дабы не иметь большой overdraw).
Для AIR используем Starling (знаю, что есть и более производительные фреймворки, однако этот является наиболее известным и поддерживается Adobe, то есть это выбор большинства).
Также используем 16bit, родной старлинговский FPSометр.

В Unity3D Камера ортогональная, Solid Color. Шейдер — простой самописный (цвет без альфы).
Use 32-bit Display Buffer и Use 24-bit Depth Buffer отключены.

eAhNVDy

Компилируем, выгружаем на девайс.. Результат, FPS:
AIR — 32
Unity3D — 20

Весьма интересные результаты, но вполне обоснованные. В Юнити все вычисления производятся исходя из того, что объекты могут быть 3D и находятся в 3D пространстве.
В старлинге же применены различные оптимизации благодаря изначально двухмерной природе фреймворка. Кстати, в данный момент идет работа над 2D режимом в Unity 😉
Quads*

Теперь натянем текстуру на плоскости, уменьшим количество объектов до 1000 и добавим к вращению движение.
Текстурку (и сопутствующий код загрузки ресурсов для AIR теста) я взял из Starling Benchmark’а Валеры Бохана (привет, Валера! =) ).
Сжатие в Unity3D было выставлено в RGBA DXT5, благо Гэлекси на тегре и текстура с прозрачностью.

cA6t0eU

Компилируем, выливаем. Результат, FPS:
AIR — 43
Unity3D — 47

Ну, тут результат несколько неоднозначный. Думаю, Юнити удалось тут немного обогнать AIR только за счет DXT5 сжатия, которое оптимально для устройств на Tegra, но не универсально. При использовании RGBA32 (единственный формат среди оставшихся, при котором визуально текстура не отличается от таковой в AIR) FPS в тесте Unity3D проседает до 30. С другой стороны, у меня нет под рукой других Андроид девайсов не на Tegra, так что я не могу проверить производительность других форматов сжатия в новых условиях. В любом случае, можно загрузить в Google Play билды под разные устройства — с разным сжатием текстур например, тем самым позволяя пользователям работать с наиболее подходящими.
Texture*

Выходит, по совокупности тестов и нюансов, производительность 2D на GPU в AIR выше, чем в Unity3D. Полагаю, основная причина такого различия описана мной выше — 2D фреймворк, выбранный в качестве представителя лагеря AIR хорошо оптимизирован под двухмерное применение, в то время как Unity3D манипулирует объектами и производит расчёты в 3D пространстве. Возможно, 2D режим в будущем позволит ей тягаться с AIR 2D фреймворками на равных.
Кстати, на этом примере хорошо заметна мощь Stage3D — благодаря низкоуровневости, создатели фреймворков могут производить различные оптимизации под конкретные задачи, очень гибкий подход.

3D

Думаю, достаточно 2D, перейдём к 3D!
Для начала — стандартный тест с кучей кубиков без освещения. Добавляем 1000 кубиков так, чтобы они не сильно перекрывали экран, дабы не упереться в fillRate, используем самый простой шейдер.
Для AIR будем использовать Away3D (4.1.0 Alpha), по тем же причинам, что и Starling.

fskxVfR

Компилируем, выливаем. Результат, FPS:
AIR — 10
Unity3D — 49

Безоговорочная победа Unity3D. И нет, AIR билд не отладочный — релизный. Вот на всякий случай исходный код основного класса AIR проекта: http://pastebin.com/iNzPZTnM
Simple Cubes*

Теперь немного усложним задачу. Загрузим более сложную модельку, размножим в количестве 10 штук, добавим 1 направленный источник света без теней и поменяем шейдер. В Unity будем использовать обычный Specular шейдер без оптимизаций. Сцена получилась из 173840 треугольников.

gzCy14h

Компилируем, выливаем. Результат, FPS:
AIR — 27
Unity3D — 51

Вполне ожидаемые результаты.
Lighting*

PS

В заключение хочу сказать, что высокая производительность Unity3D совместно с интегрированной и очень гибкой IDE делают её хорошим кандидатом на роль заменителя AIR.
Так что, если вы пожелаете попробовать что-то новое для создания кроссплатформенных игр, я бы не задумываясь посоветовал познакомиться с Unity3D.
Кстати, при создании тестовых проектов я использовал не все возможности по оптимизации, но это и к лучшему — задача была сравнить производительность платформ при использовании с настройками по умолчанию и некоторыми общими оптимизациями.
Ну и я долгое время не касался библиотеки Away3D, так что мог забыть что-то важное. Если заметите — дайте знать, исправлюсь!)

Буду рад услышать ваши мысли на эту тему!
На а если вы уже проводили подобные сравнения и получили иные результаты — пишите, буду рад ознакомиться!

Архив с примерами приложений:
AIRvsUnity3D.7z (38 МБ)
Сжато в LZMA2 (используйте 7-zip 9 или аналог)

Обновление 1:
Архив с Unity3D исходниками:
UnityPerformanceTest.7z (750 КБ, LZMA2)
Это Unity3D 4.1 проект. Там есть по одному тесту (в отдельных сценах) из каждой части статьи.


Все приведенные в архиве примеры тестировались исключительно под 1280 на 800 (landscape), на Tegra 2 устройстве (Samsung Galaxy Tab 10.1) под управлением Android OS 4.0.4. Корректная работа в других условиях не гарантируется.

Комментарии

AIR vs. Unity3D. Кто быстрее? (Обновление 1) — 5 комментариев

  1. А можно увидеть исходники для тестов графики, в частности, для текстурированных квадов? Честно говоря, результаты выглядят как-то сомнительно.

    • Если вас интересуют исходники на Unity — то да, они будут доступны несколько позже (там можно будет посмотреть 2D тест с текстурами, 3D тест с муравьями) — я добавлю ссылку на архив с ними в конце статьи. К сожалению, исходники для всех тестов не сохранились — не было времени все красиво оформлять.

  2. Большое спасибо за проведенную работу! Впечатляющие результаты. Интересно узнать как обстоят дела на айпаде 🙂

  3. Прекрасный обзор, не хватает лишь одного — большого заголовка «Выводы» в конце со сводной таблицей результатов под ним.

    Ну, и про айпад интересно, да 🙂

    • Спасибо, Рост!) Постараюсь исправиться в следующей статье, в которой я сравню с Юнити ряд других трёхмерных Stage3D фреймворков и обновлённый тест для Away3D 😉