0Pricing
Android Academy · Урок

Измерение производительности

Профилировщики, трассировки и тесты производительности

«Измерение производительности» — бесплатный урок 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 — локальная установка не требуется.

Все уроки этого курса

  1. Измерение производительности
  2. Управление рекомпозицией
  3. Утечки памяти и исправления
  4. Профили запуска и Baseline Profiles
← Назад к Android Academy