Настройка трассировки системы

Вы можете настроить трассировку системы для захвата профиля ЦП и потоков вашего приложения за короткий период времени. Затем вы можете использовать выходной отчет из системной трассировки, чтобы улучшить производительность вашей игры.

Настройте трассировку системы на основе игры

Инструмент Systrace доступен двумя способами:

Systrace — это инструмент низкого уровня, который:

  • Предоставляет основную правду . Systrace захватывает выходные данные непосредственно из ядра, поэтому собираемые им показатели практически идентичны тем, которые сообщают серии системных вызовов.
  • Потребляет мало ресурсов . Systrace требует очень низких затрат на устройство, обычно менее 1%, поскольку передает данные в потоковом режиме в буфер в памяти.

Оптимальные настройки

Важно предоставить инструменту разумный набор аргументов:

  • Категории: лучший набор категорий, который можно включить для трассировки системы на основе игры: { sched , freq , idle , am , wm , gfx , view , sync , binder_driver , hal , dalvik }.
  • Размер буфера. Общее правило заключается в том, что размер буфера в 10 МБ на ядро ​​ЦП позволяет отслеживать длительность около 20 секунд. Например, если устройство имеет два четырехъядерных процессора (всего 8 ядер), подходящее значение для передачи в программу systrace — 80 000 КБ (80 МБ).

    Если ваша игра выполняет большое количество переключений контекста, увеличьте буфер до 15 МБ на ядро ​​ЦП.

  • Пользовательские события: если вы определяете пользовательские события для захвата в своей игре, включите флаг -a , который позволит Systrace включать эти пользовательские события в выходной отчет.

Если вы используете программу командной строки systrace , используйте следующую команду, чтобы записать системную трассировку, которая применяет лучшие практики для набора категорий, размера буфера и пользовательских событий:

python systrace.py -a com.example.myapp -b 80000 -o my_systrace_report.html \
  sched freq idle am wm gfx view sync binder_driver hal dalvik

Если вы используете системное приложение Systrace на устройстве, выполните следующие шаги, чтобы записать системную трассировку, которая применяет лучшие практики для набора категорий, размера буфера и пользовательских событий:

  1. Включите опцию Отслеживать отлаживаемые приложения .

    Чтобы использовать этот параметр, на устройстве должно быть доступно 256 МБ или 512 МБ (в зависимости от того, имеет ли ЦП 4 или 8 ядер), а каждый фрагмент памяти объемом 64 МБ должен быть доступен как непрерывный фрагмент.

  2. Выберите «Категории» , затем включите категории из следующего списка:

    • am : Менеджер активности
    • binder_driver : Драйвер ядра Binder.
    • dalvik : Далвик ВМ
    • freq : частота процессора
    • gfx : Графика
    • hal : Аппаратные модули
    • idle : процессор простаивает
    • sched : Планирование ЦП
    • sync : Синхронизация
    • view : Система просмотра
    • wm : Оконный менеджер
  3. Включите отслеживание записей .

  4. Загрузите игру.

  5. Выполняйте взаимодействия в игре, соответствующие игровому процессу, производительность устройства которого вы хотите измерить.

  6. Вскоре после того, как вы обнаружите нежелательное поведение в игре, отключите системную трассировку.

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

Чтобы сэкономить место на диске, системные трассировки на устройстве сохраняют файлы в сжатом формате трассировки ( *.ctrace ). Чтобы распаковать этот файл при создании отчета, используйте программу командной строки и включите параметр --from-file :

python systrace.py --from-file=/data/local/traces/my_game_trace.ctrace \
  -o my_systrace_report.html

Улучшить конкретные области производительности

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

Скорость загрузки

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

  • Выполните отложенную загрузку . Если вы используете одни и те же ресурсы в последовательных сценах или уровнях игры, загружайте эти ресурсы только один раз.
  • Уменьшите размер ваших активов. Таким образом, вы можете объединить несжатые версии этих ресурсов с APK-файлом вашей игры.
  • Используйте метод сжатия, эффективный для диска. Примером такого метода является zlib .
  • Используйте IL2CPP вместо mono . (Применяется только в том случае, если вы используете Unity.) IL2CPP обеспечивает более высокую производительность выполнения ваших сценариев C#.
  • Сделайте свою игру многопоточной. Более подробную информацию см. в разделе «Консистентность частоты кадров» .

Согласованность частоты кадров

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

Многопоточность

При разработке для нескольких платформ вполне естественно размещать все действия в игре в одном потоке. Хотя этот метод выполнения легко реализовать во многих игровых движках, он далеко не оптимален при работе на устройствах Android. В результате однопоточные игры часто загружаются медленно и не имеют постоянной частоты кадров.

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

Схема потоков в системной трассировке

Рисунок 1. Отчет Systrace для однопоточной игры

Чтобы повысить производительность вашей игры, сделайте ее многопоточной . Обычно лучшая модель — иметь два потока:

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

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

Вы также можете внести некоторые изменения, специфичные для движка, чтобы повысить производительность многопоточности вашей игры:

  • Если вы разрабатываете игру с использованием игрового движка Unity, включите параметры многопоточного рендеринга и скининга графического процессора .
  • Если вы используете собственный механизм рендеринга, убедитесь, что конвейер команд рендеринга и конвейер графических команд выровнены правильно; в противном случае вы можете внести задержки в отображение сцен вашей игры.

После применения этих изменений вы должны увидеть, что ваша игра одновременно использует как минимум 2 процессора, как показано на рисунке 2:

Схема потоков в системной трассировке

Рисунок 2. Отчет Systrace для многопоточной игры

Загрузка элемента пользовательского интерфейса

Схема стека кадров в системной трассировке
Рисунок 3. Отчет Systrace для игры, которая одновременно отображает десятки элементов пользовательского интерфейса.

При создании многофункциональной игры возникает соблазн показать игроку множество различных опций и действий одновременно. Однако для поддержания постоянной частоты кадров важно учитывать относительно небольшой размер мобильных дисплеев и делать пользовательский интерфейс максимально простым.

Отчет Systrace, показанный на рисунке 3, является примером кадра пользовательского интерфейса, который пытается отобразить слишком много элементов по сравнению с возможностями мобильного устройства.

Хорошая цель — сократить время обновления пользовательского интерфейса до 2-3 миллисекунд . Вы можете добиться таких быстрых обновлений, выполнив оптимизацию, подобную следующей:

  • Обновите только те элементы на экране, которые были перемещены.
  • Ограничьте количество текстур и слоев пользовательского интерфейса. Рассмотрите возможность объединения графических вызовов, таких как шейдеры и текстуры, использующих один и тот же материал.
  • Перенесите операции анимации элементов на графический процессор.
  • Выполните более агрессивное отсечение усеченных структур и окклюзий.
  • Если возможно, выполняйте операции рисования с помощью API Vulkan. Накладные расходы на вызовы отрисовки в Vulkan ниже.

Потребляемая мощность

Даже после оптимизации, описанной в предыдущем разделе, вы можете обнаружить, что частота кадров в вашей игре снижается в течение первых 45–50 минут игрового процесса. Кроме того, со временем устройство может начать нагреваться и потреблять больше заряда батареи.

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

Храните потоки с интенсивным использованием памяти на одном процессоре

На многих мобильных устройствах кэши L1 расположены на определенных процессорах, а кэши L2 — на наборе процессоров, которые используют одну и ту же тактовую частоту. Чтобы максимально увеличить количество обращений к кэшу L1, обычно лучше всего, чтобы основной поток вашей игры вместе с любыми другими потоками, требующими большого объема памяти, работал на одном процессоре.

Отложите краткосрочную работу на процессоры с меньшей мощностью.

Большинство игровых движков, включая Unity, умеют переносить операции рабочего потока на другой процессор относительно основного потока вашей игры. Однако движок не знает конкретной архитектуры устройства и не может предугадать рабочую нагрузку вашей игры так хорошо, как вы.

Большинство устройств типа «система-на-кристалле» имеют как минимум две общие тактовые частоты: одну для быстрых ЦП устройства, а другую для медленных ЦП устройства. Следствием этой архитектуры является то, что если один быстрый процессор должен работать на максимальной скорости, все остальные быстрые процессоры также будут работать на максимальной скорости.

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

Схема потоков в системной трассировке

Рисунок 4. Отчет Systrace, показывающий неоптимальное назначение потоков процессорам устройства.

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

На большинстве устройств медленные процессоры указываются перед быстрыми процессорами, но вы не можете предполагать, что SOC вашего устройства использует этот порядок. Чтобы проверить, запустите команды, аналогичные тем, которые показаны в этом коде обнаружения топологии ЦП на GitHub.

После того, как вы узнаете, какие процессоры на вашем устройстве являются медленными, вы можете объявить сходство для своих потоков с небольшой продолжительностью, чему будет следовать планировщик устройства. Для этого добавьте следующий код в каждый поток:

#include <sched.h>
#include <sys/types.h>
#include <unistd.h>

pid_t my_pid; // PID of the process containing your thread.

// Assumes that cpu0, cpu1, cpu2, and cpu3 are the "slow CPUs".
cpu_set_t my_cpu_set;
CPU_ZERO(&my_cpu_set);
CPU_SET(0, &my_cpu_set);
CPU_SET(1, &my_cpu_set);
CPU_SET(2, &my_cpu_set);
CPU_SET(3, &my_cpu_set);
sched_setaffinity(my_pid, sizeof(cpu_set_t), &my_cpu_set);

Термический стресс

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

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

Сначала объявите объект PowerManager и инициализируйте его в методе onCreate() . Добавьте к объекту прослушиватель теплового состояния.

Котлин

class MainActivity : AppCompatActivity() {
    lateinit var powerManager: PowerManager

    override fun onCreate(savedInstanceState: Bundle?) {
        powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
        powerManager.addThermalStatusListener(thermalListener)
    }
}

Ява

public class MainActivity extends AppCompatActivity {
    PowerManager powerManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
        powerManager.addThermalStatusListener(thermalListener);
    }
}

Определите действия, которые необходимо предпринять, когда прослушиватель обнаружит изменение статуса. Если ваша игра использует C/C++, добавьте код к уровням теплового статуса в onThermalStatusChanged() , чтобы вызывать собственный код игры с помощью JNI , или используйте собственный Thermal API .

Котлин

val thermalListener = object : PowerManager.OnThermalStatusChangedListener() {
    override fun onThermalStatusChanged(status: Int) {
        when (status) {
            PowerManager.THERMAL_STATUS_NONE -> {
                // No thermal status, so no action necessary
            }

            PowerManager.THERMAL_STATUS_LIGHT -> {
                // Add code to handle light thermal increase
            }

            PowerManager.THERMAL_STATUS_MODERATE -> {
                // Add code to handle moderate thermal increase
            }

            PowerManager.THERMAL_STATUS_SEVERE -> {
                // Add code to handle severe thermal increase
            }

            PowerManager.THERMAL_STATUS_CRITICAL -> {
                // Add code to handle critical thermal increase
            }

            PowerManager.THERMAL_STATUS_EMERGENCY -> {
                // Add code to handle emergency thermal increase
            }

            PowerManager.THERMAL_STATUS_SHUTDOWN -> {
                // Add code to handle immediate shutdown
            }
        }
    }
}

Ява

PowerManager.OnThermalStatusChangedListener thermalListener =
    new PowerManager.OnThermalStatusChangedListener () {

    @Override
    public void onThermalStatusChanged(int status) {

        switch (status)
        {
            case PowerManager.THERMAL_STATUS_NONE:
                // No thermal status, so no action necessary
                break;

            case PowerManager.THERMAL_STATUS_LIGHT:
                // Add code to handle light thermal increase
                break;

            case PowerManager.THERMAL_STATUS_MODERATE:
                // Add code to handle moderate thermal increase
                break;

            case PowerManager.THERMAL_STATUS_SEVERE:
                // Add code to handle severe thermal increase
                break;

            case PowerManager.THERMAL_STATUS_CRITICAL:
                // Add code to handle critical thermal increase
                break;

            case PowerManager.THERMAL_STATUS_EMERGENCY:
                // Add code to handle emergency thermal increase
                break;

            case PowerManager.THERMAL_STATUS_SHUTDOWN:
                // Add code to handle immediate shutdown
                break;
        }
    }
};

Задержка касания дисплея

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

Чтобы определить, можно ли улучшить частоту кадров в игре, выполните следующие действия:

  1. Создайте отчет Systrace, включающий категории gfx и input . Эти категории включают особенно полезные измерения для определения задержки касания дисплея.
  2. Проверьте раздел SurfaceView отчета Systrace. Переполненный буфер приводит к тому, что количество ожидающих отрисовок буфера колеблется от 1 до 2, как показано на рисунке 5:

    Схема буферной очереди в системной трассировке

    Рисунок 5. Отчет Systrace, показывающий переполненный буфер, который периодически оказывается слишком заполнен для приема команд рисования.

Чтобы устранить это несоответствие в темпе кадров, выполните действия, описанные в следующих разделах:

Интегрируйте Android Frame Pacing API в свою игру.

Android Frame Pacing API помогает выполнять замену кадров и определять интервал замены, чтобы ваша игра поддерживала более постоянную частоту кадров.

Уменьшите разрешение ресурсов вашей игры, не связанных с пользовательским интерфейсом.

Дисплеи на современных мобильных устройствах содержат гораздо больше пикселей, чем может обработать игрок, поэтому можно уменьшить разрешение, чтобы серия из 5 или даже 10 пикселей содержала один цвет. Учитывая структуру большинства кэшей дисплея, лучше всего уменьшать разрешение только по одному измерению .

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

Плавность рендеринга

Когда SurfaceFlinger фиксируется на буфере дисплея, чтобы показать сцену в вашей игре, активность ЦП на мгновение увеличивается. Если эти всплески активности ЦП происходят неравномерно, в вашей игре могут наблюдаться подтормаживания. Диаграмма на рисунке 6 изображает причину, по которой это происходит:

Диаграмма кадров, в которых отсутствует окно вертикальной синхронизации, поскольку они начали рисовать слишком поздно

Рисунок 6. Отчет Systrace, показывающий, как кадр может пропустить Vsync.

Если кадр начнет отрисовываться слишком поздно, даже на несколько миллисекунд, он может пропустить следующее окно отображения. Затем кадр должен дождаться отображения следующей вертикальной синхронизации (33 миллисекунды при запуске игры со скоростью 30 кадров в секунду), что вызывает заметную задержку с точки зрения игрока.

Чтобы решить эту ситуацию, используйте Android Frame Pacing API , который всегда представляет новый кадр на волновом фронте VSync.

Состояние памяти

При запуске игры в течение длительного периода времени на устройстве могут возникать ошибки нехватки памяти.

В этой ситуации проверьте активность ЦП в отчете Systrace и посмотрите, как часто система выполняет вызовы демона kswapd . Если во время выполнения вашей игры выполняется много вызовов, лучше внимательно посмотреть, как ваша игра управляет памятью и очищает ее.

Дополнительную информацию см. в разделе «Эффективное управление памятью в играх» .

Состояние потока

При навигации по типичным элементам отчета Systrace вы можете просмотреть количество времени, которое данный поток провел в каждом возможном состоянии потока , выбрав поток в отчете, как показано на рисунке 7:

Схема отчета Systrace

Рис. 7. Отчет Systrace, показывающий, как выбор потока приводит к отображению в отчете сводной информации о состоянии этого потока.

Как показано на рис. 7, вы можете обнаружить, что потоки вашей игры не находятся в состоянии «работает» или «работает» так часто, как следовало бы. В следующем списке показано несколько распространенных причин, по которым данный поток может периодически переходить в необычное состояние:

  • Если поток находится в режиме ожидания в течение длительного периода времени, возможно, он страдает либо от конфликта блокировок, либо от ожидания активности графического процессора.
  • Если поток постоянно блокируется при вводе-выводе, вы либо считываете слишком много данных с диска за раз, либо ваша игра тормозит.

Дополнительные ресурсы

Чтобы узнать больше об улучшении производительности игры, посетите следующие дополнительные ресурсы:

Видео