Как написать микротест

Чтобы узнать, как использовать библиотеку Microbenchmark, добавляя изменения в код приложения, ознакомьтесь с разделом Быстрый старт. Чтобы узнать, как выполнить полную настройку с более сложными изменениями в базе кода, ознакомьтесь с разделом Полная настройка проекта.

Краткое руководство

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

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

  1. Добавьте библиотеку в файл build.gradle или build.gradle.kts модуля:

    Kotlin

    dependencies {
        implementation("androidx.benchmark:benchmark-junit4:1.2.4")
    }

    Яркий

    dependencies {
        implementation 'androidx.benchmark:benchmark-junit4:1.2.4'
    }

    Используйте зависимость implementation вместо androidTestImplementation. Если вы используете androidTestImplementation, тесты не будут выполняться, поскольку манифест библиотеки не объединен с манифестом приложения.

  2. Обновите тип сборки debug, чтобы она не была отладочной:

    Kotlin

    android {
        ...
        buildTypes {
            debug {
                isDebuggable = false
            }
        }
    }

    Яркий

    android {
        ...
        buildTypes {
            debug {
                debuggable false
            }
        }
    }
  3. Измените testInstrumentationRunner на AndroidBenchmarkRunner:

    Kotlin

    android {
        ...
        defaultConfig {
            testInstrumentationRunner = "androidx.benchmark.junit4.AndroidBenchmarkRunner"
        }
    }

    Яркий

    android {
        ...
        defaultConfig {
            testInstrumentationRunner "androidx.benchmark.junit4.AndroidBenchmarkRunner"
        }
    }
  4. Добавьте экземпляр BenchmarkRule в тестовый файл в каталоге androidTest, чтобы добавить свой тест производительности. Подробнее о том, как создать класс Microbenchmark…

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

    Kotlin

    @RunWith(AndroidJUnit4::class)
    class SampleBenchmark {
        @get:Rule
        val benchmarkRule = BenchmarkRule()
    
        @Test
        fun benchmarkSomeWork() {
            benchmarkRule.measureRepeated {
                doSomeWork()
            }
        }
    }

    Java

    @RunWith(AndroidJUnit4.class)
    class SampleBenchmark {
        @Rule
        public BenchmarkRule benchmarkRule = new BenchmarkRule();
    
        @Test
        public void benchmarkSomeWork() {
                BenchmarkRuleKt.measureRepeated(
                    (Function1<BenchmarkRule.Scope, Unit>) scope -> doSomeWork()
                );
           }
        }
    }

Чтобы узнать, как написать тест, перейдите к разделу Создание класса Microbenchmark.

Полная настройка проекта

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

Поскольку Microbenchmark запускает ваш код напрямую, поместите код, который вы хотите протестировать, в отдельный модуль Gradle и установите зависимость от этого модуля, как показано на рисунке 1.

структура приложения
Рисунок 1. Структура приложения с модулями Gradle :app, :microbenchmark и :benchmarkable, которая позволяет Microbenchmarks тестировать код в модуле :benchmarkable.

Чтобы добавить новый модуль Gradle, используйте мастер модулей в Android Studio. Мастер создаст модуль, предварительно настроенный для тестирования, с добавленным каталогом тестов и параметром debuggable, установленным в значение false.

  1. На панели Project (Проект) в Android Studio нажмите правой кнопкой мыши на проект или модуль и выберите New > Module (Создать > Модуль).

  2. На панели Шаблоны выберите Контрольный показатель.

  3. В качестве типа модуля выберите Microbenchmark.

  4. Введите "microbenchmark" в качестве названия модуля.

  5. Нажмите Готово.

Настроить новый модуль библиотеки
Рисунок 2. Добавьте новый модуль Gradle в Android Studio Bumblebee.

После создания модуля измените его файл build.gradle или build.gradle.kts и добавьте androidTestImplementation в модуль, содержащий код для тестирования:

Kotlin

dependencies {
    // The module name might be different.
    androidTestImplementation(project(":benchmarkable"))
}

Яркий

dependencies {
    // The module name might be different.
    androidTestImplementation project(':benchmarkable')
}

Создание класса Microbenchmark

Тесты производительности – это стандартные инструментальные тесты. Чтобы создать тест, используйте класс BenchmarkRule, предоставляемый библиотекой. Для сравнения действий используйте ActivityScenario или ActivityScenarioRule. Для тестирования кода пользовательского интерфейса используйте @UiThreadTest.

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

Kotlin

@RunWith(AndroidJUnit4::class)
class SampleBenchmark {
    @get:Rule
    val benchmarkRule = BenchmarkRule()

    @Test
    fun benchmarkSomeWork() {
        benchmarkRule.measureRepeated {
            doSomeWork()
        }
    }
}
    

Java

@RunWith(AndroidJUnit4.class)
class SampleBenchmark {
    @Rule
    public BenchmarkRule benchmarkRule = new BenchmarkRule();

    @Test
    public void benchmarkSomeWork() {
        final BenchmarkState state = benchmarkRule.getState();
        while (state.keepRunning()) {
            doSomeWork();
        }
    }
}
    

Отключить таймер для настройки

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

Kotlin

// using random with the same seed, so that it generates the same data every run
private val random = Random(0)

// create the array once and just copy it in benchmarks
private val unsorted = IntArray(10_000) { random.nextInt() }

@Test
fun benchmark_quickSort() {
    // ...
    benchmarkRule.measureRepeated {
        // copy the array with timing disabled to measure only the algorithm itself
        listToSort = runWithTimingDisabled { unsorted.copyOf() }

        // sort the array in place and measure how long it takes
        SortingAlgorithms.quickSort(listToSort)
    }

    // assert only once not to add overhead to the benchmarks
    assertTrue(listToSort.isSorted)
}
    

Java

private final int[] unsorted = new int[10000];

public SampleBenchmark() {
    // Use random with the same seed, so that it generates the same data every
    // run.
    Random random = new Random(0);

    // Create the array once and copy it in benchmarks.
    Arrays.setAll(unsorted, (index) -> random.nextInt());
}

@Test
public void benchmark_quickSort() {
    final BenchmarkState state = benchmarkRule.getState();

    int[] listToSort = new int[0];

    while (state.keepRunning()) {
        
        // Copy the array with timing disabled to measure only the algorithm
        // itself.
        state.pauseTiming();
        listToSort = Arrays.copyOf(unsorted, 10000);
        state.resumeTiming();
        
        // Sort the array in place and measure how long it takes.
        SortingAlgorithms.quickSort(listToSort);
    }

    // Assert only once, not to add overhead to the benchmarks.
    assertTrue(SortingAlgorithmsKt.isSorted(listToSort));
}
    

Постарайтесь минимизировать объем работы, выполняемой внутри блока measureRepeated и внутри runWithTimingDisabled. Блок measureRepeated выполняется несколько раз, и это может повлиять на общее время, необходимое для выполнения теста. Если вам нужно проверить некоторые результаты теста, вы можете подтвердить последний результат, а не делать это на каждой итерации теста.

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

Запустите тест производительности в Android Studio так же, как и любой другой тест @Test, используя действие на полях рядом с тестовым классом или методом, как показано на рисунке 3.

Запустить микротест
Рисунок 3. Запустите тест Microbenchmark, используя действие на полях рядом с тестовым классом.

Вы также можете запустить все тесты из указанного модуля Gradle, выполнив в командной строке команду connectedCheck.

./gradlew benchmark:connectedCheck

или один тест:

./gradlew benchmark:connectedCheck -P android.testInstrumentationRunnerArguments.class=com.example.benchmark.SampleBenchmark#benchmarkSomeWork

Результат сравнения

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

Результаты микротестирования
Рисунок 4. Результаты микротестирования.

Отчеты JSON и трассировки профилирования также автоматически копируются с устройства на хост. Они записываются на хост-компьютере в следующем расположении:

project_root/module/build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/device_id/

По умолчанию отчет в формате JSON записывается на диск на устройстве в общую папку тестового APK-файла, которая обычно находится по адресу /storage/emulated/0/Android/media/**app_id**/**app_id**-benchmarkData.json.

Ошибки конфигурации

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

  • Для атрибута debuggable задано значение false.
  • Используется физическое устройство (эмуляторы не поддерживаются).
  • Если на устройстве есть root-доступ, частота заблокирована.
  • Уровень заряда батареи на устройстве не менее 25%.

Если какая-либо из предыдущих проверок не удалась, тест сообщает об ошибке, чтобы предотвратить неточные измерения.

Чтобы подавить определенные типы ошибок как предупреждения и предотвратить остановку теста, передайте тип ошибки в виде списка, разделенного запятыми, в аргумент инструментирования androidx.benchmark.suppressErrors.

Это можно сделать в скрипте Gradle, как показано в примере ниже.

Kotlin

android {
    defaultConfig {
       …
      testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "DEBUGGABLE,LOW-BATTERY"
    }
}

Яркий

android {
    defaultConfig {
       …
      testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "DEBUGGABLE,LOW-BATTERY"
    }
}

Вы также можете подавить ошибки из командной строки:

$ ./gradlew :benchmark:connectedCheck -P andoidtestInstrumentationRunnerArguments.androidx.benchmark.supperssErrors=DEBUGGABLE,LOW-BATTERY

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