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

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

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

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

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

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

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

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

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

  • Задержки P95 и P99 очень близки к медианной задержке. Когда приложение долго запускается, это ухудшает пользовательский опыт. Межпроцессные коммуникации (IPC) и ненужный ввод-вывод на критическом пути запуска приложения могут вызывать конфликты блокировок и приводить к несоответствиям.

Прокрутка jank

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

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

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

Переходы, которые не являются плавными

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

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

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

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

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

Определить проблемы

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

  1. Определите и проверьте следующие критические пути пользователя:
    • Обычные процессы запуска, в том числе из панели запуска и уведомлений.
    • Экраны, на которых пользователь прокручивает данные.
    • Переходы между экранами.
    • Длительные процессы, такие как навигация или воспроизведение музыки.
  2. Проверьте, что происходит во время предыдущих потоков, используя следующие инструменты отладки:
    • Perfetto : позволяет вам видеть, что происходит на всем устройстве, с точными данными о времени.
    • Профилировщик памяти : позволяет увидеть, какие выделения памяти происходят в куче.
    • Simpleperf : показывает график функций, которые используют больше всего ресурсов процессора в течение определенного периода времени. Когда вы определяете что-то, что занимает много времени в Systrace, но не знаете почему, Simpleperf может предоставить дополнительную информацию.

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

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

App Links — это глубокие ссылки, основанные на URL вашего веб-сайта, которые проверены на принадлежность к вашему веб-сайту. Ниже приведены причины, по которым проверки App Link могут не пройти.

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

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

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

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

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

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

АПК-соображения

Варианты отладки могут быть полезны для устранения неполадок и символизации образцов стека, но они оказывают серьезное влияние на производительность. Устройства под управлением 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. След, показывающий активность батута.

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

Ненужные распределения, вызывающие частые GC

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

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

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

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

Рамки Janky

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

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

alt_text Рисунок 3. Траектория, показывающая частые быстрые кадры.

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

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

Если вы видите нарушение этого регулярного ритма, то это дергающийся кадр, как показано на рисунке 5:

alt_text Рисунок 5. След, показывающий дергающийся кадр.

Вы можете попрактиковаться в их распознавании.

alt_text Рисунок 6. Траектория, показывающая большее количество неровных кадров.

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

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

Распространенные ошибки RecyclerView

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

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

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

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

Отладка вашего приложения

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

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

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

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

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

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

Вот некоторые вещи, на которые следует обратить внимание:

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

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

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

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

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

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

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

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

Вы можете использовать Android Studio Memory Profiler для проверки давления памяти, которое может быть вызвано утечками памяти или плохими шаблонами использования. Он обеспечивает живой просмотр распределения объектов.

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

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

  1. Обнаружение проблем с памятью.

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

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

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

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

  2. Диагностируйте очаги давления в памяти.

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

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

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

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

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

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

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

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

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

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

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

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

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

    • Вероятность сбоев из-за нехватки памяти снизится, если приложение не будет постоянно сталкиваться с нехваткой памяти.
    • Меньшее количество GC улучшает показатели jank, особенно в P99. Это связано с тем, что GC вызывают конфликт CPU, что может привести к отсрочке задач рендеринга во время выполнения GC.
{% дословно %} {% endverbatim %} {% дословно %} {% endverbatim %}