Criar uma comparação da Microbenchmark

Para aprender a usar a biblioteca Microbenchmark adicionando mudanças no código do aplicativo, consulte a seção Guia de início rápido. Para saber como concluir uma configuração completa com mudanças mais complicadas na base de código, consulte a seção Configuração completa do projeto.

Guia de início rápido

Esta seção mostra como testar as comparações e executar medições únicas sem ter que mover o código para os módulos. Para que os resultados de desempenho sejam precisos, estas etapas envolvem desativar a depuração no app. Portanto, mantenha uma cópia local de trabalho sem confirmar as mudanças no sistema de controle de origem.

Para realizar uma comparação única, siga estas etapas:

  1. Adicione a biblioteca ao arquivo build.gradle ou build.gradle.kts do módulo:

    Kotlin

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

    Groovy

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

    Use uma dependência implementation em vez de uma dependência androidTestImplementation. Se você usar androidTestImplementation, as comparações não vão ser executadas, porque o manifesto da biblioteca não será integrado ao manifesto do app.

  2. Atualize o tipo de build debug para que ele não seja depurável:

    Kotlin

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

    Groovy

    android {
        ...
        buildTypes {
            debug {
                debuggable false
            }
        }
    }
    
  3. Altere testInstrumentationRunner para AndroidBenchmarkRunner:

    Kotlin

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

    Groovy

    android {
        ...
        defaultConfig {
            testInstrumentationRunner "androidx.benchmark.junit4.AndroidBenchmarkRunner"
        }
    }
    
  4. Inclua uma instância de BenchmarkRule em um arquivo de teste no diretório androidTest para adicionar a comparação. Para saber mais sobre como criar comparações, consulte Criar uma classe de Microbenchmark.

    O snippet de código a seguir mostra como adicionar uma comparação a um teste instrumentado:

    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()
                );
           }
        }
    }
    

Para aprender a criar uma comparação, consulte Criar uma classe de Microbenchmark.

Configuração completa do projeto

Para configurar comparações frequentes, em vez de uma única, isole as comparações em módulos. Isso garante que a configuração, como definir debuggable como false, seja diferente dos testes normais.

Como a Microbenchmark executa o código diretamente, coloque o código que você quer comparar em um módulo separado do Gradle e defina a dependência nele, como mostrado na figura 1.

estrutura do app
Figura 1. Estrutura do app com os módulos :app, :microbenchmark e :benchmarkable do Gradle, que permite as comparações da Microbenchmark no módulo :benchmarkable.

Use o assistente do Android Studio para adicionar um novo módulo do Gradle. O assistente cria um módulo pré-configurado, com um diretório de comparação e debuggable definido como false.

  1. Clique com o botão direito do mouse no seu projeto ou módulo no painel Project do Android Studio e clique em New > Module.

  2. Selecione a opção Benchmark no painel Templates.

  3. Selecione a Microbenchmark como tipo de módulo de comparações.

  4. Digite "microbenchmark" como o nome do módulo.

  5. Clique em Finish.

Configuração de novo módulo de biblioteca
Figura 2. Adicione um novo módulo do Gradle no Android Studio Bumblebee.

Após a criação do módulo, mude o arquivo build.gradle ou build.gradle.kts e adicione androidTestImplementation ao módulo que contém o código que será comparado:

Kotlin

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

Groovy

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

Criar uma classe de Microbenchmark

As comparações são testes de instrumentação padrão. Para criar uma comparação, use a classe BenchmarkRule oferecida pela biblioteca. Para comparar atividades, use ActivityScenario ou ActivityScenarioRule. Para comparar o código da interface, use @UiThreadTest.

O código a seguir apresenta uma amostra de comparação:

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();
        }
    }
}
    

Desativar a medição do tempo de configuração

É possível desativar a medição de tempo para as seções de código que você não quer avaliar usando o bloco runWithTimingDisabled{}. Essas seções geralmente representam um código que você precisa executar em cada iteração da comparação.

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

Tente minimizar a quantidade de trabalho feito dentro do bloco measureRepeated e dentro de runWithTimingDisabled. O bloco measureRepeated é executado várias vezes e pode afetar o tempo total necessário para fazer a comparação. Se for preciso verificar resultados de uma comparação, declare o último resultado em vez de fazer todas as iterações.

Executar a comparação

No Android Studio, execute a comparação da mesma forma que faria com qualquer @Test usando a ação de gutter ao lado da classe ou do método de teste, conforme mostrado na Figura 3.

Executar uma comparação de Microbenchmark
Figura 3. Execução do teste da Microbenchmark usando a ação de gutter ao lado de uma classe de teste.

Na linha de comando, você também pode executar connectedCheck para fazer todos os testes do módulo especificado do Gradle:

./gradlew benchmark:connectedCheck

Ou, para executar um único teste:

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

Resultados da comparação

Após a execução de uma comparação da Microbenchmark, as métricas são mostradas diretamente no Android Studio, e um relatório de comparação completo com mais métricas e informações sobre o dispositivo é gerado no formato JSON.

Resultados da comparação de Microbenchmark
Figura 4. Resultados da comparação de Microbenchmark.

Os relatórios JSON e todos os rastros de criação de perfil também são copiados automaticamente do dispositivo para o host. Eles são gravados na máquina host no seguinte local:

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

Por padrão, o relatório JSON é gravado no disco do dispositivo na pasta de mídias compartilhadas externas do APK de teste, que normalmente fica localizada em /storage/emulated/0/Android/media/**app_id**/**app_id**-benchmarkData.json.

Erros de configuração

A biblioteca detecta as condições a seguir para garantir que o projeto e o ambiente estejam configurados de modo a apresentar uma performance precisa para a versão:

  • O atributo "Debuggable" está definido como false.
  • Um dispositivo físico está sendo usado. Não há suporte a emuladores.
  • A frequência do processador está bloqueada, caso o dispositivo tenha acesso root.
  • O dispositivo está com um nível de bateria suficiente de pelo menos 25%.

Se alguma das condições anteriores não for respeitada, a comparação vai informar um erro para evitar medições imprecisas.

Para suprimir tipos específicos de erro, como avisos, e impedir que interrompam a comparação, transmita o tipo de erro em uma lista separada por vírgulas ao argumento de instrumentação androidx.benchmark.suppressErrors.

Você pode definir isso no script do Gradle, conforme mostrado no exemplo a seguir:

Kotlin

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

Groovy

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

Também é possível suprimir erros usando a linha de comando:

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

A supressão de erros permite que a comparação seja executada em uma configuração incorreta, e a saída da comparação é renomeada intencionalmente ao adicionar o erro aos nomes de teste. Por exemplo, executar uma comparação depurável com a supressão no snippet anterior adiciona DEBUGGABLE_ como prefixo aos nomes dos testes.