Анализ следов Profiler
При профилировании приложения могут возникнуть некоторые типичные проблемы. На этой странице описано, как выяснить причину некоторых типичных проблем с производительностью.
Расшифровка следов запуска
При просмотре трассировки времени запуска, есть два ключевых метода для проверки: UnityInitApplicationGraphics и UnityLoadApplication. Эти два метода являются основными местами, где конфигурация, ассеты и код проекта могут повлиять на время запуска.
Примечание: Время запуска вашего приложения различается от платформы к платформе. На большинстве платформ, запуск происходит в то время как запуск экрана появляется.
На приведенном выше скриншоте из трека Instruments примера проекта Unity, запущенного на устройстве iOS, в методе startUnity для конкретной платформы обратите внимание на методы UnityInitApplicationGraphics и UnityLoadApplication.
UnityInitApplicationGraphics выполняет много внутренней работы: настраивает графическое устройство и инициализирует множество внутренних систем Unity. Он также инициализирует Система ресурсов путем загрузки индекса всех файлов, содержащихся в системе ресурсов.
Unity Система ресурсов включает все файл актива в своих данных, что в Resources папка в Assets папку проекта. Это включает любые файлы в папке Resources Таким образом, время, требуемое для инициализации системы ресурсов увеличивается в зависимости от количества файлов в папке. Resources папки в проекте приложения.
UnityLoadApplication содержит методы, которые загружают и инициализируют первый Scene в проекте. Это включает в себя десерриализацию и инстанционирование данных, необходимых для отображения первого Scene, таких как компиляция Shaders, загрузка Textures и инстанционирование GameObjects. Также Unity выполняет обратные вызова Awake всех MonoBehaviourв первом Scene.
Эти процессы означают, что если в обратном вызове Awake в первом Scene проекта имеется какой-либо длительно работающий код, то этот код может быть ответственным за замедление первоначального времени запуска проекта. Решение этой проблемы включает либо устранение медленного кода, либо выполнение его в другом месте жизненного цикла приложения.
Расшифровка трасс времени выполнения
Для профилирования следов, запечатленных после первоначального времени запуска, основным местом интереса является метод PlayerLoop. Это главный цикл Unity, и код в нем выполняется один раз в кадре.
Вышеприведенный скриншот иллюстрирует некоторые из наиболее влияющих на производительность методов в PlayerLoop. Примечание: Названия методов в PlayerLoop могут различаться между версиями Unity.
PlayerRender — это метод, который запускает систему рендеринга Unity. Это включает в себя удаление объектов, вычисление динамических пакетов и отправку инструкций рисования в GPU.. Любые эффекты изображений или обратные вызова скриптов на основе рендеринга (например,,OnWillRenderObject,) также запускаются здесь. В целом, это должно быть главным потребителем времени CPU, когда проект интерактивен.
BaseBehaviourManager вызывает три шаблонные версии CommonUpdate. Они вызывают определенные обратные вызова внутри MonoBehaviour, присоединенных к активному GameObjects в текущем Scene:
CommonUpdate<UpdateManager>вызовыUpdateобратные вызоваCommonUpdate<LateUpdateManager>вызовыLateUpdateобратные вызоваCommonUpdate<FixedUpdateManager>вызываетFixedUpdate, если система физики открылась
В целом, BaseBehaviourManager::CommonUpdate<UpdateManager> является наиболее полезным семейством методов для проверки, потому что оно является точкой входа для большинства скриптовых кодов, запущенных в проекте Unity.
Существует несколько других методов, которые полезны для проверки:
UI::CanvasManagerвызывает несколько различных обратных вызовов, если проект использует систему UGUI. Это включает пакетные вычисления Unity UI и обновления макета; две операции, которые чаще всего вызывают появлениеCanvasManagerв Profiler.DelayedCallManager::Updateзапускает сопроцедуры.-
PhysicsManager::FixedUpdateзапускает физическую систему PhysX. В основном это работа внутреннего кода PhysX. Количество физических объектов в текущей сцене — например,RigidbodyиColliderвлияют на внутренний код PhysX. Сюда же попадают обратные вызовы, связанные с физикой, в частностиOnTriggerStayиOnCollisionStay.
Если проект использует физику 2D, то она появляется как аналогичный набор вызовов под Physics2DManager::FixedUpdate.
Разбор метода скрипта
При вызове скриптов на платформах, кросс-компилированных с IL2CPP, искать строки трассировки, содержащие ScriptingInvocation объект. Это точка, в которой внутренний нативный код Unity переходит в среду выполнения скриптов, чтобы выполнить код скрипта. Примечание: Технически, после того, как Unity запускает ваш код C# через IL2CPP, он также становится нативным кодом. Однако, этот кросс-компилированный код в основном выполняет методы через фреймворк runtime IL2CPP и не похож на написанный от руки C++.
На вышеприведённом скриншоте методы, вложенные под строкой RuntimeInvoker_Void, являются частью кросс-компилированных скриптов C#, которые Unity выполняет один раз в кадре.
Имена строк трассировки состоят из имени исходного класса, символа подчёркивания и имени исходного метода. В этом примере трассировки видно EventSystem.Update, PlayerShooting.Update и ряд других Update Это стандартные Unity Update обратные вызовы, найденные в большинстве MonoBehaviours.
Вы можете развернуть эти методы, чтобы увидеть, какие методы в них потребляли CPU времени. Это включает другие методы скриптов в проекте, Unity, APIs и код библиотеки C#.
Вышеприведенный трек показывает, что метод StandaloneInputModule.Process пролегал через весь UI один раз в кадре. Этот метод обнаруживает, были ли какие-либо касательные события, зависшие над элементами UI или активировавшие их. Метод, итерирующий все элементы UI и проверяющий, находится ли позиция мыши в пределах их ограничивающего прямоугольника, ресурсоемкий.
Загрузка ассетов
Вы также можете идентифицировать загрузку ассетов в трассе CPU. Основным методом, указывающим на загрузку ассета, является SerializedFile::ReadObject. Этот метод соединяет поток двоичных данных из файла с системой сериализации Unity, которая работает через метод Transfer. Метод Transfer применяется ко всем типам ассетов, таким как Тексты, MonoBehaviours и Системы Частиц.
Вышеприведенный скриншот представляет собой след Unity, загружающего Scene. Когда он загружает Scene, Unity читает и десериализует все Assets в Scene, как это обозначено вызовами различных методов Transfer под SerializedFile::ReadObject.
Если во время выполнения наблюдается замедление производительности, а в трассировке производительности показано, что SerializedFile::ReadObject использовал значительное количество времени, это означает, что загрузки ассетов снизили частоту кадров. Примечание: SerializedFile::ReadObject обычно появляются в главном потоке, когда SceneManager, Resources или AssetBundle APIs запрашивают синхронные загрузки ассетов.
Чтобы решить эту проблему, вы можете сделать загрузку ассетов асинхронной (что переместит тяжелый вызов ReadObject в рабочий поток), или предварительно загрузить некоторые тяжелые ассеты.
Вызовы Transfer также появляются, когда Unity клонирует объекты (обозначенные методом CloneObject в треке). Если вызов Transfer появляется под вызовами CloneObject, то Unity не загружает Asset из хранилища. Вместо этого Unity передаёт данные старого объекта новому объекту. Для этого Unity сериализирует старый объект и десериялизирует полученные данные как новый объект.