Напишите микротест

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

Быстрый старт

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

Для проведения разового сравнительного анализа выполните следующие действия:

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

    Котлин

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

    Круто

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

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

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

    Котлин

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

    Круто

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

    Котлин

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

    Круто

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

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

    Котлин

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

    Ява

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

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

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

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

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

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

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

  1. Щелкните правой кнопкой мыши свой проект или модуль на панели «Проект» в Android Studio и выберите «Создать» > «Модуль» .

  2. Выберите Benchmark на панели «Шаблоны» .

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

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

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

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

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

Котлин

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 .

Следующий код демонстрирует пример теста:

Котлин

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

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

Ява

@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{} . Эти разделы обычно представляют собой некоторый код, который вам нужно запустить на каждой итерации бенчмарка.

Котлин

// 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)
}
    

Ява

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 используя действие gutter рядом с вашим тестовым классом или методом, как показано на рисунке 3.

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

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

./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, как показано в следующем примере:

Котлин

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_ .

{% дословно %} {% endverbatim %} {% дословно %} {% endverbatim %} ,

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

Быстрый старт

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

Для проведения разового сравнительного анализа выполните следующие действия:

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

    Котлин

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

    Круто

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

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

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

    Котлин

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

    Круто

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

    Котлин

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

    Круто

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

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

    Котлин

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

    Ява

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

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

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

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

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

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

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

  1. Щелкните правой кнопкой мыши свой проект или модуль на панели «Проект» в Android Studio и выберите «Создать» > «Модуль» .

  2. Выберите Benchmark на панели «Шаблоны» .

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

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

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

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

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

Котлин

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 .

Следующий код демонстрирует пример теста:

Котлин

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

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

Ява

@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{} . Эти разделы обычно представляют собой некоторый код, который вам нужно запустить на каждой итерации бенчмарка.

Котлин

// 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)
}
    

Ява

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 используя действие gutter рядом с вашим тестовым классом или методом, как показано на рисунке 3.

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

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

./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, как показано в следующем примере:

Котлин

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_ .

{% дословно %} {% endverbatim %} {% дословно %} {% endverbatim %} ,

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

Быстрый старт

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

Для проведения разового сравнительного анализа выполните следующие действия:

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

    Котлин

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

    Круто

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

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

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

    Котлин

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

    Круто

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

    Котлин

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

    Круто

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

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

    Котлин

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

    Ява

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

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

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

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

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

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

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

  1. Щелкните правой кнопкой мыши свой проект или модуль на панели «Проект» в Android Studio и выберите «Создать» > «Модуль» .

  2. Выберите Benchmark на панели «Шаблоны» .

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

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

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

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

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

Котлин

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 .

Следующий код демонстрирует пример теста:

Котлин

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

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

Ява

@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{} . Эти разделы обычно представляют собой некоторый код, который вам нужно запустить на каждой итерации бенчмарка.

Котлин

// 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)
}
    

Ява

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 используя действие gutter рядом с вашим тестовым классом или методом, как показано на рисунке 3.

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

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

./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, как показано в следующем примере:

Котлин

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_ .

{% дословно %} {% endverbatim %} {% дословно %} {% endverbatim %} ,

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

Быстрый старт

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

Для проведения разового сравнительного анализа выполните следующие действия:

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

    Котлин

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

    Круто

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

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

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

    Котлин

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

    Круто

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

    Котлин

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

    Круто

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

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

    Котлин

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

    Ява

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

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

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

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

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

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

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

  1. Щелкните правой кнопкой мыши свой проект или модуль на панели «Проект» в Android Studio и выберите «Создать» > «Модуль» .

  2. Выберите Benchmark на панели «Шаблоны» .

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

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

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

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

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

Котлин

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 .

Следующий код демонстрирует пример теста:

Котлин

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

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

Ява

@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{} . Эти разделы обычно представляют собой некоторый код, который вам нужно запустить на каждой итерации бенчмарка.

Котлин

// 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)
}
    

Ява

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 используя действие gutter рядом с вашим тестовым классом или методом, как показано на рисунке 3.

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

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

./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, как показано в следующем примере:

Котлин

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_ .

{% дословно %} {% endverbatim %} {% дословно %} {% endverbatim %}