Обзор измерения производительности приложений

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

Ключевые проблемы производительности

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

Задержка запуска

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

При разработке своих приложений для стартапов ставьте перед собой следующие цели:

  • Холодный запуск менее чем за 500 мс. Холодный запуск происходит, когда запускаемое приложение отсутствует в памяти системы. Это случается при первом запуске приложения после перезагрузки или после остановки процесса приложения пользователем или системой. Холодный запуск требует от системы наибольших усилий, поскольку ей необходимо загрузить все данные из памяти и инициализировать приложение. Постарайтесь, чтобы холодный запуск занимал 500 мс или меньше.
  • «Теплый старт» занимает менее 200 мс, а «горячий старт» — менее 150 мс. «Теплый старт» происходит, когда процесс приложения уже запущен в фоновом режиме, но системе необходимо повторно инициализировать пользовательский интерфейс или вернуть активность на передний план, например, когда пользователь закрывает приложение и вскоре открывает его снова. « Горячий старт» еще быстрее, поскольку активность приложения уже кэширована в памяти и ее нужно только перевести на передний план, без необходимости пересоздания иерархии представлений. Старайтесь, чтобы время «теплого старта» не превышало 200 мс, а «горячего старта» — 150 мс.
  • Задержки P95 и P99 очень близки к медианной задержке. P95 и P99 представляют собой 95-й и 99-й процентили времени запуска, в то время как медиана — это 50-й процентиль. Длительный запуск приложения ухудшает пользовательский опыт. Межпроцессное взаимодействие (IPC) и ненужные операции ввода-вывода во время критического пути запуска приложения могут привести к конфликтам блокировок и вызвать несоответствия.
Скролл-джанк

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

В ваших приложениях стремитесь к частоте обновления 90 Гц. Традиционная частота рендеринга составляет 60 Гц, но многие новые устройства работают в режиме 90 Гц во время взаимодействия с пользователем, например, при прокрутке. Некоторые устройства поддерживают даже более высокую частоту — до 120 Гц.

Чтобы увидеть, какую частоту обновления использует устройство в данный момент времени, включите наложение в разделе «Отладка» > «Параметры разработчика» > «Показать частоту обновления» .

Переходы, которые не проходят гладко

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

Неэффективность использования энергии

Выполнение работы снижает заряд батареи и, в конечном итоге, сокращает срок её службы.

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

Как выделение памяти, так и сборка мусора стали намного быстрее и эффективнее, особенно для временных объектов. Хотя раньше считалось лучшей практикой избегать выделения объектов, когда это возможно, мы рекомендуем делать то, что наиболее целесообразно для вашего приложения и архитектуры. Экономия на выделении памяти с риском создания неподдерживаемого кода — не лучшая практика, учитывая возможности ART.

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

Выявление проблем

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

  • Типичные сценарии запуска, в том числе из панели запуска и через уведомления.
  • Экраны, на которых пользователь прокручивает данные.
  • Переходы между экранами.
  • Длительные процессы, такие как навигация или воспроизведение музыки.

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

  • Perfetto : позволяет отслеживать происходящее на всем устройстве с помощью точных данных о времени.
  • Memory Profiler : позволяет увидеть, какие операции выделения памяти происходят в куче.
  • Simpleperf : отображает график распространения ошибки, показывающий, какие вызовы функций используют больше всего ресурсов ЦП в течение определенного периода времени. Если вы обнаружили в Systrace функцию, которая занимает много времени, но не знаете причину, Simpleperf может предоставить дополнительную информацию.

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

  • Стартап-потоки
  • Джанк
    • Полевые показатели
      • Основные показатели работы Play Console: в Play Console нельзя сузить метрики до конкретного пользовательского сценария. Она сообщает только об общих проблемах в работе приложения.
      • Пользовательские измерения с помощью FrameMetricsAggregator : вы можете использовать FrameMetricsAggregator для записи метрик задержки во время выполнения определенного рабочего процесса.
    • Лабораторные анализы
      • Прокрутка с помощью Macrobenchmark .
      • Macrobenchmark собирает данные о времени отрисовки кадров с помощью команд dumpsys gfxinfo , которые охватывают определенный пользовательский сценарий. Это позволяет понять вариации в задержках отрисовки на протяжении конкретного пользовательского сценария. Метрики RenderTime , которые показывают, сколько времени занимает отрисовка кадров, важнее, чем количество кадров с задержками, для выявления регрессий или улучшений.

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

  • Неправильные области действия фильтров намерений: добавляйте autoVerify в фильтры намерений только для тех URL-адресов, на которые ваше приложение может отвечать.
  • Непроверенные переключения протоколов: непроверенные перенаправления на стороне сервера и поддоменов считаются угрозой безопасности и приводят к сбою проверки. Они приводят к сбою всех ссылок autoVerify . Например, перенаправление ссылок с HTTP на HTTPS, таких как example.com на www.example.com, без проверки ссылок HTTPS может привести к сбою проверки. Обязательно проверяйте ссылки приложений , добавляя фильтры намерений.
  • Непроверяемые ссылки: добавление непроверяемых ссылок в целях тестирования может привести к тому, что система не проверит ссылки на приложения.
  • Ненадежные серверы: убедитесь, что ваши серверы могут подключаться к клиентским приложениям.

Настройте ваше приложение для анализа производительности.

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

Точки трассировки

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

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

Вопросы, касающиеся APK

Отладочные варианты могут быть полезны для устранения неполадок и символизации примеров стека, но они оказывают серьезное влияние на производительность. Устройства под управлением Android 10 (уровень API 29) и выше могут использовать profileable android:shell="true" в своем манифесте для включения профилирования в релизных сборках.

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

Компиляция

Скомпилируйте приложение на устройстве до известного состояния — как правило, для простоты speed или speed-profile , чтобы оно более точно соответствовало производительности в производственной среде (хотя это требует предварительного запуска приложения и сохранения профилей или компиляции базовых профилей приложения).

И speed , и speed-profile уменьшают объем кода, выполняемого интерпретируемым из dex, и, следовательно, объем фоновой компиляции just-in-time (JIT), которая может вызывать значительные помехи. Только speed-profile уменьшает влияние загрузки классов из dex во время выполнения.

Следующая команда компилирует приложение в режиме speed :

adb shell cmd package compile -m speed -f com.example.packagename

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

/data/misc/profiles/ref/[package-name]/primary.prof

Системные аспекты

Для проведения измерений низкого уровня и высокой точности откалибруйте свои устройства. Проведите A/B-сравнения на одном и том же устройстве и одной и той же версии ОС. Даже на устройствах одного типа могут наблюдаться значительные различия в производительности.

На устройствах с root-доступом рекомендуется использовать скрипт lockClocks для Microbenchmarks. Среди прочего, эти скрипты выполняют следующие действия:

  • Устанавливайте процессоры на фиксированную частоту.
  • Отключите маломощные ядра и настройте графический процессор.
  • Отключить терморегулирование.

Мы не рекомендуем использовать скрипт lockClocks для тестов, ориентированных на пользовательский опыт, таких как запуск приложения, тестирование DoU и тестирование на задержки, но он может быть необходим для снижения уровня шума в тестах Microbenchmark.

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

Медленный запуск приложения: ненужные действия, напоминающие батут.

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

alt_text Рисунок 1. График, демонстрирующий активность на батуте.

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

Ненужные выделения ресурсов приводят к частым сборкам мусора.

В Systrace вы можете заметить, что сборка мусора (GC) происходит чаще, чем вы ожидаете.

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

alt_text Рисунок 2. График, показывающий промежутки между событиями GC.

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

Некачественные рамки

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

Когда отрисовка кадров выполняется с минимальными усилиями со стороны приложения, точки трассировки Choreographer.doFrame() возникают с частотой 16,7 мс на устройстве с частотой 60 кадров в секунду:

alt_text Рисунок 3. График, демонстрирующий частое появление быстрых кадров.

Если уменьшить масштаб и просмотреть трассировку, иногда можно заметить, что обработка кадров занимает немного больше времени, но не более отведенных 16,7 мс. Эти кадры в порядке:

alt_text Рисунок 4. График, демонстрирующий частые быстрые кадры с периодическими всплесками работы.

Когда вы видите нарушение этого регулярного ритма, это означает некачественную структуру изображения, как показано на рисунках 5 и 6:

alt_text Рисунок 5. Траектория, демонстрирующая некачественный кадр.

alt_text Рисунок 6. График, демонстрирующий еще больше некачественных кадров.

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

Для получения дополнительной информации об оптимизации производительности Compose см. раздел «Производительность Jetpack Compose» . Для получения дополнительной информации об обнаружении проблем с отображением кадров и устранении причин их возникновения см. раздел «Медленная отрисовка» .

Распространенные ошибки ленивой компоновки

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

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

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

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

Отладьте ваше приложение

Ниже приведены методы отладки производительности вашего приложения.

Отладка запуска приложения с помощью Systrace

В разделе «Время запуска приложения» представлен обзор процесса запуска приложения, а в следующем видеоролике — обзор трассировки системы и использования профилировщика Android Studio.

Различить типы стартапов можно на следующих этапах:

  • Холодный запуск: начинается с создания нового процесса без сохранения состояния .
  • «Теплый запуск»: либо воссоздает активность с повторным использованием процесса, либо воссоздает процесс с сохраненным состоянием.
  • Горячий старт: перезапускает активность и начинает с инфляции.

Мы рекомендуем создавать трассировочные файлы Systrace с помощью приложения System Tracing на устройстве . Для Android 10 и выше используйте Perfetto . Для Android 9 и ниже используйте Systrace . Мы также рекомендуем просматривать трассировочные файлы с помощью веб-просмотрщика трассировок Perfetto . Для получения дополнительной информации см. раздел «Обзор трассировки системы» .

Следует обратить внимание на следующие моменты:

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

  • Одновременная сборка мусора: это распространенная проблема, оказывающая относительно небольшое влияние, но если вы сталкиваетесь с ней часто, стоит изучить ее с помощью профилировщика памяти Android Studio.

  • Ввод-вывод: проверьте операции ввода-вывода, выполняемые во время запуска, и найдите длительные задержки.

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

Для улучшения отчетности по метрикам запуска приложения мы рекомендуем вызывать reportFullyDrawn после завершения запуска с точки зрения приложения. Дополнительную информацию об использовании reportFullyDrawn см. в разделе «Время до полного отображения» . Вы можете извлечь время запуска, определенное в RFD, с помощью процессора трассировки Perfetto, и будет сгенерировано видимое пользователю событие трассировки.

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

Для записи трассировки системы на устройстве можно использовать системное приложение System Tracing. Это приложение позволяет записывать трассировку с устройства без необходимости его подключения к сети или через adb .

Используйте профилировщик памяти Android Studio.

С помощью инструмента Android Studio Memory Profiler можно проверить нехватку памяти, которая может быть вызвана утечками памяти или некорректным использованием. Он предоставляет информацию о выделении памяти для объектов в режиме реального времени.

Вы можете устранить проблемы с памятью в своем приложении, используя профилировщик памяти, чтобы отслеживать причины и частоту выполнения сборок мусора.

Для профилирования памяти приложения выполните следующие действия:

  1. Выявление проблем с памятью.

    Запишите сеанс профилирования памяти для пользовательского сценария, на котором вы хотите сосредоточиться. Обратите внимание на увеличение количества объектов, как показано на рисунке 7, что в конечном итоге приводит к сборке мусора, как показано на рисунке 8.

    alt_text Рисунок 7. Увеличение количества объектов.

    alt_text Рисунок 8. Сбор мусора.

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

  2. Диагностика очагов перегрузки памяти.

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

    alt_text Рисунок 9. Значения для распределения и размера неглубокого участка.

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

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

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

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

    • Небольшой размер : отслеживает только память самого объекта. Это полезно для отслеживания простых классов, состоящих в основном из примитивных типов данных.

    • Остаточный размер : показывает общий объем памяти, выделенный самим объектом, плюс любые ссылки, на которые ссылается только этот объект. Это полезно для отслеживания нагрузки на память, вызванной сложными объектами. Чтобы получить это значение, сделайте полный дамп памяти, как показано на рисунке 10. Остаточный размер добавляется в качестве столбца, как показано на рисунке 11.

      alt_text Рисунок 10. Полный дамп памяти.

      Столбец «Сохраненный размер».
      Рисунок 11. Столбец «Сохраненный размер».
  3. Оцените влияние оптимизации.

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

    Для оценки влияния оптимизации на временную шкалу профилировщика измерьте время между циклами сборки мусора. Положительное влияние проявляется в увеличении времени между циклами сборки мусора.

    В конечном итоге, результаты улучшения памяти следующие:

    • Вероятность сбоев из-за нехватки памяти значительно снизится, если приложение не будет постоянно испытывать нехватку памяти.
    • Меньшее количество сборщиков мусора улучшает показатели задержки, особенно в P99. Это связано с тем, что сборщики мусора вызывают конкуренцию за ресурсы ЦП, что может привести к отсрочке задач рендеринга на время их выполнения.
{% verbatim %} {% endverbatim %} {% verbatim %} {% endverbatim %}