Тестирование производительности при непрерывной интеграции

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

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

Неточные результаты

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

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

Запускайте тесты производительности на физических устройствах Android. Хотя их можно запускать на эмуляторах, это настоятельно не рекомендуется, поскольку это не отражает реалистичный пользовательский опыт, а вместо этого предоставляет цифры, привязанные к возможностям хост-ОС и оборудования. Рекомендуем использовать реальные устройства или сервис, который позволяет проводить на них тестирование, например Firebase Test Lab.

Как запустить тесты

Запуск тестов в рамках конвейера непрерывной интеграции может отличаться от локального запуска из Android Studio. На локальном устройстве интеграционные тесты Android обычно запускаются с помощью одной задачи Gradle connectedCheck. Эта задача автоматически создает APK-файл и тестовый APK-файл и запускает тесты на устройствах, подключенных к серверу непрерывной интеграции. При запуске в CI этот поток обычно необходимо разделить на отдельные фазы.

Сборка

Для библиотеки Microbenchmark запустите задачу Gradle assemble[VariantName]AndroidTest, которая создает тестовый APK-файл, содержащий код приложения и тестируемый код.

Библиотека Macrobenchmark требует, чтобы вы создали целевой APK и тестовый APK отдельно. Поэтому запустите задачи Gradle :app:assemble[VariantName] и :macrobenchmark:assemble[VariantName].

Установка и запуск

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

Для установки используйте команду adb install и укажите тестовый или целевой APK-файл.

Выполните команду adb shell am, чтобы запустить все тесты:

adb shell am instrument -w com.example.benchmark/androidx.benchmark.junit4.AndroidBenchmarkRunner

При использовании библиотеки Macrobenchmark используйте обычный androidx.test.runner.AndroidJUnitRunner в качестве средства запуска инструментирования.

Вы можете передать те же аргументы инструментирования, что и в конфигурации Gradle, используя аргумент -e. Все варианты аргументов инструментирования можно найти в статьях Аргументы инструментирования Microbenchmark и Добавление аргументов инструментирования для Macrobenchmark.

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

adb shell am instrument -w -e "androidx.benchmark.dryRunMode.enable" "true" com.example.benchmark/androidx.benchmark.junit4.AndroidBenchmarkRunner

Подробнее о том, как запускать инструментальные тесты из командной строки…

Заблокировать часы

Плагин Microbenchmark Gradle предоставляет команду ./gradlew lockClocks для блокировки тактовой частоты ЦП на устройстве с настроенным root-доступом. Это полезно для обеспечения стабильности, когда у вас есть доступ к устройствам с root-доступом, например сборкам userdebug. Вы можете повторить это с помощью сценария командной строки lockClocks.sh, доступного в исходном коде библиотеки.

Вы можете запустить скрипт непосредственно на хосте Linux или Mac или отправить его на устройство с помощью нескольких команд adb:

adb push path/lockClocks.sh /data/local/tmp/lockClocks.sh
adb shell /data/local/tmp/lockClocks.sh
adb shell rm /data/local/tmp/lockClocks.sh

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

Подробнее о том, почему полезно блокировать тактовую частоту ЦП, см. в разделе Как получить стабильные результаты тестирования.

Сбор результатов

После каждого запуска библиотеки тестирования производительности выводят результаты измерений в формате JSON и трассировки профилирования в каталог на устройстве Android. Библиотека Macrobenchmark выводит несколько файлов трассировки Perfetto – по одному на каждую итерацию цикла MacrobenchmarkRule.measureRepeated. Однако Microbenchmark создает только один файл трассировки для всех итераций каждого теста BenchmarkRule.measureRepeated. Файлы трассировки профилирования также выводятся в этот каталог.

Как сохранить и найти файлы

Если вы запускаете тесты с помощью Gradle, эти файлы автоматически копируются в каталог выходных данных хост-компьютера в разделе build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/.

Если вы запускаете команду adb напрямую, вам нужно извлечь файлы вручную. По умолчанию отчеты сохраняются на устройстве в каталоге media внешнего хранилища тестируемого приложения. Для удобства библиотека выводит путь к файлу в Logcat. Обратите внимание, что папка вывода может отличаться в зависимости от версии Android, на которой выполняются тесты.

Benchmark: writing results to /storage/emulated/0/Android/media/com.example.macrobenchmark/com.example.macrobenchmark-benchmarkData.json

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

adb shell am instrument -w -e additionalTestOutputDir /sdcard/Download/ com.example.benchmark/androidx.benchmark.junit4.AndroidBenchmarkRunner

На устройствах с Android 10 (уровень API 29) и более поздними версиями ОС тесты приложения по умолчанию выполняются в изолированной среде, которая не позволяет приложению получать доступ к файлам за пределами его каталога. Чтобы сохранить данные в глобальный каталог, например /sdcard/Download, передайте следующий аргумент инструментирования:

-e no-isolated-storage true

Вы также должны явно разрешить устаревшие варианты хранения в манифесте теста:

<application android:requestLegacyExternalStorage="true" ... >

Подробнее о том, как временно отключить области хранения данных…

Как получить файлы

Чтобы получить сгенерированные файлы с устройства, используйте команду adb pull, которая переносит указанный файл в текущий каталог на хосте:

adb pull /storage/emulated/0/Android/media/com.example.macrobenchmark/com.example.macrobenchmark-benchmarkData.json

Чтобы получить все объекты benchmarkData из указанной папки, используйте следующий фрагмент кода:

# The following command pulls all files ending in -benchmarkData.json from the directory
# hierarchy starting at the root /storage/emulated/0/Android.
adb shell find /sdcard/Download -name "*-benchmarkData.json" | tr -d '\r' | xargs -n1 adb pull

Файлы трассировки (.trace или .perfetto-trace) сохраняются в той же папке, что и benchmarkData.json, поэтому их можно собрать таким же образом.

Пример сравнительных данных

Библиотеки тестов генерируют JSON-файлы, содержащие информацию об устройстве, на котором выполнялись тесты, и о самих тестах. Ниже приведен пример сгенерированного JSON-файла.

{
    "context": {
        "build": {
            "brand": "google",
            "device": "blueline",
            "fingerprint": "google/blueline/blueline:12/SP1A.210812.015/7679548:user/release-keys",
            "model": "Pixel 3",
            "version": {
                "sdk": 31
            }
        },
        "cpuCoreCount": 8,
        "cpuLocked": false,
        "cpuMaxFreqHz": 2803200000,
        "memTotalBytes": 3753299968,
        "sustainedPerformanceModeEnabled": false
    },
    "benchmarks": [
        {
            "name": "startup",
            "params": {},
            "className": "com.example.macrobenchmark.startup.SampleStartupBenchmark",
            "totalRunTimeNs": 4975598256,
            "metrics": {
                "timeToInitialDisplayMs": {
                    "minimum": 347.881076,
                    "maximum": 347.881076,
                    "median": 347.881076,
                    "runs": [
                        347.881076
                    ]
                }
            },
            "sampledMetrics": {},
            "warmupIterations": 0,
            "repeatIterations": 3,
            "thermalThrottleSleepSeconds": 0
        }
    ]
}

Дополнительные ресурсы