Version: 2017.1
Сбои
Оптимизации

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

Первые шаги

Unity relies on the CPU (heavily optimized for the SIMD part of it, like SSE on x86 or NEON on ARM) for skinning, batching, physics, user scripts, particles, etc.

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 time spent on rendering in total. >95% means the app is GPU bound.
    • “Renderer Utilization %” - GPU time spent drawing pixels.
    • “Tiler Utilization %” - GPU time spent processing vertices.
    • “Split count” - the number of frame splits, where the vertex data didn’t fit into allocated buffers.

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

PVRUniSCo gives cycles for the whole shader, and approximate cycles for each line in the shader code. Windows & Mac! But it won’t match what Apple’s drivers are doing exactly anyway. Still, a good ballpark measure.

Инструменты для 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, в котором запущен профайлер.

Память

There are two types of memory, Mono memory and Unity memory.

Память mono

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

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

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

“Too many heap sections” means you’ve run out of Mono memory (because of fragmentation or heavy usage).

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

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

Память Unity

Unity memory handles Asset data (Textures, Meshes, Audio, Animation, etc), Game objects, Engine internals (Rendering, Particles, Physics, etc). Use Profiler.usedHeapSize to get the total used Unity memory.

Карта памяти

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

  • Профайлер 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, которые не очищаются при смене сцен.
Сбои
Оптимизации