Actual PerfDog оптимизирует производительность небольших игр

контрольная работа

задний план:
Наш движок — Egret, использующий родной EUI и переносящий мини-игры WeChat;
После выхода первой версии проекта используйте PerfDog для тестирования волны данных. В результате было обнаружено много проблем.Эта статья в основном разделена на две части.

  • Первая часть в основном знакомит с поиском проблем с помощью PerfDog,
  • Вторая часть в основном знакомит с позиционированием данных и решением проблем с помощью PerfDog.

Для конкретных операций PerfDog см. документациюИнструкции по использованию PerfDog

Часть 1. Анализ данных

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

первые тестовые данные

ФПС:

ОЗУ:

ПРОЦЕССОР:

в заключении:

1. Мы обнаружили, что FPS сильно колеблется во время боя.
2. Память продолжает расти
3. Использование ЦП приложением слишком мало, что составляет всего около 1%

Прежде всего, для объяснения вопроса 3:
Я решил протестировать приложение WeChat ранее, и мини-игра существует как подпроцесс, поэтому я должен выбрать для тестирования подпроцесс PerfDog, чтобы полученные данные были более точными; темный процесс на рисунке ниже представляет запущенный процесс верхнего уровня

Для этого многопроцессного теста приложения:

На платформе iOS мультипроцесс APP разделен на расширение APP и системный XPC-сервер.
Например, программное обеспечение для прямых трансляций киберспорта использует процесс расширения APP (имя процесса расширения LABroadcastUpload). Конечно, также можно использовать системный служебный процесс XPC Server, например, обычный веб-браузер будет использовать webkit.

Платформа Android, общие крупномасштабные приложения, такие как игры, иногда запускаются совместно с несколькими процессами (мини-игры WeChat, Weishi и другие приложения и игры, такие как Honor of Kings и другие игры, имеют несколько подпроцессов), вы можете выбрать цель подпроцесс для целевого тестирования. По умолчанию это основной процесс. Как показано на картине славы короля


Подробную инструкцию по применению можно найти здесь:Руководство по эксплуатации PerfDog

Чтобы определить, что вызвало большое колебание FPS, и определить, есть ли OOM, теперь давайте выберем подпроцесс для второго теста;

Данные второго теста

Состав тестовых данных:
Чтобы проверить некоторые мои догадки и более детально локализовать проблему, мы проделали в тесте несколько специальных операций:

1. Бой зависает [чтобы определить не сработала ли утечка памяти во время боя]
2. Несколько раз откройте и закройте пользовательский интерфейс [чтобы определить, есть ли утечка памяти при создании и уничтожении пользовательского интерфейса]
3. Все еще на странице пользовательского интерфейса [чтобы отличить ее от других сцен]
4. Повесить экран с выключенным экраном [чтобы определить, является ли это утечкой памяти, вызванной ресурсами изображения, или утечкой, вызванной ресурсами кода]
Данные ФПС:

Данные процессора:

Данные памяти:

нагрузка на графический процессор

Анализ FPS и GPU:

С помощью данных FPS мы обнаружили, что рывок во время игрового процесса очень серьезен, а колебания FPS слишком велики, особенно когда пользовательский интерфейс включен или выключен.Для игры экран рендеринга, условно говоря, может есть узкое место.Проверьте GPU один за другим, это Когда мы проверили данные, мы обнаружили, что коэффициент использования GPU также стал аномально высоким.Очевидно, что давление рендеринга очень велико, и когда наш игровой интерфейс открывается, битва на самом деле будет отображаться Это связано с дизайном нашей игры, поэтому давление рендеринга очень велико.

Анализ памяти:

По данным PerfDog мы выяснили, что память постоянно растет, и конечный результат — убить System. По факту можно определить, что произошла утечка памяти, за 72 минуты память увеличилась с 726М до 956М, и продолжает расти;

Вот еще немного,Чтобы увидеть, есть ли OOM, вы можете не просто посмотреть PSS (память PerfDog по умолчанию - PSS), но также обратить внимание на VSS.В некоторых играх может быть общий размер PSS и постоянное увеличение VSS, что также ненаучно.
Простой обмен общими отношениями индикатора памяти

потребление памяти
VSS — потребление виртуальной памяти Virtual Set Size (включая память, занимаемую разделяемыми библиотеками)
RSS — Размер резидентного набора фактически использует физическую память (включая память, занятую разделяемыми библиотеками).
PSS — Proportional Set Size фактическая используемая физическая память (пропорциональное распределение памяти, занимаемой разделяемыми библиотеками)
USS — физическая память, занимаемая только процессом уникального набора размеров (исключая память, занимаемую общей библиотекой).
Вообще говоря, размер используемой памяти выглядит следующим образом: VSS >= RSS >= PSS >= USS

Вот еще немного об AndroidLMK (Убийца низкой памяти), подробности вдаваться не буду.

1. Система Android будет регулярно выполнять проверку, когда объем памяти достигнет определенного значения, соответствующий процесс будет остановлен, а память освобождена.
2. Каждая программа будет иметь значение oom_adj, чем меньше значение, тем важнее программа и тем ниже вероятность быть убитым
3. Убийца нехватки памяти в основном оценивает важность процесса через oom_adj процесса. Размер oom_adj связан с типом процесса и порядком, в котором процессы запланированы.
4. Таблицу порогов можно настроить через /sys/module/lowmemorykiller/parameters/adj и /sys/module/lowmemorykiller/parameters/minfree

Теперь объедините два тестовых данных, чтобы сделать вывод.

в заключении:

1. Колебания FPS слишком резкие и нестабильные, особенно при создании и закрытии UI;
2. Утечка памяти.Поскольку память продолжает увеличиваться независимо от того, какая операция, высокая вероятность вызвана общедоступными компонентами.
3. На самом деле есть еще какие-то мелкие проблемы, но приоритетом является решение этих двух

Вторая часть - проблемная локация

Анализ проблемы утечки памяти

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

Локальная архитектура проекта:

1. Инфраструктура нашего проекта — это тот же базовый класс (наследственный код), который вызывают все базовые функции, такие как классы связи и т. д.;
2. Мы обнаружили, что память увеличивается, независимо от того, в какой среде находится персонаж, или даже при выключенном экране, мы действительно можем локализовать проблему с базовым классом внутри проекта с высокой вероятностью;

Затем начните подробное расследование;

Устранение неполадок с утечкой памяти

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

механизм рециркуляции
Выделение и восстановление памяти в JS автоматически выполняются виртуальной машиной, и нет необходимости писать парные коды удаления/освобождения для каждой операции new/malloc, как в C/C++.Хранение переменных в движке JS осуществляется в основном в стеке память, куча оперативной памяти. Суть утечки памяти в том, что некоторые объекты неожиданно не рекуперируются, а находятся в памяти.
Принцип ГК
Одной из характеристик виртуальной машины JavaScript является то, что накладные расходы на создание объекта намного превышают накладные расходы на вычисление объекта, а создание объекта приводит к сборке мусора, а сборка мусора время от времени приводит к зависанию игры.
Просматривайте бесполезные объекты в куче и освобождайте место в памяти, занимаемое этими объектами. Большинство реализаций GC (Gabage Collection) в браузерах используют алгоритм достижимости.Достижимые объекты — это объекты, которые могут образовывать связный граф с корнями GC. Когда у объекта нет цепочки ссылок на корни GC, он становится целью сборщика мусора, и система при необходимости освобождает занимаемую им память.

Здесь я использую профилирование головы Google Chrome, или вы можете использовать профилировщик движка egret:
Его легко использовать:

1. Откройте браузер Google, откройте веб-страницу, которую нужно отслеживать, нажмите F12 под Win, чтобы открыть инструменты разработчика.
2. Переключитесь на «Память», выберите тип кучи и выберите «Создать моментальный снимок кучи», чтобы начать делать снимки.
3. Представление справа перечисляет список объектов в куче Щелкните объект, чтобы просмотреть иерархию ссылок объекта.
4. После входа в игру сделайте снимок, откройте интерфейс, закройте интерфейс и сделайте снимок.
5. Преобразуйте новый снимок в представление «Сравнение» для сравнительного анализа памяти.

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

Время от времени мы можем делать снэпшоты (из-за проекта компании я не буду показывать реальный проект, здесь просто для обучения):

Мы можем открыть инструмент анализа памяти Google Chrome, и есть три варианта, которые мы можем использовать поочередно в соответствии с нашими собственными методами отладки;

1. Моментальный снимок кучи — используется для печати моментальных снимков кучи, файлы моментальных снимков кучи показывают распределение памяти между объектами javascript страницы и связанными узлами DOM.
2. Инструментарий распределения на временной шкале. Запись информации о памяти на временной шкале и запись информации о памяти по мере изменения времени.
3. Выборка распределения - выборка информации о памяти, используйте метод выборки для записи распределения памяти. Этот тип профиля имеет минимальную нагрузку на производительность и может использоваться для длительных операций. Это обеспечивает хорошее приближение распределения подразделений стека, выполняемых javascript.


Вот пример использования анализа моментальных снимков кучи,

Посмотреть подробности справа

Видно, что прямоугольный объект увеличивается, поэтому мы можем проверить причину, по которой прямоугольный объект не освобождается:

Именно потому, что в объекте Rect есть свойство rect, на которое была сделана ссылка, и память не может быть освобождена, мы можем найти соответствующее место кода, и мы можем быстрее найти причину; в конце концов, мы обнаружили, что это было потому, что в пользовательском глобальном прослушивателе событий. Объект создается, но некоторые свойства этого объекта будут по-прежнему ссылаться на этот прослушиватель событий и не будут переработаны

Конечно, чтобы определить, какая функция быстрее, мы также можем использовать

Общий результат такой

График HEAP (куча) в Обзоре представляет кучу JS.
Стек вызовов Вообще говоря, вертикальное направление не имеет большого значения, оно просто означает, что функция глубоко вложена, а горизонтальное направление означает время вызова, если время вызова слишком велико, то необходима оптимизация. Стек вызовов результата записи. Когда отображается горизонтальное направление, будут плавающие окна с более подробной информацией. Вертикальное направление — это стек вызовов, а вызовы функций — сверху вниз. Прокрутите колесико мыши, чтобы просмотреть информацию о стеке вызовов за определенный период времени. Наведите указатель мыши на функцию в стеке вызовов Call Stacks, чтобы просмотреть сведения о функции. Обычно это связано с оптимизацией производительности, а при утечке памяти в основном используется для определения того, какая операция была выполнена.
Панель счетчика. Здесь вы можете увидеть использование памяти (так же, как график HEAP на панели обзора), показывающий следующее соответственно: куча JS, документы, узлы DOM), слушатели (listeners) и память GPU (память GPU). Установите или снимите флажок, чтобы показать или скрыть его на диаграмме.

В основном сосредоточьтесь на третьей памяти кучи JS, количестве узлов и количестве слушателей. Наведите указатель мыши на кривую, чтобы отобразить определенные данные в левом нижнем углу. Если один из этих данных продолжает расти и нет тенденции к снижению, это может быть утечка.
Из-за нехватки места я не буду здесь больше рассказывать об использовании этих инструментов, и в Интернете есть много соответствующих руководств;

Катон оптимизация

С помощью данных PerfDog мы обнаружили, что GPU находится под большой нагрузкой.В играх долгая отрисовка экрана обычно связана с тем, что слишком много вызовов отрисовки или каждая отрисовка занимает много времени.

Однако после проверки вызова отрисовки нашей игры было установлено, что во время работы игры слишком много вызовов отрисовки, из-за чего рендеринг каждого кадра занимает много времени, поэтому возникает явление зависания;
О просмотре drawcall и т.п. можно посмотреть через собственную панель FPS Egret.документация по отладке цапли
Перед оптимизацией мы должны сначала понять, что делает белая цапля в отрендеренном кадре.

можно разделить на

Рабочее содержание каждого кадра:

1. Выполнить EnterFrame один раз, в это время движок будет выполнять логику в игре. и вызывает событие EnterFrame
2. Двигатель выполнит очистку. Стереть все изображения предыдущего кадра
3. Ядро Egret обходит все объекты DisplayObject в игровой сцене и пересчитывает преобразования всех объектов отображения.
4. Все изображения нарисованы на холсте

Теперь, чтобы оптимизировать его:
Первое, что нужно сделать, это уменьшить вызов отрисовки:

1. Замените все маленькие картинки атласами
2. Реализуйте пакетную обработку текста, настроив шрифты, используйте шрифты изображений вместо собственных шрифтов.
3. Разделение динамического и статического, размещение изменений, которые необходимо изменить, и тех, которые остаются неизменными, на разных уровнях, таких как фоновый слой, слой значков и слой динамических изменений.
4. Попробуйте использовать анимацию кадра костей дракона вместо анимации позвоночника.
5. Используйте cacheAsBitmap для расчета векторной графики в виде растровых изображений во время выполнения.

Уменьшите накладные расходы на события кадра:

1. Если вам не нужен DisplayObject, сразу удалитеChild вместо установки его свойства visible в false, иначе он будет участвовать в расчете на третьем шаге
2. Не создавайте никаких объектов в основном цикле, персонажи, монстры и эффекты умений в игре превращаются в пулы объектов.
3. Не делайте слишком много операций в событии EnterFrame, вы должны использовать некоторые события, которые можно настроить

Мы можем подсчитать количество игровых объектов, созданных с помощью следующей функции

Он показывает, что hashCount сравнивается с предыдущим hashCount каждую секунду.Этот hashCount используется внутренним API механизма egret для подсчета количества созданных объектов механизма. Если игра стоит на месте, то теоретически результат hashCount diff должен быть равен 0. На самом деле он должен максимально контролироваться ниже 120. Если он превышает стандарт, просто добавьте точку останова в конструктор HashObject движка и проверьте это во время выполнения Просто проверьте стек вызовов.

Посмотреть сведения о PerfDog:производительность dog.QQ.com/?AD tag=Modi…