Scrivi un Macrobenchmark

Utilizza la libreria Macrobenchmark per testare casi d'uso più ampi della tua app, tra cui l'avvio dell'app e manipolazioni complesse della UI, come lo scorrimento di un LazyColumn o l'esecuzione di animazioni. Se vuoi testare aree più piccole del tuo codice, consulta Microbenchmark. Questa pagina mostra come configurare la libreria Macrobenchmark.

La libreria genera i risultati del benchmark nella console di Android Studio e in un file JSON con maggiori dettagli. Fornisce inoltre file di traccia che puoi caricare e analizzare in Android Studio.

Utilizza la libreria Macrobenchmark in un ambiente di integrazione continua (CI), come descritto in Esegui il benchmark nell'integrazione continua.

Puoi utilizzare Macrobenchmark per generare profili di riferimento. Per prima cosa, configura la libreria Macrobenchmark, quindi puoi creare un profilo di riferimento.

Macrobenchmark è lo strumento consigliato per testare le UI create con Jetpack Compose. Per scoprire come scrivere test UI Automator che interagiscono con i nodi di Compose, consulta Interoperabilità.

Configurazione del progetto

Ti consigliamo di utilizzare Macrobenchmark con l'ultima versione di Android Studio.

Configura il modulo Macrobenchmark

I macrobenchmark richiedono un modulo com.android.test, separato dal codice dell'app, responsabile dell'esecuzione dei test che misurano l'app.

In Android Studio è disponibile un modello per semplificare la configurazione del modulo Macrobenchmark. Il modello del modulo di benchmark crea automaticamente un modulo nel tuo progetto per misurare l'app creata da un modulo dell'app, incluso un benchmark di avvio di esempio.

Per utilizzare il modello di modulo per creare un nuovo modulo, segui questi passaggi:

  1. Fai clic con il tasto destro del mouse sul progetto o sul modulo nel riquadro Progetto in Android Studio e seleziona Nuovo > Modulo.

  2. Seleziona Benchmark dal riquadro Modelli. Puoi personalizzare l'app di destinazione, ovvero l'app di cui eseguire il benchmark, nonché il nome del pacchetto e del modulo per il nuovo modulo Macrobenchmark.

  3. Fai clic su Fine.

Modello di modulo di benchmark.
Figura 1. Modello di modulo di benchmark.

Configurare l'app

Per eseguire il benchmark di un'app, nota come target di Macrobenchmark, l'app deve essere profileable, il che consente di leggere informazioni dettagliate sulla traccia senza influire sulle prestazioni. La procedura guidata del modulo aggiunge automaticamente il tag <profileable> al file AndroidManifest.xml dell'app.

Assicurati che l'app di destinazione includa ProfilerInstaller 1.3 o versioni successive, che è necessario alla libreria Macrobenchmark per abilitare l'acquisizione e il ripristino del profilo e lo svuotamento della cache degli shader.

Configura l'app sottoposta a benchmark in modo che sia il più simile possibile alla versione di release o di produzione. Configurala come non abilitata al debug e preferibilmente con la minimizzazione attiva, che migliora le prestazioni. In genere, questa operazione viene eseguita creando una copia della variante di release, che ha le stesse prestazioni, ma è firmata localmente con chiavi di debug. In alternativa, puoi utilizzare initWith per indicare a Gradle di farlo al posto tuo:

Kotlin

buildTypes {
    getByName("release") {
        isMinifyEnabled = true
        isShrinkResources = true
        proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
    }

    create("benchmark") {
        initWith(getByName("release"))
        signingConfig = signingConfigs.getByName("debug")
    }
}

Alla moda

buildTypes {
    release {
        isMinifyEnabled = true
        isShrinkResources = true
        proguardFiles(
            getDefaultProguardFile("proguard-android-optimize.txt"),
            "keep-rules.pro"
        )
        // In real app, this would use its own release keystore
        signingConfig = signingConfigs.getByName("debug")
        baselineProfile.automaticGenerationDuringBuild = true
    }
}

Per assicurarti che l'esecuzione del benchmark crei e testi la variante corretta della tua app, come mostrato nella figura 2, segui questi passaggi:

  1. Esegui una sincronizzazione Gradle.
  2. Apri il riquadro Varianti di compilazione.
  3. Seleziona la variante di benchmark sia dell'app sia del modulo Macrobenchmark.
Seleziona la variante di benchmark.
Figura 2. Seleziona la variante di benchmark.

(Facoltativo) Configura l'app multimodulo

Se la tua app ha più di un modulo Gradle, assicurati che gli script di build sappiano quale variante di compilazione compilare. Aggiungi la proprietà matchingFallbacks al tipo di build benchmark dei moduli :macrobenchmark e :app. Il resto dei moduli Gradle può avere la stessa configurazione di prima.

Kotlin

create("benchmark") {
    initWith(getByName("release"))
    signingConfig = signingConfigs.getByName("debug")

    matchingFallbacks += listOf("release")
 }

Alla moda

benchmark {
    initWith buildTypes.release
    signingConfig signingConfigs.debug

    matchingFallbacks = ['release']
 }

Senza questo, il tipo di build benchmark appena aggiunto causa l'errore della build e fornisce il seguente messaggio di errore:

> Could not resolve project :shared.
     Required by:
         project :app
      > No matching variant of project :shared was found.
      ...

Quando selezioni le varianti di compilazione nel progetto, scegli benchmark per i moduli :app e :macrobenchmark e release per tutti gli altri moduli presenti nell'app, come mostrato nella figura 3:

Varianti di benchmark per un progetto multimodulo con i tipi di build di release e benchmark selezionati.
Figura 3. Varianti di benchmark per un progetto multimodulo con tipi di build di release e benchmark selezionati.

Per saperne di più, consulta Risolvi gli errori di build relativi alla corrispondenza delle varianti.

(Facoltativo) Configura le versioni prodotto

Se nella tua app sono impostate più versioni prodotto, configura il modulo :macrobenchmark in modo che sappia quale versione prodotto della tua app creare e sottoporre a benchmark.

Gli esempi in questa pagina utilizzano le due versioni prodotto nel modulo :app, demo e production, come mostrato nello snippet seguente:

Kotlin

flavorDimensions += "environment"
productFlavors {
    create("demo") {
        dimension = "environment"
        // ...
    }
    create("production") {
        dimension = "environment"
        // ...
    }
}

Alla moda

flavorDimensions 'environment'
productFlavors {
    demo {
        dimension 'environment'
        // ...
    }

    production {
        dimension 'environment'
        // ...
    }
}

Senza questa configurazione, potresti ricevere un errore di build simile a quello causato da più moduli Gradle:

Could not determine the dependencies of task ':macrobenchmark:connectedBenchmarkAndroidTest'.
> Could not determine the dependencies of null.
   > Could not resolve all task dependencies for configuration ':macrobenchmark:benchmarkTestedApks'.
      > Could not resolve project :app.
        Required by:
            project :macrobenchmark
         > The consumer was configured to find a runtime of a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'benchmark', attribute 'com.android.build.api.attributes.AgpVersionAttr' with value '7.3.0'. However we cannot choose between the following variants of project :app:
             -   demoBenchmarkRuntimeElements
             -   productionBenchmarkRuntimeElements
           All of them match the consumer attributes:
           ...

Le due sezioni seguenti descrivono i modi per configurare il benchmarking con più versioni prodotto.

Utilizza missingDimensionStrategy

Se specifichi missingDimensionStrategy in defaultConfig del modulo :macrobenchmark, il sistema di build utilizzerà la dimensione versioni come alternativa. Specifica le dimensioni da utilizzare se non le trovi nel modulo. Nell'esempio seguente, la versione production viene utilizzata come dimensione predefinita:

Kotlin

defaultConfig {
    missingDimensionStrategy("environment", "production")
}

Alla moda

defaultConfig {
    missingDimensionStrategy "environment", "production"
}

In questo modo, il modulo :macrobenchmark è in grado di creare ed eseguire il benchmark solo della versione prodotto specificata, il che è utile se sai che solo una versione prodotto ha la configurazione corretta per il benchmark.

Definisci le versioni prodotto nel modulo :macrobenchmark

Se vuoi creare ed eseguire il benchmark di altre versioni prodotto, definiscile nel modulo :macrobenchmark. Specificalo in modo simile a come hai fatto nel modulo :app, ma assegna productFlavors solo a un dimension. Non sono necessarie altre impostazioni:

Kotlin

flavorDimensions += "environment"
productFlavors {
    create("demo") {
        dimension = "environment"
    }
    create("production") {
        dimension = "environment"
    }
}

Alla moda

flavorDimensions 'environment'
productFlavors {
    demo {
        dimension 'environment'
    }

    production {
        dimension 'environment'
    }
}

Dopo aver definito e sincronizzato il progetto, scegli la variante di compilazione pertinente dal riquadro Varianti di compilazione, come mostrato nella figura 4:

Varianti di benchmark per il progetto con versioni prodotto che mostrano productionBenchmark e release selezionati.
Figura 4. Varianti di benchmark per il progetto con versioni prodotto che mostrano "productionBenchmark" e "release" selezionati.

Per saperne di più, consulta Risolvi gli errori di build relativi alla corrispondenza delle varianti.

Crea una classe Macrobenchmark

I test di benchmark vengono forniti tramite l'API della regola JUnit4 MacrobenchmarkRule nella libreria Macrobenchmark. Contiene un metodo measureRepeated che ti consente di specificare varie condizioni su come eseguire e valutare l'app di destinazione.

Devi almeno specificare il packageName dell'app di destinazione, cosa vuoi misurare (metrics) e quante iterations deve eseguire il benchmark.

Kotlin

@LargeTest
@RunWith(AndroidJUnit4::class)
class SampleStartupBenchmark {
    @get:Rule
    val benchmarkRule = MacrobenchmarkRule()

    @Test
    fun startup() = benchmarkRule.measureRepeated(
        packageName = TARGET_PACKAGE,
        metrics = listOf(StartupTimingMetric()),
        iterations = DEFAULT_ITERATIONS,
    ) {
        // starts default launch activity
        uiAutomator { startApp(TARGET_PACKAGE) }
    }
}

Per tutte le opzioni di personalizzazione del benchmark, consulta la sezione Personalizza i benchmark.

Esegui il benchmark delle interazioni con la UI di Compose

Mentre l'esempio precedente misura l'avvio dell'app, spesso vorrai misurare le prestazioni della UI, ad esempio i frame persi (jank) durante lo scorrimento.

Per eseguire il benchmark di un'applicazione Jetpack Compose, ad esempio per misurare FrameTimingMetric durante lo scorrimento di un LazyColumn, puoi utilizzare UI Automator per interagire direttamente con i nodi di Compose, come mostrato nello snippet seguente.

@LargeTest
@RunWith(AndroidJUnit4::class)
class ComposeScrollBenchmark {
    @get:Rule
    val benchmarkRule = MacrobenchmarkRule()

    @Test
    fun scrollLazyColumn() = benchmarkRule.measureRepeated(
        packageName = "com.example.compose.app",
        metrics = listOf(FrameTimingMetric()),
        iterations = 5,
        startupMode = StartupMode.WARM,
        setupBlock = { pressHome() }
    ) {
        startActivityAndWait()

        // Find the Compose node using the testTag defined in your app
        val lazyColumn = device.findObject(By.res("my_lazy_column"))

        // Simulate a scroll gesture to measure FrameTimingMetric
        lazyColumn.setGestureMargin(device.displayWidth / 5)
        lazyColumn.fling(Direction.DOWN)
    }
}

Esegui il benchmark

Esegui il test da Android Studio per misurare le prestazioni della tua app sul dispositivo. Puoi eseguire i benchmark nello stesso modo in cui esegui qualsiasi altro @Test utilizzando l'azione del gutter accanto alla classe o al metodo di test, come mostrato nella figura 5.

Esegui Macrobenchmark con l'azione della barra laterale accanto alla classe di test.
Figura 5. Esegui Macrobenchmark con l'azione del gutter accanto alla classe di test.

Puoi anche eseguire tutti i benchmark in un modulo Gradle dalla riga di comando eseguendo il comando connectedCheck:

./gradlew :macrobenchmark:connectedCheck

Puoi eseguire un singolo test con il seguente comando:

./gradlew :macrobenchmark:connectedCheck -P android.testInstrumentationRunnerArguments.class=com.example.macrobenchmark.startup.SampleStartupBenchmark#startup

Consulta Benchmark in Continuous Integration per informazioni su come eseguire e monitorare i benchmark nell'integrazione continua.

Risultati benchmark

Dopo l'esecuzione di un benchmark, le metriche vengono visualizzate direttamente in Android Studio e vengono generate per l'utilizzo di CI in un file JSON. Ogni iterazione misurata acquisisce un tracciamento del sistema separato. Puoi aprire questi risultati della traccia facendo clic sui link nel riquadro Risultati del test, come mostrato nella figura 6:

Risultati di avvio di Macrobenchmark.
Figura 6. Risultati di avvio di Macrobenchmark.

Quando la traccia viene caricata, Android Studio ti chiede di selezionare il processo da analizzare. La selezione viene precompilata con il processo dell'app di destinazione, come mostrato nella figura 7:

Selezione del processo di traccia di Studio.
Figura 7. Selezione del processo di traccia di Studio.

Dopo aver caricato il file di traccia, Studio mostra i risultati nello strumento CPU Profiler:

Traccia di Studio.
Figura 8. Studio trace.

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

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

Accedi manualmente ai file di traccia

Se vuoi utilizzare lo strumento Perfetto per analizzare un file di traccia, devi seguire alcuni passaggi aggiuntivi. Perfetto ti consente di ispezionare tutti i processi in esecuzione sul dispositivo durante la traccia, mentre il CPU Profiler di Android Studio limita l'ispezione a un singolo processo.

Se richiami i test da Android Studio o dalla riga di comando Gradle, i file di traccia vengono copiati automaticamente dal dispositivo all'host. Questi vengono scritti sulla macchina host nella seguente posizione:

project_root/module/build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/device_id/TrivialStartupBenchmark_startup[mode=COLD]_iter002.perfetto-trace

Una volta ottenuto il file di traccia nel sistema host, puoi aprirlo in Android Studio con File > Apri nel menu. Viene visualizzata la vista dello strumento Profiler mostrata nella sezione precedente.

Errori di configurazione

Se l'app non è configurata correttamente (abilitata al debug o non profilabile), Macrobenchmark restituisce un errore anziché segnalare una misurazione errata o incompleta. Puoi eliminare questi errori con l'argomento androidx.benchmark.suppressErrors.

Macrobenchmark restituisce errori anche quando tenta di misurare un emulatore o su un dispositivo con batteria in esaurimento, il che potrebbe compromettere la disponibilità dei core e la velocità di clock.

Personalizza i benchmark

La funzione measureRepeated accetta vari parametri che influiscono sulle metriche raccolte dalla libreria, sul modo in cui l'app viene avviata e compilata o sul numero di iterazioni eseguite dal benchmark.

Acquisisci le metriche

Le metriche sono il tipo principale di informazioni estratte dai benchmark. Sono disponibili le metriche riportate di seguito:

Per ulteriori informazioni sulle metriche, consulta Acquisisci le metriche di Macrobenchmark.

Migliora i dati di traccia con gli eventi personalizzati

Può essere utile instrumentare la tua app con eventi di traccia personalizzati, che vengono visualizzati con il resto del report sulla traccia e possono aiutarti a identificare i problemi specifici della tua app. Per scoprire di più sulla creazione di eventi di traccia personalizzati, consulta Definisci eventi personalizzati.

CompilationMode

I macrobenchmark possono specificare un CompilationMode, che definisce la quantità di app che deve essere precompilata dal bytecode DEX (il formato bytecode all'interno di un APK) al codice macchina (simile al C++ precompilato).

Per impostazione predefinita, i macrobenchmark vengono eseguiti con CompilationMode.DEFAULT, che installa un profilo di riferimento, se disponibile, su Android 7 (livello API 24) e versioni successive. Se utilizzi Android 6 (livello API 23) o versioni precedenti, la modalità di compilazione compila completamente l'APK come comportamento predefinito del sistema.

Puoi installare un profilo di riferimento se l'app di destinazione contiene sia un profilo di riferimento sia la libreria ProfileInstaller.

Su Android 7 e versioni successive, puoi personalizzare CompilationMode per influire sulla quantità di precompilazione on-device per simulare diversi livelli di compilazione ahead-of-time (AOT) o memorizzazione nella cache JIT. Vedi CompilationMode.Full, CompilationMode.Partial, CompilationMode.None e CompilationMode.Ignore.

Questa funzionalità si basa sui comandi di compilazione ART. Ogni benchmark cancella i dati del profilo prima di iniziare, per garantire che non vi siano interferenze tra i benchmark.

StartupMode

Per eseguire l'avvio di un'attività, puoi passare una modalità di avvio predefinita: COLD, WARM o HOT. Questo parametro modifica il modo in cui l'attività viene avviata e lo stato del processo all'inizio del test.

Per scoprire di più sui tipi di avvio, consulta Tempo di avvio dell'app.

Esempi

Un progetto di esempio è disponibile nell'esempio di Macrobenchmark del repository su GitHub.

Invia il tuo feedback

Per segnalare problemi o inviare richieste di funzionalità per Jetpack Macrobenchmark, consulta l'Issue Tracker pubblico.