Измерение производительности
Профилировщики, трассировки и тесты производительности
«Измерение производительности» — бесплатный урок Android Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Android Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Android Academy содержит 4 уроков всего.
Измеряйте перед оптимизацией
Золотое правило работы с производительностью: сначала измеряйте, никогда не гадайте. Человеческая интуиция почти всегда ошибается в оценке того, что работает медленно.
В этом уроке вы познакомитесь с инструментами Android, которые позволяют наблюдать за производительностью: профилировщиками, системными трассировками и микротестами производительности. Когда вы умеете измерять, каждая оптимизация становится решением на основе данных, а не догадкой.
- Профилировщики показывают загрузку CPU, использование памяти и энергии в реальном времени.
- Системные трассировки точно показывают, на что уходит время каждого кадра.
- Тесты производительности дают воспроизводимые числа, которые можно сравнивать между сборками.
Бюджет кадра 16 мс
На экране с частотой 60 Гц система отображает новый кадр каждые 16,67 мс. Если приложение не успевает подготовить кадр за это время, кадр пропускается, и пользователи замечают подёргивания. На устройствах с частотой 120 Гц бюджет сокращается примерно до 8 мс.
Оптимизация производительности на самом деле сводится к тому, чтобы укладываться в этот бюджет. Комментарий ниже показывает вычисление, которое нужно держать в уме.
// Frame budget math
// 60 Hz -> 1000ms / 60 = 16.67ms per frame
// 90 Hz -> 1000ms / 90 = 11.11ms per frame
// 120 Hz -> 1000ms / 120 = 8.33ms per frame
//
// Exceed the budget on the UI thread = a dropped frame = visible jank.
fun frameBudgetMs(refreshHz: Int): Double = 1000.0 / refreshHz
fun main() {
println("60Hz -> %.2f ms".format(frameBudgetMs(60)))
println("120Hz -> %.2f ms".format(frameBudgetMs(120)))
}Профилировщик Android Studio
Профилировщик Android Studio (Просмотр > Окна инструментов > Профилировщик) подключается к работающему приложению и показывает текущие временные диаграммы для CPU, памяти, энергопотребления и сети.
- CPU: записывайте трассировки методов и системы, чтобы находить самые нагруженные участки кода.
- Память: отслеживайте выделения памяти и создавайте дампы кучи, чтобы находить утечки.
- Энергопотребление: выявляйте блокировки пробуждения и чрезмерное количество заданий, разряжающих батарею.
Всегда профилируйте сборку в стиле релизной на реальном устройстве. Отладочные сборки и эмуляторы дают вводящие в заблуждение показатели, поскольку оптимизации отключены.
Макротестирование реальных сценариев
Библиотека Jetpack Macrobenchmark измеряет целые пользовательские сценарии (запуск, прокрутку, навигацию) на реальном устройстве и выдаёт стабильные показатели, например время отрисовки кадров и запуска.
Вы пишете тест, который запускает приложение и выполняет определённый сценарий; библиотека запускает его много раз и выдаёт значения медианы и 99-го процентиля.
@RunWith(AndroidJUnit4::class)
class StartupBenchmark {
@get:Rule val rule = MacrobenchmarkRule()
@Test
fun coldStartup() = rule.measureRepeated(
packageName = "com.example.app",
metrics = listOf(StartupTimingMetric()),
iterations = 5,
startupMode = StartupMode.COLD
) {
pressHome()
startActivityAndWait()
}
}Микротестирование нагруженного кода
Если нужно узнать, насколько быстро выполняется одна функция, используйте библиотеку Microbenchmark. Она запускает ваш код в тесном цикле, прогревает JIT и сообщает количество наносекунд на операцию, отфильтровывая шум.
Используйте её для анализаторов, сериализаторов, сортировки и любых привязанных к CPU вспомогательных функций, которые вызываются часто.
@RunWith(AndroidJUnit4::class)
class JsonParseBenchmark {
@get:Rule val benchmarkRule = BenchmarkRule()
@Test
fun parseLargePayload() {
val raw = loadSampleJson()
benchmarkRule.measureRepeated {
val parsed = parseUsers(raw) // code under test
// assertEquals avoids the compiler dropping the result
assertTrue(parsed.isNotEmpty())
}
}
}Системная трассировка с Perfetto
Системная трассировка записывает, что делал каждый поток и система в целом, кадр за кадром. Профилировщик CPU в Android Studio может записывать такую трассировку; также её можно получить с помощью adb и открыть в Perfetto (ui.perfetto.dev).
Трассировка показывает злополучные красные кадры, работу в главном потоке и время, проведённое в GPU. Это лучший инструмент для диагностики подёргиваний.
# Record a 5-second system trace from the command line
adb shell perfetto -o /data/misc/perfetto-traces/trace.perfetto-trace \
-t 5s sched freq idle am wm gfx view
# Pull it to your machine, then open in https://ui.perfetto.dev
adb pull /data/misc/perfetto-traces/trace.perfetto-traceПользовательские разделы трассировки
По умолчанию трассировки показывают работу фреймворка. Чтобы пометить собственный код, оберните его в именованный раздел трассировки. Затем эти имена отображаются в Perfetto как подписанные блоки, благодаря чему нагруженные участки легко заметить.
Используйте API androidx.tracing, чтобы разделы отображались и в отладочных трассировках, и в трассировках тестов производительности.
import androidx.tracing.trace
fun loadDashboard(repo: Repo): Dashboard {
return trace("loadDashboard") {
val user = trace("fetchUser") { repo.user() }
val feed = trace("fetchFeed") { repo.feed() }
Dashboard(user, feed)
}
}
// In Perfetto you will now see named slices:
// loadDashboard > fetchUser, fetchFeedОтслеживание пропущенных кадров во время работы
Профилировщик подключён не всегда. JankStats — это библиотека Jetpack, которая сообщает о подёргивающихся кадрах прямо из работающего приложения, поэтому вы можете записывать их в журнал или отправлять агрегированные данные в аналитику.
Она подключается к окну и вызывает ваш код каждый раз, когда кадр превышает отведённый ему бюджет.
val jankStats = JankStats.createAndTrack(window) { frameData ->
if (frameData.isJank) {
Log.w("Jank", "Janky frame: ${frameData.frameDurationUiNanos} ns")
analytics.logJank(frameData.frameDurationUiNanos)
}
}
// Pause/resume with your screen lifecycle
override fun onResume() { super.onResume(); jankStats.isTrackingEnabled = true }
override fun onPause() { super.onPause(); jankStats.isTrackingEnabled = false }Чтение показателей: процентили
Одно среднее значение скрывает проблемные случаи. Если средняя длительность кадра составляет 10 мс, а 99-й процентиль — 40 мс, то 1% кадров подёргиваются, и пользователи это ощущают. Всегда смотрите на P50, P90, P99, а не только на среднее.
Тесты производительности рассчитывают эти показатели за вас. Вспомогательный код ниже показывает идею на исходных образцах длительности кадров.
fun percentile(samplesMs: List<Double>, p: Int): Double {
val sorted = samplesMs.sorted()
val index = ((p / 100.0) * (sorted.size - 1)).toInt()
return sorted[index]
}
fun main() {
val frames = listOf(8.0, 9.0, 10.0, 9.5, 11.0, 40.0, 9.0, 10.5)
println("P50 = %.1f ms".format(percentile(frames, 50)))
println("P99 = %.1f ms".format(percentile(frames, 99)))
}Повторяемый процесс измерений
Достоверные показатели получают с помощью дисциплинированного процесса. Каждый раз выполняйте этот список:
- Используйте релизную сборку без отладки (R8 включён).
- Запускайте приложение на физическом устройстве, подключённом к питанию, со стабильным тепловым состоянием.
- Повторите сценарий несколько раз и укажите медиану.
- Изменяйте только одну вещь за раз и проводите измерение повторно.
- Сравнивайте результат с сохранённым базовым показателем, чтобы подтвердить улучшение.
Если пропустить эти шаги, вы будете追аться за шумом вместо устранения реальных проблем.
Куда на самом деле уходит время
Большинство подёргиваний в Android вызвано небольшим набором причин. Зная их, вы поймёте, куда направить инструменты:
- Работа в главном потоке: операции с диском и сетью, а также JSON в потоке пользовательского интерфейса.
- Избыточная рекомпозиция: Compose перерисовывает слишком большую область.
- Частые выделения памяти: паузы сборки мусора.
- Проходы компоновки: глубоко вложенные или многократно измеряемые макеты.
В следующих уроках этого курса мы непосредственно разберём рекомпозицию, утечки памяти и запуск приложения. Именно измерения подскажут, что исправлять в первую очередь.
Быстрая проверка
Вы хотите стабильно и повторяемо измерить, сколько времени занимает холодный запуск приложения на реальном устройстве при нескольких запусках. Какой инструмент лучше всего подходит?
Повторение: теперь вы умеете измерять
Вы научились делать производительность видимой до внесения изменений:
- Бюджет кадра 16 мс определяет, будет ли интерфейс плавным или подёргивающимся.
- Профилировщик Android Studio показывает текущие показатели CPU, памяти и энергопотребления.
- Макро- и микротесты производительности дают повторяемые числа.
- Трассировки Perfetto и пользовательские разделы
trace()показывают, куда уходит время. - JankStats сообщает о подёргиваниях прямо из работающего приложения.
- Читайте показатели P50/P90/P99, профилируйте релизные сборки на реальных устройствах и изменяйте только одну вещь за раз.
Далее мы применим эти знания к одному из главных источников подёргиваний в Compose: рекомпозиции.
Часто задаваемые вопросы
Урок «Измерение производительности» бесплатный?
Да — полный текст урока «Измерение производительности» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Android Academy, подпишись на CoddyKit PRO. Курс Android Academy содержит 4 уроков всего.
Чему я научусь в уроке «Измерение производительности»?
Профилировщики, трассировки и тесты производительности Ты практикуешь Android Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Android Academy?
Предыдущий опыт не требуется. Android Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Измерение производительности»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Android Academy?
Да. Каждый урок Android Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Измерение производительности
- Управление рекомпозицией
- Утечки памяти и исправления
- Профили запуска и Baseline Profiles