Benchmark dans l'intégration continue

Vous pouvez exécuter des benchmarks dans l'intégration continue (CI) pour suivre les performances au fil du temps et identifier les régressions (ou les améliorations) de performances avant même le lancement de votre application. Cette page fournit des informations de base sur le benchmark dans la CI.

Avant de commencer à utiliser l'analyse comparative dans la CI, tenez compte des différences entre la capture et l'évaluation des résultats et les tests standards.

Résultats flous

Bien que les benchmarks soient des tests instrumentés, les résultats ne se traduisent pas simplement par une réussite ou un échec. Les benchmarks fournissent des mesures temporelles pour l'appareil sur lequel elles s'exécutent. La représentation graphique des résultats au fil du temps vous permet de surveiller les modifications et d'observer le bruit dans le système de mesure.

Utiliser de vrais appareils

Exécutez des benchmarks sur des appareils Android physiques. Bien qu'ils puissent s'exécuter sur des émulateurs, nous vous le déconseillons vivement, car cela ne représente pas une expérience utilisateur réaliste. Cette approche fournit plutôt des chiffres liés au système d'exploitation hôte et aux fonctionnalités matérielles. Envisagez d'utiliser des appareils réels ou un service qui vous permet d'exécuter des tests sur des appareils réels, tels que Firebase Test Lab.

Exécuter les benchmarks

L'exécution des benchmarks dans votre pipeline CI peut être différente de l'exécution locale à partir d'Android Studio. En local, vous exécutez généralement les tests d'intégration Android avec une tâche connectedCheck Gradle. Cette tâche crée automatiquement votre APK et votre APK de test, puis exécute les tests sur le ou les appareils connectés au serveur CI. Lors de l'exécution dans la CI, ce flux doit généralement être divisé en plusieurs phases.

Créer

Pour la bibliothèque Microbenchmark, exécutez la tâche Gradle assemble[VariantName]AndroidTest, qui crée votre APK de test contenant à la fois le code de votre application et votre code testé.

La bibliothèque Macrobenchmark nécessite également de créer votre APK cible et de tester l'APK séparément. Exécutez donc les tâches Gradle :app:assemble[VariantName] et :macrobenchmark:assemble[VariantName].

Installer et exécuter

Ces étapes se font généralement sans qu'il soit nécessaire d'exécuter des tâches Gradle. Notez qu'elles peuvent être extraites selon que vous utilisez ou non un service qui vous permet d'exécuter des tests sur des appareils réels.

Pour l'installation, utilisez la commande adb install et spécifiez l'APK test (ou l'APK cible).

Exécutez la commande instrument adb shell am pour exécuter tous les benchmarks :

adb shell am instrument -w com.example.benchmark/androidx.benchmark.junit4.AndroidBenchmarkRunner

Lorsque vous utilisez la bibliothèque Macrobenchmark, utilisez l'androidx.test.runner.AndroidJUnitRunner standard en tant qu'exécuteur d'instrumentation.

Vous pouvez transmettre les mêmes arguments d'instrumentation que dans la configuration Gradle à l'aide de l'argument -e. Pour toutes les options d'arguments d'instrumentation, consultez les sections Arguments d'instrumentation de Microbenchmark et Ajouter des arguments d'instrumentation pour Macrobenchmark.

Par exemple, vous pouvez définir l'argument dryRunMode pour exécuter des microbenchmarks dans votre processus de validation des demandes d'extraction. Lorsque cet indicateur est activé, les microbenchmarks ne s'exécutent qu'en boucle simple, ce qui permet de vérifier qu'ils fonctionnent correctement, sans prendre trop de temps.

adb shell am instrument -w -e "androidx.benchmark.dryRunMode.enable" "true" com.example.benchmark/androidx.benchmark.junit4.AndroidBenchmarkRunner

Pour en savoir plus sur l'exécution de tests d'instrumentation à partir de la ligne de commande, consultez Exécuter des tests avec ADB.

Verrouiller des horloges

Le plug-in Gradle de Microbenchmark fournit la commande ./gradlew lockClocks permettant de verrouiller les horloges de processeur d'un appareil en mode root. Cela est utile pour garantir la stabilité lorsque vous avez accès aux appareils en mode root, tels que les builds « userdebug ». Vous pouvez répliquer cela à l'aide du script shell lockClocks.sh, disponible dans la source de la bibliothèque.

Vous pouvez exécuter le script directement à partir d'un hôte Linux ou Mac, ou le transférer sur l'appareil à l'aide de quelques commandes adb :

adb push path/lockClocks.sh /data/local/tmp/lockClocks.sh
adb shell /data/local/tmp/lockClocks.sh
adb shell rm /data/local/tmp/lockClocks.sh

Si vous exécutez le script shell directement sur un hôte, il envoie ces commandes à un appareil connecté.

Pour savoir pourquoi il est utile de verrouiller les horloges de processeur, découvrez comment obtenir des benchmarks cohérents.

Recueillir les résultats

Les bibliothèques d'analyse comparative génèrent des mesures au format JSON, ainsi que des traces de profilage vers un répertoire de l'appareil Android après chaque exécution de benchmark. La bibliothèque Macrobenchmark génère plusieurs fichiers de suivi Perfetto : un par itération mesurée de chaque boucle MacrobenchmarkRule.measureRepeated. Microbenchmark, en revanche, crée un seul fichier de suivi pour toutes les itérations de chaque BenchmarkRule.measureRepeated. Les fichiers de suivi de profilage sont également générés dans ce même répertoire.

Enregistrer et localiser les fichiers

Si vous exécutez les benchmarks avec Gradle, ces fichiers sont automatiquement copiés sous build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/ dans le répertoire de sortie de votre ordinateur hôte.

Si vous exécutez directement la commande adb, vous devez extraire les fichiers manuellement. Par défaut, les rapports sont enregistrés sur l'appareil dans le répertoire multimédia de la mémoire de stockage externe de l'application testée. Pour plus de commodité, la bibliothèque affiche le chemin du fichier dans Logcat. Notez que le dossier de sortie peut varier selon la version d'Android sur laquelle les benchmarks s'exécutent.

Benchmark: writing results to /storage/emulated/0/Android/media/com.example.macrobenchmark/com.example.macrobenchmark-benchmarkData.json

Vous pouvez également configurer l'emplacement où les rapports de benchmark sont enregistrés sur l'appareil à l'aide de l'argument d'instrumentation additionalTestOutputDir. Ce dossier doit être accessible en écriture par votre application.

adb shell am instrument -w -e additionalTestOutputDir /sdcard/Download/ com.example.benchmark/androidx.benchmark.junit4.AndroidBenchmarkRunner

Sur Android 10 (niveau d'API 29) ou version ultérieure, les tests de votre appli s'exécutent par défaut dans un bac à sable de stockage, ce qui empêche votre application d'accéder aux fichiers situés en dehors du répertoire spécifique à l'appli. Pour pouvoir enregistrer dans un répertoire global, tel que /sdcard/Download, transmettez l'argument d'instrumentation suivant :

-e no-isolated-storage true

Vous devez également autoriser explicitement les anciennes options de stockage dans le fichier manifeste de votre benchmark :

<application android:requestLegacyExternalStorage="true" ... >

Pour en savoir plus, consultez la section Désactiver temporairement l'espace de stockage cloisonné.

Récupérer les fichiers

Pour récupérer les fichiers générés à partir de l'appareil, exécutez la commande adb pull, qui extrait le fichier spécifié dans le répertoire actuel de votre hôte :

adb pull /storage/emulated/0/Android/media/com.example.macrobenchmark/com.example.macrobenchmark-benchmarkData.json

Pour récupérer toutes les benchmarkData d'un dossier spécifié, reportez-vous à l'extrait de code suivant :

# The following command pulls all files ending in -benchmarkData.json from the directory
# hierarchy starting at the root /storage/emulated/0/Android.
adb shell find /sdcard/Download -name "*-benchmarkData.json" | tr -d '\r' | xargs -n1 adb pull

Notez que les fichiers de suivi (.trace ou .perfetto-trace) sont enregistrés dans le même dossier que benchmarkData.json. Vous pouvez donc les collecter de la même manière.

Exemple de données de benchmark

Les bibliothèques de benchmark génèrent des fichiers JSON contenant des informations sur l'appareil d'exécution des benchmarks ainsi que sur les benchmarks réellement exécutés. L'extrait de code suivant représente le fichier JSON généré :

{
    "context": {
        "build": {
            "brand": "google",
            "device": "blueline",
            "fingerprint": "google/blueline/blueline:12/SP1A.210812.015/7679548:user/release-keys",
            "model": "Pixel 3",
            "version": {
                "sdk": 31
            }
        },
        "cpuCoreCount": 8,
        "cpuLocked": false,
        "cpuMaxFreqHz": 2803200000,
        "memTotalBytes": 3753299968,
        "sustainedPerformanceModeEnabled": false
    },
    "benchmarks": [
        {
            "name": "startup",
            "params": {},
            "className": "com.example.macrobenchmark.startup.SampleStartupBenchmark",
            "totalRunTimeNs": 4975598256,
            "metrics": {
                "timeToInitialDisplayMs": {
                    "minimum": 347.881076,
                    "maximum": 347.881076,
                    "median": 347.881076,
                    "runs": [
                        347.881076
                    ]
                }
            },
            "sampledMetrics": {},
            "warmupIterations": 0,
            "repeatIterations": 3,
            "thermalThrottleSleepSeconds": 0
        }
    ]
}

Ressources supplémentaires