Version: 5.3 (switch to 5.4b)
Сбои
Оптимизации

Профилирование

Первые шаги

Unity полагается на CPU (с хорошей оптимизацией для SIMD части процессора, напр. SSE на x86 или NEON на ARM) для скиннинга, батчинга, физики, пользовательских скриптов, частиц, и т.д.

GPU используется для шейдеров, вызовов отрисовки, эффектов пост-обработки.

Предел CPU или GPU

  • Используйте встроенный профайлер для определения CPU и GPU мс

Закон Парето при анализе

Основная масса проблем (80%) создаётся небольшим количеством ключевых причин (20%). Вы можете использовать профайлер редактора для идентификации наиболее ёмких в использовании CPU вызовов функций и оптимизировать их в первую очередь. Зачастую, оптимизация нескольких ключевых функций может дать существенный прирост общей производительности.

Вам следует удостовериться, что функции вызываются только тогда, когда это действительно нужно. Например, вы можете использовать события, такие как OnBecameVisible и OnBecameInvisible для определения того, что объект не виден, и избежать его обновления. Корутины могут отлично подходить в тех случаях, когда требуется вызвать код, который требует постоянных обновлений, но нет необходимости вызывать его каждый кадр:-

// Do some stuff every frame:
void Update () {
}

//Do some stuff every 0.2 seconds:
IEnumerator SlowUpdate () {
   while (true) {
      //do something
      yield return new WaitForSeconds (0.2f);
   }
}


Вы можете использовать класс .NET System.Threading.Thread для запуска тяжёлых вычислений в отдельном потоке. Это позволяет вам выполнять код на нескольких ядрах, но учтите, что API Unity не thread-safe (плохо совместимы с поточностью) - вам следует буферизировать входящие данные и результаты, а также считывать и присваивать их в главном потоке, чтобы иметь возможность использовать вызовы API Unity.

Профилирование CPU

Профилирование пользовательского кода

Не весь пользовательский код отображается в профайлере. Но вы можете использовать методы Profiler.BeginSample и Profiler.EndSample, чтобы нужный пользовательский код появился в профайлере.

Профилирование GPU

Профайлер редактора Untiy на данный момент не может отображать GPU данные. Мы работаем с производителями аппаратного обеспечения для того, чтобы это произошло. Tegra устройства должны первыми появиться в профайлере редактора.

Инструменты для iOS

  • Встроенный профайлер Unity (не тот, что в редакторе). Показывает время GPU для всей сцены.
  • Анализатор шейдеров PowerVR PVRUniSCo. См. ниже.
  • iOS: Xcode OpenGL ES Driver Instruments могут показать только высокоуровневую информацию:
    • “Device Utilization %” - время GPU, потраченное на отрисовку в целом. >95% означает, что приложению не хватает GPU ресурсов.
    • “Renderer Utilization %” - время GPU, потраченное на отрисовку пикселей.
    • “Tiler Utilization %” - время GPU, потраченное на обработку вершин.
    • “Split count” - количество разделений кадра, где данные вершин не влезли в выделенные буферы.

PowerVR - отложенный отрисовщик, основанный на тайлах, так что для него невозможно получить время GPU для каждого вызова отрисовки (draw call). Однако, вы можете получить время GPU для всей сцены, используя встроенный профайлер Unity (тот, что выводит результаты в консоль Xcode). Инструменты Apple на данный момент могут лишь сказать как занят GPU и его части, но не выдают время в миллисекундах.

PVRUniSCo выдаёт количество циклов для всего шейдера и примерное количество циклов для каждой строчки в коде шейдера. Windows & Mac! Но он в любом случае не совпадает в точности с тем, что делают драйверы Apple. Всё же, это хорошие примерные измерения.

Инструменты для Android

  • Adreno (Qualcomm)
  • NVPerfHUD (NVIDIA)
  • PVRTune, PVRUniSCo (PowerVR)

На Tegra, NVIDIA предоставляет отличные инструменты для замеров производительности, которые делают всё что вы пожелаете - время GPU для каждого вызова отрисовки, количество циклов для шейдеров, форсированную 2x2 текстуру, Null view прямоугольник, работает на Windows, OSX, Linux. PerfHUD ES не работает с потребительскими устройствами просто так, вот потребуется макетная плата (development board) от NVIDIA.

Qualcomm предоставляет отличный профайлер Adreno, который только под Windows, но работает с потребительскими устройствами! Он предоставляет графики временной шкалы, захват кадров, отладку кадров, вызовы API, анализатор шейдеров, “живое” редактирование.

Профилирование CPU в контексте графики

Встроенный профайлер выдаёт хороший обзор для каждого модуля:

  • время, потраченное не вызовы OpenGL ES API
  • эффективность батчинга
  • скиннинг, анимации, частицы

Profiler Ports

Порты, используемые профайлером Unity:

  • MulticastPort : 54998
  • ListenPorts : 55000 - 55511
  • Multicast (unittests) : 55512 - 56023

Они должны быть доступны из сетевого узла. То есть, устройства, которые вы пытаетесь профилировать, должны “видеть” эти порты на машине с редактором Unity, в котором запущен профайлер.

Память

Существует память Unity и память mono.

Память mono

В памяти mono содержатся скриптовые объекты, обёртки для объектов Unity (игровые объекты, ассеты, компоненты и т.д.). Сборщик мусора (Garbage Collector) отрабатывает при недостаточном количестве доступной памяти в момент очередного выделения памяти, или при вызове метода System.GC.Collect().

Память выделяется в блоках кучи. Если в выделенном блоке недостаточно места, может быть выделено больше блоков. Блоки кучи будут храниться в mono до тех пор, пока приложение не закроют. Другими словами, Mono не освобождает любую используемую память для ОС (Unity 3.x). Как только вы выделили некоторое количество памяти, она резервируется для Mono и не доступна для ОС. Даже если вы её освободите, на самом деле она будет доступна только для Mono, а не для ОС. Размер кучи памяти в профайлере будет только увеличиваться и никогда не уменьшится.

Если система больше не может разместить новые данные в выделенном блоке кучи, Mono вызывает “GC” (от Garbage Collector, сборщик мусора) и может выделить новый блок кучи (например, из-за фрагментации).

“Too many heap sections” означает, что вам недостаточно памяти Mono (из-за фрагментации или слишком активного использования).

Используйте System.GC.GetTotalMemory для получения общего количества используемой памяти Mono.

Основной совет - избегайте выделения памяти там, где это возможно.

Память Unity

Память Unity содержит данные ассетов (текстуры, меши, аудио, анимации, и т.д.), игровые объекты, внутренние объекты движка (для отрисовки, частиц, физики, и т.д.). Используйте Profiler.usedHeapSize для получения общего объёма используемой памяти Unity.

Карта памяти

Инструментов пока нет, но вы можете использовать описанные ниже варианты.

  • Профайлер Unity - не идеален, пропускает разные вещи, но вы можете получить примерные показатели. Работает на устройстве!
  • Встроенный профайлер. Показывает используемую кучу и выделенную кучу - см. память Mono. Показывает количество выделяемой памяти Mono за кадр.
  • Инструменты Xcode - iOS
  • Xcode Instruments Activity Monitor - столбец Real Memory.
  • Xcode Instruments Allocations - чистые показания выделяемой памяти для создаваемых и активных объектов.
  • VM Tracker (память для текстур обычно выделяется с меткой IOKit и меши обычно учитываются в VM Allocate).

Вы также можете создать собственный инструмент с помощью вызовов API Unity:-

  • FindObjectsOfTypeAll (type : Type) : Object[]
  • FindObjectsOfType (type : Type): Object[]
  • GetRuntimeMemorySize (o : Object) : int
  • GetMonoHeapSize
  • GetMonoUsedSize
  • Profiler.BeginSample/EndSample - профилирует ваш код
  • UnloadUnusedAssets () : AsyncOperation
  • System.GC.GetTotalMemory/Profiler.usedHeapSize

Ссылки на загруженные объекты выяснить невозможно. В качестве альтернативы, вы можете использовать “Find references in scene” для публичных переменных.

Сборщик мусора (Garbage collector, GC)

  • Отрабатывает, когда система не может уместить новые данные в выделенном блоке кучи.
  • Не используйте OnGUI на мобильных устройствах: он отрабатывает несколько раз за кадр, полностью перерисовывает видимую область и создаёт тонны вызовов для выделения памяти, которые требуют вызова GC.

Слишком часто создаёте/удаляете множество объектов?

  • Это может привести к фрагментации.
  • Используйте профайлер редактора для отслеживания использования памяти.
  • Встроенный профайлер можно использовать для отслеживания использования памяти mono.
  • System.GC.Collect() Вы можете использовать эту .Net функцию в тот момент, когда появление рывков не страшно.

Рывки при выделении памяти

  • Используйте списки повторно используемых экземпляров классов, память для которых была заранее выделена, чтобы реализовать свою схему управления.
  • Не допускайте выделения большого количества памяти за один кадр, вместо этого кэшируйте и выделяйте память заранее.
  • Проблемы с фрагментацией?

Предварительное выделение пула памяти

  • Храните список (List) неактивных GameObject’ов и повторно их используйте вместо вызовов Instantiate и Destroy.

Недостаточно памяти mono

  • Профилируйте активность памяти - когда заполняется первая страница памяти?
  • Вам действительно требуется такое количество игровых объектов, что одной страницы памяти недостаточно?
  • Используйте структуры вместо классов для локальных данных. Классы хранятся в куче, а структуры на стеке.
  • Read the Understanding Automatic Memory Management page.

Падения из-за нехватки памяти

Иногда игра может упасть с ошибкой “out of memory”, хотя в теории, памяти должно хватать. Когда это происходит, сравните нормальное потребление памяти вашей игры и размер выделенной памяти в момент падения. Если числа не совпадут, значит произошёл скачок в потреблении памяти. Это может произойти по следующим причинам:

  • Одновременно загружаются слишком большие сцены - чтобы это исправить, используйте пустую сцену между двумя большими.
  • Additive загрузка сцены - удалите неиспользуемые части для сохранения приемлемого уровня потребления памяти.
  • В память загружаются огромные ассет бандлы.
  • Используются текстуры без правильного сжатия (совсем плохо для мобильных устройств).
  • У текстур включен доступ к пикселям (Get/Set pixels). Это требует наличия распакованной текстуры в памяти.
  • Текстуры, загружаемые из JPEG/PNGs во время выполнения по сути не сжаты.
  • Большие mp3 файлы, помеченные как “decompress on loading” (распаковывать при загрузке).
  • Хранение неиспользуемых ассетов во всяческих странных кэшах, вроде статических полей MonoBehaviour, которые не очищаются при смене сцен.
Сбои
Оптимизации