Практический пример: как команда Gmail Wear OS улучшила запуск своего приложения на 50 %, Практический пример: как команда Gmail Wear OS улучшила запуск своего приложения на 50 %

Запуск приложения представляет собой первое впечатление вашего приложения на пользователей. А пользователи не любят ждать, поэтому вам нужно убедиться, что ваше приложение запускается быстро. Чтобы показать вам, как реальная команда разработчиков приложений обнаружила и диагностировала проблемы при запуске своего приложения, вот что сделала команда Gmail Wear OS.

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

Запишите трассировку и посмотрите на запуск приложения

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

Рисунок 1. Первоначальный вид трассировки в Perfetto.

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

Строка «Запуски приложений Android» с возможностью закрепления выделена.
Рисунок 2. Закрепите специальный показатель «Запуски приложений Android» в верхней части панели управления для упрощения анализа.

Обратите внимание, что метрика «Запуски приложений Android» представляет время первого отображения , даже если вы используете reportFullyDrawn() . Чтобы определить время полного отображения , найдите reportFullyDrawn() в поле поиска Perfetto.

Проверьте основную тему

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

Чтобы найти основной поток, найдите строку с именем пакета вашего приложения и разверните ее. Две строки с тем же именем, что и пакет (обычно первые две строки в разделе), представляют основной поток. Из двух строк основных потоков первая представляет состояние процессора, а вторая строка представляет точки трассировки. Закрепите две основные строки цепочки под показателем «Запуски приложений для Android» .

Запуски приложений Android и основные строки цепочек закреплены.
Рис. 3. Закрепите строки основного потока под пользовательской метрикой «Запуски приложений для Android», чтобы облегчить анализ.

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

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

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

На панели «Состояния потока» выделен основной поток с общим временем нахождения в разных состояниях.
Рис. 4. Оцените относительное время от состояний Runnable до Running чтобы получить первоначальное представление о степени конкуренции за процессор.

Чем выше отношение времени в состоянии Runnable к времени в состоянии Running , тем больше вероятность возникновения конфликта ЦП. При проверке проблем с производительностью таким способом сначала сосредоточьтесь на самом длинном кадре и работайте над меньшими.

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

Время, проведенное в OpenDexFilesFromOat*

Теперь проверьте время, проведенное в OpenDexFilesFromOat* ; в трассировке это происходит одновременно с bindApplication . Этот срез представляет время, необходимое для чтения файлов DEX приложения.

Заблокированные транзакции связывания

Затем проверьте транзакции связывателя. Транзакции связывания представляют собой вызовы между клиентом и сервером: в этом случае приложение (клиент) вызывает систему Android (сервер) с binder transaction , а сервер отвечает binder reply . Убедитесь, что приложение не выполняет ненужные транзакции связывания во время запуска, поскольку они увеличивают риск конфликта ЦП. Если есть возможность, отложите работу, включающую вызовы связующих, на после периода запуска приложения. Если вам необходимо выполнить транзакции с привязкой, убедитесь, что они не занимают больше времени, чем частота обновления Vsync вашего устройства.

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

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

    Линия соединяет вызов связующего и ответ.
    Рисунок 5. Определите транзакции связывания, происходящие во время запуска приложения, и оцените, можете ли вы их отложить.
  3. Чтобы увидеть, как системный сервер обрабатывает эту транзакцию связывания, закрепите потоки ЦП 0 и ЦП 1 в верхней части экрана.

  4. Найдите системные процессы, которые обрабатывают ответ связывателя, найдя фрагменты, которые включают имя потока ответа связывателя, в данном случае «Binder: 687_11 [2542]». Щелкните соответствующие системные процессы, чтобы получить дополнительную информацию о транзакции связывания.

Взгляните на этот системный процесс, связанный с интересующей транзакцией связывания, которая происходит на ЦП 0:

Системный процесс с конечным состоянием «Выполняемый (вытесненный).
Рис. 6. Системный процесс находится в состоянии Runnable (Preempted) , что указывает на задержку его выполнения.

Конечное состояние говорит Runnable (Preempted) , что означает, что процесс задерживается, потому что процессор делает что-то еще. Чтобы узнать, что его вытесняет, разверните строки Ftrace Events . На открывшейся вкладке «События Ftrace» прокрутите список и найдите события, связанные с интересующим потоком связующих «Binder:687_11 [2542]». Примерно в то время, когда системный процесс вытесняется, произошли два события системного сервера, включающие аргумент «decon», что означает, что они связаны с контроллером дисплея. Это звучит разумно, поскольку контроллер дисплея выводит кадры на экран – а это важная задача! Тогда оставьте события как есть.

События FTrace, связанные с интересующей транзакцией связывания, выделены.
Рис. 7. События FTrace указывают на то, что транзакция связывателя задерживается из-за событий контроллера дисплея.

JIT-деятельность

Чтобы исследовать активность JIT-компиляции , разверните процессы, принадлежащие вашему приложению, найдите две строки «Пул потоков Jit» и закрепите их в верхней части экрана. Поскольку это приложение использует базовые профили во время запуска приложения, до тех пор, пока не будет отрисован первый кадр, происходит очень небольшая активность JIT, о чем свидетельствует конец первого вызова Choreographer.doFrame . Однако обратите внимание на причину медленного запуска JIT compiling void , которая предполагает, что системная активность, происходящая во время точки трассировки, помеченной как Application creation вызывает большое количество фоновой JIT-активности. Чтобы решить эту проблему, добавьте события, которые происходят вскоре после отрисовки первого кадра, в базовый профиль, расширив коллекцию профилей до момента, когда приложение готово к использованию. Во многих случаях это можно сделать, добавив строку в конец теста Macrobenchmark коллекции базовых профилей, который ожидает появления определенного виджета пользовательского интерфейса на вашем экране, указывая, что экран полностью заполнен.

Пулы потоков Jit с выделенным фрагментом «Jit compiling void».
Рисунок 8. Если вы видите много JIT-активности, расширьте свой базовый профиль до момента, когда приложение будет готово к использованию.

Результаты

В результате этого анализа команда Gmail Wear OS внесла следующие улучшения:

  • Поскольку они заметили конфликты во время запуска приложения при просмотре активности ЦП , они заменили анимацию счетчика, используемую для указания того, что приложение загружается, одним статическим изображением. Они также продлили заставку, чтобы отложить состояние мерцания (второе состояние экрана, используемое для обозначения загрузки приложения) и освободить ресурсы ЦП. Это улучшило задержку запуска приложения на 50%.
  • Судя по времени, потраченному на OpenDexFilesFromOat* и активности JIT , они позволили R8 перезаписать базовые профили . Это улучшило задержку запуска приложения на 20%.

Вот несколько советов от команды о том, как эффективно анализировать производительность приложения:

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

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

,

Запуск приложения представляет собой первое впечатление вашего приложения на пользователей. А пользователи не любят ждать, поэтому вам нужно убедиться, что ваше приложение запускается быстро. Чтобы показать вам, как реальная команда разработчиков приложений обнаружила и диагностировала проблемы при запуске своего приложения, вот что сделала команда Gmail Wear OS.

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

Запишите трассировку и посмотрите на запуск приложения

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

Рисунок 1. Первоначальный вид трассировки в Perfetto.

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

Строка «Запуски приложений Android» с возможностью закрепления выделена.
Рисунок 2. Закрепите специальный показатель «Запуски приложений Android» в верхней части панели управления для упрощения анализа.

Обратите внимание, что метрика «Запуски приложений Android» представляет время до первого отображения , даже если вы используете reportFullyDrawn() . Чтобы определить время полного отображения , найдите reportFullyDrawn() в поле поиска Perfetto.

Проверьте основную тему

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

Чтобы найти основной поток, найдите строку с именем пакета вашего приложения и разверните ее. Две строки с тем же именем, что и пакет (обычно первые две строки в разделе), представляют основной поток. Из двух строк основных потоков первая представляет состояние процессора, а вторая строка представляет точки трассировки. Закрепите две основные строки цепочек под показателем «Запуски приложений для Android» .

Запуски приложений Android и основные строки цепочек закреплены.
Рисунок 3. Закрепите строки основного потока под пользовательской метрикой «Запуски приложений для Android», чтобы облегчить анализ.

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

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

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

На панели «Состояния потока» выделен основной поток с общим временем нахождения в разных состояниях.
Рис. 4. Оцените относительное время от состояний Runnable до Running чтобы получить первоначальное представление о степени конкуренции за процессор.

Чем выше отношение времени в состоянии Runnable к времени в состоянии Running , тем больше вероятность возникновения конфликта ЦП. При проверке проблем с производительностью таким способом сначала сосредоточьтесь на самом длинном кадре и работайте над меньшими.

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

Время, проведенное в OpenDexFilesFromOat*

Теперь проверьте время, проведенное в OpenDexFilesFromOat* ; в трассировке это происходит одновременно с bindApplication . Этот срез представляет время, необходимое для чтения файлов DEX приложения.

Заблокированные транзакции связывания

Затем проверьте транзакции связывателя. Транзакции связывания представляют собой вызовы между клиентом и сервером: в этом случае приложение (клиент) вызывает систему Android (сервер) с binder transaction , а сервер отвечает binder reply . Убедитесь, что приложение не выполняет ненужные транзакции связывания во время запуска, поскольку они увеличивают риск конфликта ЦП. Если есть возможность, отложите работу, включающую вызовы связующих, после периода запуска приложения. Если вам необходимо выполнить транзакции с привязкой, убедитесь, что они не занимают больше времени, чем частота обновления Vsync вашего устройства.

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

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

    Линия соединяет вызов связующего и ответ.
    Рисунок 5. Определите транзакции связывания, происходящие во время запуска приложения, и оцените, можете ли вы их отложить.
  3. Чтобы увидеть, как системный сервер обрабатывает эту транзакцию связывания, закрепите потоки ЦП 0 и ЦП 1 в верхней части экрана.

  4. Найдите системные процессы, которые обрабатывают ответ связывателя, найдя фрагменты, которые включают имя потока ответа связывателя, в данном случае «Binder: 687_11 [2542]». Щелкните соответствующие системные процессы, чтобы получить дополнительную информацию о транзакции связывания.

Взгляните на этот системный процесс, связанный с интересующей транзакцией связывания, которая происходит на ЦП 0:

Системный процесс с конечным состоянием «Выполняемый (вытесненный).
Рис. 6. Системный процесс находится в состоянии Runnable (Preempted) , что указывает на задержку его выполнения.

В конечном состоянии указано Runnable (Preempted) , что означает, что процесс задерживается, потому что процессор делает что-то еще. Чтобы узнать, что его вытесняет, разверните строки Ftrace Events . На открывшейся вкладке «События Ftrace» прокрутите список и найдите события, связанные с интересующим потоком связующих «Binder:687_11 [2542]». Примерно в то время, когда системный процесс вытесняется, произошли два события системного сервера, включающие аргумент «decon», что означает, что они связаны с контроллером дисплея. Это звучит разумно, поскольку контроллер дисплея выводит кадры на экран – а это важная задача! Тогда оставьте события как есть.

События FTrace, связанные с интересующей транзакцией связывания, выделены.
Рис. 7. События FTrace указывают на то, что транзакция связывателя задерживается из-за событий контроллера дисплея.

JIT-деятельность

Чтобы исследовать активность JIT-компиляции , разверните процессы, принадлежащие вашему приложению, найдите две строки «Пул потоков Jit» и закрепите их в верхней части экрана. Поскольку это приложение использует базовые профили во время запуска приложения, до тех пор, пока не будет отрисован первый кадр, происходит очень небольшая активность JIT, о чем свидетельствует конец первого вызова Choreographer.doFrame . Однако обратите внимание на причину медленного запуска JIT compiling void , которая предполагает, что системная активность, происходящая во время точки трассировки, помеченной как Application creation вызывает большое количество фоновой JIT-активности. Чтобы решить эту проблему, добавьте события, которые происходят вскоре после отрисовки первого кадра, в базовый профиль, расширив коллекцию профилей до момента, когда приложение готово к использованию. Во многих случаях это можно сделать, добавив строку в конец теста Macrobenchmark коллекции базовых профилей, который ожидает появления определенного виджета пользовательского интерфейса на вашем экране, указывая, что экран полностью заполнен.

Пулы потоков Jit с выделенным фрагментом «Jit compiling void».
Рисунок 8. Если вы видите много JIT-активности, расширьте свой базовый профиль до момента, когда приложение будет готово к использованию.

Результаты

В результате этого анализа команда Gmail Wear OS внесла следующие улучшения:

  • Поскольку они заметили конфликты во время запуска приложения при просмотре активности ЦП , они заменили анимацию счетчика, используемую для указания того, что приложение загружается, одним статическим изображением. Они также продлили заставку, чтобы отложить состояние мерцания (второе состояние экрана, используемое для обозначения загрузки приложения) и освободить ресурсы ЦП. Это улучшило задержку запуска приложения на 50%.
  • Судя по времени, потраченному на OpenDexFilesFromOat* и активности JIT , они позволили R8 перезаписать базовые профили . Это улучшило задержку запуска приложения на 20%.

Вот несколько советов от команды о том, как эффективно анализировать производительность приложения:

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

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