Scrivi un microbenchmark

Per scoprire come utilizzare la libreria Microbenchmark aggiungendo modifiche al codice dell'applicazione, consulta la sezione Guida rapida. Per scoprire come completare una configurazione completa con modifiche più complicate al codice sorgente, consulta la sezione Configurazione completa del progetto.

Guida rapida

Questa sezione mostra come provare il benchmarking ed eseguire misurazioni una tantum senza dover spostare il codice nei moduli. Per risultati accurati delle prestazioni, questi passaggi prevedono la disattivazione del debug nell'app, quindi mantienila in una copia di lavoro locale senza eseguire il commit delle modifiche nel sistema di controllo del codice sorgente.

Per eseguire un benchmarking una tantum:

  1. Aggiungi la libreria al file build.gradle o build.gradle.kts del modulo:

    Kotlin

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

    Groovy

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

    Utilizza una dipendenza implementation anziché una dipendenza androidTestImplementation. Se utilizzi androidTestImplementation, i benchmark non vengono eseguiti perché il manifest della libreria non viene unito al manifest dell'app.

  2. Aggiorna il tipo di build debug in modo che non sia eseguibile il debug:

    Kotlin

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

    Groovy

    android {
        ...
        buildTypes {
            debug {
                debuggable false
            }
        }
    }
  3. Modifica testInstrumentationRunner in AndroidBenchmarkRunner:

    Kotlin

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

    Groovy

    android {
        ...
        defaultConfig {
            testInstrumentationRunner "androidx.benchmark.junit4.AndroidBenchmarkRunner"
        }
    }
  4. Aggiungi un'istanza di BenchmarkRule in un file di test nella directory androidTest per aggiungere il benchmark. Per saperne di più sulla scrittura di benchmark, consulta Creare una classe Microbenchmark.

    Il seguente snippet di codice mostra come aggiungere un benchmark a un test strumentato:

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

Per scoprire come scrivere un benchmark, vai a Crea una classe Microbenchmark.

Configurazione completa del progetto

Per configurare un benchmarking regolare anziché una tantum, isola i benchmark nel proprio modulo. In questo modo, la loro configurazione, ad esempio l'impostazione di debuggable su false, è separata dai test regolari.

Poiché Microbenchmark esegue il codice direttamente, inserisci il codice di cui vuoi eseguire il benchmark in un modulo Gradle separato e imposta la dipendenza da quel modulo come mostrato nella figura 1.

struttura dell&#39;app
Figura 1. Struttura dell'app con i moduli Gradle :app, :microbenchmark e :benchmarkable, che consente a Microbenchmark di eseguire il benchmark del codice nel modulo :benchmarkable.

Per aggiungere un nuovo modulo Gradle, puoi utilizzare la procedura guidata per i moduli in Android Studio. La procedura guidata crea un modulo preconfigurato per il benchmarking, con una directory di benchmark aggiunta e debuggable impostato su false.

  1. Fai clic con il tasto destro del mouse sul progetto o sul modulo nel riquadro Project (Progetto) di Android Studio e fai clic su New > Module (Nuovo > Modulo).

  2. Seleziona Benchmark nel riquadro Modelli.

  3. Seleziona Microbenchmark come tipo di modulo di benchmark.

  4. Digita "microbenchmark" come nome del modulo.

  5. Fai clic su Fine.

Configurare il nuovo modulo della raccolta
Figura 2. Aggiungi un nuovo modulo Gradle in Android Studio Bumblebee.

Dopo aver creato il modulo, modifica il file build.gradle o build.gradle.kts e aggiungi androidTestImplementation al modulo contenente il codice da confrontare:

Kotlin

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

Groovy

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

Crea una classe Microbenchmark

I benchmark sono test di instrumentazione standard. Per creare un benchmark, utilizza la classe BenchmarkRule fornita dalla libreria. Per confrontare le attività con i benchmark, utilizza ActivityScenario o ActivityScenarioRule. Per eseguire il benchmark del codice dell'interfaccia utente, utilizza @UiThreadTest.

Il seguente codice mostra un benchmark di esempio:

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

Disattivare la sincronizzazione per la configurazione

Puoi disattivare la misurazione del tempo per le sezioni di codice che non vuoi misurare con il blocco runWithTimingDisabled{}. Queste sezioni in genere rappresentano un codice che devi eseguire a ogni iterazione del benchmark.

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

Cerca di ridurre al minimo la quantità di lavoro svolto all'interno del blocco measureRepeated e all'interno di runWithTimingDisabled. Il blocco measureRepeated viene eseguito più volte e può influire sul tempo complessivo necessario per eseguire il benchmark. Se devi verificare alcuni risultati di un benchmark, puoi asserire l'ultimo risultato anziché farlo a ogni iterazione del benchmark.

Esegui il benchmark

In Android Studio, esegui il benchmark come faresti con qualsiasi @Test utilizzando l'azione gutter accanto alla classe o al metodo di test, come mostrato nella figura 3.

Esegui Microbenchmark
Figura 3. Esegui il test Microbenchmark utilizzando l'azione di margine accanto a una classe di test.

In alternativa, dalla riga di comando, esegui connectedCheck per eseguire tutti i test dal modulo Gradle specificato:

./gradlew benchmark:connectedCheck

o un singolo test:

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

Risultati benchmark

Dopo l'esecuzione corretta di Microbenchmark, le metriche vengono visualizzate direttamente in Android Studio e un report di benchmark completo con metriche aggiuntive e informazioni sul dispositivo è disponibile in formato JSON.

Risultati del microbenchmark
Figura 4. Risultati del microbenchmark.

Anche i report JSON e le tracce di profilazione vengono copiati automaticamente dal dispositivo all'host. Questi vengono scritti sul computer host nel seguente percorso:

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

Per impostazione predefinita, il report JSON viene scritto su disco sul dispositivo nella cartella multimediale condivisa esterna dell'APK di test, che in genere si trova in /storage/emulated/0/Android/media/**app_id**/**app_id**-benchmarkData.json.

Errori di configurazione

La libreria rileva le seguenti condizioni per garantire che il progetto e l'ambiente siano configurati per prestazioni accurate della release:

  • Debuggable è impostato su false.
  • Viene utilizzato un dispositivo fisico. Gli emulatori non sono supportati.
  • I quadranti vengono bloccati se il dispositivo è rooted.
  • Livello della batteria del dispositivo sufficiente, almeno al 25%.

Se uno dei controlli precedenti non va a buon fine, il benchmark segnala un errore per scoraggiare misurazioni imprecise.

Per eliminare tipi di errori specifici come avvisi ed evitare che interrompano il benchmark, passa il tipo di errore in un elenco separato da virgole all'argomento di strumentazione androidx.benchmark.suppressErrors.

Puoi impostarlo dallo script Gradle, come mostrato nell'esempio seguente:

Kotlin

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

Groovy

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

Puoi anche eliminare gli errori dalla riga di comando:

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

La soppressione degli errori consente l'esecuzione del benchmark in uno stato configurato in modo errato e l'output del benchmark viene rinominato intenzionalmente anteponendo i nomi dei test all'errore. Ad esempio, l'esecuzione di un benchmark sottoponibile a debug con la soppressione nello snippet precedente antepone i nomi dei test con DEBUGGABLE_.