Ce document présente les bonnes pratiques pour analyser les problèmes et vous assurer que vos profils de référence fonctionnent correctement pour en tirer le meilleur parti.
Problèmes de compilation
Si vous avez copié l'exemple de profils de référence dans l'appli exemple Now in Android, cela peut se traduire par des échecs du test, notamment lors de la tâche indiquant que les tests ne peuvent pas être exécutés sur un émulateur :
./gradlew assembleDemoRelease
Starting a Gradle Daemon (subsequent builds will be faster)
Calculating task graph as no configuration cache is available for tasks: assembleDemoRelease
Type-safe project accessors is an incubating feature.
> Task :benchmarks:pixel6Api33DemoNonMinifiedReleaseAndroidTest
Starting 14 tests on pixel6Api33
com.google.samples.apps.nowinandroid.foryou.ScrollForYouFeedBenchmark > scrollFeedCompilationNone[pixel6Api33] FAILED
java.lang.AssertionError: ERRORS (not suppressed): EMULATOR
WARNINGS (suppressed):
...
Ces échecs se produisent, car l'appli Now in Android utilise un appareil géré par Gradle pour générer des profils de référence. Il est normal que vous rencontriez ces échecs, car il n'est pas conseillé d'exécuter des analyses comparatives des performances sur un émulateur. Toutefois, comme vous ne collectez pas de métriques de performances lorsque vous générez des profils de référence, vous pouvez exécuter la collecte de profils de référence sur des émulateurs pour plus de commodité. Pour utiliser des profils de référence avec un émulateur, effectuez la compilation et l'installation à partir de la ligne de commande, puis définissez un argument pour activer les règles de profils de référence :
installDemoRelease -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile
Vous pouvez également créer une configuration d'exécution personnalisée dans Android Studio pour activer les profils de référence sur les émulateurs en sélectionnant Run > Edit Configurations (Exécuter > Modifier les configurations) :
![Ajouter une configuration d'exécution personnalisée pour créer des profils de référence dans l'appli Now in Android](https://developer.android.com/static/topic/performance/images/studio/run-config-nowinandroid-baselineprofiles.png?authuser=5&hl=fr)
Problèmes d'installation
Vérifiez que l'APK ou l'AAB que vous compilez provient d'une variante de compilation qui inclut des profils de référence. Le moyen le plus simple de vérifier cela consiste à ouvrir l'APK dans Android Studio en sélectionnant Build > Analyze APK (Compiler > Analyser l'APK). Ouvrez ensuite votre fichier APK, et recherchez le profil dans le fichier /assets/dexopt/baseline.prof
:
![Vérifier un profil de référence à l'aide du lecteur d'APK dans Android Studio](https://developer.android.com/static/topic/performance/images/studio/baseline-profile-in-apk.png?authuser=5&hl=fr)
Les profils de référence doivent être compilés sur l'appareil qui exécute l'appli. Pour les installations sur la plate-forme de téléchargement d'applications et pour les applis installées à l'aide de PackageInstaller
, la compilation sur l'appareil s'effectue lors du processus d'installation de l'appli. Toutefois, lorsque l'appli est téléchargée indépendamment d'Android Studio ou à l'aide d'outils de ligne de commande, la bibliothèque Jetpack ProfileInstaller
est chargée de mettre les profils en file d'attente avant qu'ils ne soient compilés lors du prochain processus d'optimisation DEX en arrière-plan. Dans ce cas, si vous souhaitez vous assurer que vos profils de référence sont utilisés, vous devrez peut-être forcer la compilation des profils de référence. ProfileVerifier
vous permet d'interroger l'état de l'installation et de la compilation du profil, comme illustré dans l'exemple suivant :
Kotlin
private const val TAG = "MainActivity" class MainActivity : ComponentActivity() { ... override fun onResume() { super.onResume() lifecycleScope.launch { logCompilationStatus() } } private suspend fun logCompilationStatus() { withContext(Dispatchers.IO) { val status = ProfileVerifier.getCompilationStatusAsync().await() when (status.profileInstallResultCode) { RESULT_CODE_NO_PROFILE -> Log.d(TAG, "ProfileInstaller: Baseline Profile not found") RESULT_CODE_COMPILED_WITH_PROFILE -> Log.d(TAG, "ProfileInstaller: Compiled with profile") RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION -> Log.d(TAG, "ProfileInstaller: Enqueued for compilation") RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING -> Log.d(TAG, "ProfileInstaller: App was installed through Play store") RESULT_CODE_ERROR_PACKAGE_NAME_DOES_NOT_EXIST -> Log.d(TAG, "ProfileInstaller: PackageName not found") RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ -> Log.d(TAG, "ProfileInstaller: Cache file exists but cannot be read") RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILE -> Log.d(TAG, "ProfileInstaller: Can't write cache file") RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION -> Log.d(TAG, "ProfileInstaller: Enqueued for compilation") else -> Log.d(TAG, "ProfileInstaller: Profile not compiled or enqueued") } } }
Java
public class MainActivity extends ComponentActivity { private static final String TAG = "MainActivity"; @Override protected void onResume() { super.onResume(); logCompilationStatus(); } private void logCompilationStatus() { ListeningExecutorService service = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor()); ListenableFuture<ProfileVerifier.CompilationStatus> future = ProfileVerifier.getCompilationStatusAsync(); Futures.addCallback(future, new FutureCallback<>() { @Override public void onSuccess(CompilationStatus result) { int resultCode = result.getProfileInstallResultCode(); if (resultCode == RESULT_CODE_NO_PROFILE) { Log.d(TAG, "ProfileInstaller: Baseline Profile not found"); } else if (resultCode == RESULT_CODE_COMPILED_WITH_PROFILE) { Log.d(TAG, "ProfileInstaller: Compiled with profile"); } else if (resultCode == RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION) { Log.d(TAG, "ProfileInstaller: Enqueued for compilation"); } else if (resultCode == RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING) { Log.d(TAG, "ProfileInstaller: App was installed through Play store"); } else if (resultCode == RESULT_CODE_ERROR_PACKAGE_NAME_DOES_NOT_EXIST) { Log.d(TAG, "ProfileInstaller: PackageName not found"); } else if (resultCode == RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ) { Log.d(TAG, "ProfileInstaller: Cache file exists but cannot be read"); } else if (resultCode == RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILE) { Log.d(TAG, "ProfileInstaller: Can't write cache file"); } else if (resultCode == RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION) { Log.d(TAG, "ProfileInstaller: Enqueued for compilation"); } else { Log.d(TAG, "ProfileInstaller: Profile not compiled or enqueued"); } } @Override public void onFailure(Throwable t) { Log.d(TAG, "ProfileInstaller: Error getting installation status: " + t.getMessage()); } }, service); } }
Les codes de résultat suivants fournissent des indices sur la cause de certains problèmes :
RESULT_CODE_COMPILED_WITH_PROFILE
- Le profil est installé, compilé et utilisé chaque fois que l'application est exécutée. Voici le résultat que vous devez obtenir.
RESULT_CODE_ERROR_NO_PROFILE_EMBEDDED
- Aucun profil n'a été trouvé dans l'APK ni l'AAB en cours d'exécution. Si cette erreur se produit, assurez-vous d'utiliser une variante de compilation qui inclut des profils de référence et que l'APK contient un profil.
RESULT_CODE_NO_PROFILE
- Aucun profil n'a été installé pour cette appli lors de son installation via une plate-forme de téléchargement d'applications ou un gestionnaire de paquets. Ce code d'erreur apparaît, car que le programme d'installation du profil ne s'est pas exécuté,
ProfileInstallerInitializer
étant désactivé. Notez que lorsque cette erreur a été signalée, un profil intégré a tout de même été détecté dans l'APK de l'appli. Lorsqu'un profil intégré est introuvable, le code d'erreur renvoyé estRESULT_CODE_ERROR_NO_PROFILE_EMBEDDED
. RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION
- Un profil a été détecté dans l'APK ou l'AAB et a été mis en file d'attente pour compilation. Lorsqu'un profil est installé par
ProfileInstaller
, il est mis en file d'attente avant d'être compilé lors de la prochaine optimisation DEX en arrière-plan du système. Le profil n'est pas actif tant que la compilation n'est pas terminée. N'essayez pas d'effectuer une analyse comparative de vos profils de référence tant que la compilation n'est pas terminée. Vous devrez peut-être forcer la compilation des profils de référence. Cette erreur ne se produit pas lorsque l'appli est installée à partir de la plate-forme de téléchargement d'applications ou du gestionnaire de paquets sur des appareils équipés d'Android 9 (API 28) ou d'une version ultérieure, car la compilation s'effectue lors de l'installation. RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING
- Un profil qui ne correspond pas est installé et l'appli a été compilée avec ce même profil.
Ceci est dû à une installation via le Google Play Store ou le gestionnaire de paquets.
Notez que ce résultat diffère de
RESULT_CODE_COMPILED_WITH_PROFILE
, car le profil qui ne correspond pas ne compile que les méthodes encore partagées entre le profil et l'application. Le profil est en fait plus petit que prévu, et moins de méthodes seront compilées que celles incluses dans le profil de référence. RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILE
ProfileVerifier
ne peut pas écrire le fichier de cache des résultats de validation. Cela peut être lié à un problème avec les autorisations du dossier de l'appli ou si l'espace disque disponible sur l'appareil est insuffisant.RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION
- ProfileVerifier
is running on an unsupported API version of Android. ProfileVerifier
n'est compatible qu'avec Android 9 (niveau d'API 28) et les versions ultérieures. RESULT_CODE_ERROR_PACKAGE_NAME_DOES_NOT_EXIST
Lorsque - un code
PackageManager.NameNotFoundException
est généré . Cela ne se produit que rarement. Essayez de désinstaller l'appli, puis de tout réinstaller. RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ
- Un précédent fichier de cache de résultats de validation existe, mais ne peut être lu. Cela ne se produit que rarement. Essayez de désinstaller l'appli, puis de tout réinstaller.
PackageManager
est interrogé pour le package d'applis, Utiliser ProfileVerifier en production
En production, vous pouvez utiliser ProfileVerifier
conjointement avec des bibliothèques de rapports d'analyse telles que Google Analytics pour Firebase pour générer des événements d'analyse indiquant l'état du profil. Par exemple, vous serez rapidement averti lorsqu'une nouvelle version de l'appli ne contenant pas de profils de référence sortira.
Forcer la compilation des profils de référence
Si la compilation de vos profils de référence est à l'état RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION
, vous pouvez forcer la compilation instantanée à l'aide de adb
:
adb shell cmd package compile -r bg-dexopt PACKAGE_NAME
Vérifier l'état de la compilation sans ProfileVerifier
Si vous n'utilisez pas ProfileVerifier
, vous pouvez vérifier l'état de la compilation à l'aide de adb
, bien qu'il ne fournisse pas d'insights aussi détaillés que ProfileVerifier
:
adb shell dumpsys package dexopt | grep -A 2 PACKAGE_NAME
L'utilisation de adb
produit un résultat semblable à celui-ci :
[com.google.samples.apps.nowinandroid.demo]
path: /data/app/~~dzJiGMKvp22vi2SsvfjkrQ==/com.google.samples.apps.nowinandroid.demo-7FR1sdJ8ZTy7eCLwAnn0Vg==/base.apk
arm64: [status=speed-profile] [reason=bg-dexopt] [primary-abi]
[location is /data/app/~~dzJiGMKvp22vi2SsvfjkrQ==/com.google.samples.apps.nowinandroid.demo-7FR1sdJ8ZTy7eCLwAnn0Vg==/oat/arm64/base.odex]
La valeur d'état indique l'état de compilation du profil et correspond à l'une des valeurs suivantes :
État de compilation | Signification |
---|---|
speed‑profile |
Un profil compilé existe et est en cours d'utilisation. |
verify |
Aucun profil compilé n'existe. |
L'état verify
ne signifie pas que l'APK ou l'AAB ne contient aucun profil, car ils peuvent être mis en file d'attente avant d'être compilés à la prochaine tâche d'optimisation DEX en arrière-plan.
La valeur de motif indique ce qui déclenche la compilation du profil. Il s'agit de l'une des valeurs suivantes :
Motif | Signification |
---|---|
install‑dm
|
Un profil de référence a été compilé manuellement ou par Google Play lors de l'installation de l'appli. |
bg‑dexopt
|
Un profil a été compilé alors que votre appareil était inactif. Il peut s'agir d'un profil de référence ou d'un profil collecté lors de l'utilisation de l'appli. |
cmdline
|
La compilation a été déclenchée à l'aide d'adb. Il peut s'agir d'un profil de référence ou d'un profil collecté lors de l'utilisation de l'appli. |
Problèmes de performance
Cette section présente quelques bonnes pratiques pour effectuer une analyse comparative correcte de vos profils de référence et bien les définir afin d'en tirer le meilleur parti.
Effectuer une analyse comparative correcte des métriques de démarrage
Vos profils de référence seront plus efficaces si vos métriques de démarrage sont bien définies. Les deux métriques clés sont le délai d'affichage initial (TTID) et le délai d'affichage total (TTFD).
Le TTID correspond au moment où l'appli affiche sa première frame. Il est important que ce délai soit aussi court que possible, car lorsqu'un élément s'affiche, l'utilisateur comprend que l'appli est en cours d'exécution. Vous pouvez d'ailleurs afficher un indicateur de progression indéterminé pour indiquer que l'appli répond bien.
Le TTFD correspond au moment où il devient possible d'interagir avec l'appli. Il est important que ce délai soit aussi court que possible pour éviter la frustration de l'utilisateur. Si vous signalez correctement le TTFD, vous indiquez au système que le code exécuté sur le chemin du TTFD fait partie du démarrage de l'application. Par conséquent, il y a davantage de chances pour que le système place ce code dans le profil.
Pour que votre appli réponde bien, le TTID et le TTFD doivent être les plus courts possible.
Le système peut détecter le TTID, l'afficher dans Logcat et le signaler dans le cadre des analyses comparatives de départ. Toutefois, le système ne peut pas déterminer le TTFD. De plus, il est de la responsabilité de l'appli de signaler lorsqu'elle atteint un état interactif entièrement dessiné. Pour ce faire, appelez reportFullyDrawn()
ou ReportDrawn
si vous utilisez Jetpack Compose. Si vous avez plusieurs tâches en arrière-plan que vous devez toutes effectuer avant que l'appli soit considérée comme entièrement dessinée, vous pouvez utiliser FullyDrawnReporter
, tel que décrit dans la section Améliorer la précision du temps de démarrage.
Profils de bibliothèque et profils personnalisés
Lors de l'analyse comparative de l'impact des profils, il peut être difficile de séparer les avantages des profils de votre application des profils fournis par des bibliothèques, telles que les bibliothèques Jetpack. Lorsque vous compilez votre APK, le plug-in Android Gradle ajoute tous les profils dans les dépendances de bibliothèque, ainsi que votre profil personnalisé. Cette approche est utile pour optimiser les performances globales et est recommandée pour vos builds. Toutefois, il est difficile de mesurer les gains de performances supplémentaires générés par votre profil personnalisé.
Un moyen rapide de voir manuellement l'optimisation supplémentaire fournie par votre profil personnalisé consiste à le supprimer et à exécuter vos analyses comparatives. Ensuite, remplacez-le et exécutez à nouveau vos analyses comparatives. La comparaison des deux vous montrera les optimisations fournies uniquement par les profils de bibliothèque, ainsi que par les profils de bibliothèque et votre profil personnalisé.
Vous pouvez automatiquement comparer les profils en créant une variante de compilation qui ne contient que les profils de bibliothèque, et non votre profil personnalisé. Comparez les analyses comparatives de cette variante à la variante de version qui contient à la fois les profils de bibliothèque et vos profils personnalisés. L'exemple suivant montre comment configurer la variante qui n'inclut que des profils de bibliothèque. Ajoutez une variante nommée releaseWithoutCustomProfile
à votre module de client de profil, qui est généralement votre module d'application:
Kotlin
android { ... buildTypes { ... // Release build with only library profiles. create("releaseWithoutCustomProfile") { initWith(release) } ... } ... } ... dependencies { ... // Remove the baselineProfile dependency. // baselineProfile(project(":baselineprofile")) } baselineProfile { variants { create("release") { from(project(":baselineprofile")) } } }
Groovy
android { ... buildTypes { ... // Release build with only library profiles. releaseWithoutCustomProfile { initWith(release) } ... } ... } ... dependencies { ... // Remove the baselineProfile dependency. // baselineProfile ':baselineprofile"' } baselineProfile { variants { release { from(project(":baselineprofile")) } } }
L'exemple de code précédent supprime la dépendance baselineProfile
de toutes les variantes et l'applique de manière sélective uniquement à la variante release
. Il peut sembler contre-intuitif que l'ajout des profils de bibliothèque se poursuit lorsque la dépendance au module du producteur de profil est supprimée. Toutefois, ce module ne sert qu'à générer votre profil personnalisé. Le plug-in Android Gradle est toujours en cours d'exécution pour toutes les variantes et doit inclure les profils de bibliothèque.
Vous devez également ajouter la nouvelle variante au module du générateur de profils. Dans cet exemple, le module producteur est nommé :baselineprofile
.
Kotlin
android { ... buildTypes { ... // Release build with only library profiles. create("releaseWithoutCustomProfile") {} ... } ... }
Groovy
android { ... buildTypes { ... // Release build with only library profiles. releaseWithoutCustomProfile {} ... } ... }
Lorsque vous exécutez l'analyse comparative depuis Android Studio, sélectionnez une variante releaseWithoutCustomProfile
pour mesurer les performances avec des profils de bibliothèque uniquement, ou sélectionnez une variante release
pour mesurer les performances avec la bibliothèque et les profils personnalisés.
Éviter le démarrage de l'application lié aux E/S
Si votre appli effectue de nombreux appels d'E/S ou appels réseau au démarrage, cela peut affecter son temps de démarrage et la précision de votre analyse comparative de départ. Ces appels lourds peuvent s'étendre sur des durées indéterminées, qui peuvent varier dans le temps, et même entre les itérations d'une même analyse comparative. Les appels d'E/S sont généralement meilleurs que les appels réseau, car ces derniers peuvent être affectés par des facteurs externes à l'appareil et sur l'appareil lui-même. Évitez les appels réseau au démarrage. Lorsqu'il s'avère inévitable d'avoir recours à l'une ou l'autre de ces options, privilégiez les E/S.
Nous vous recommandons de faire en sorte que l'architecture de votre appli prenne en charge le démarrage de l'appli sans appels réseau ou appels d'E/S, même si ce n'est utile que lors de l'analyse comparative de départ. Cela permet de garantir la variabilité la plus faible possible entre les différentes itérations de vos analyses comparatives.
Si votre application utilise Hilt, vous pouvez fournir de fausses implémentations liées aux E/S lors de l'analyse comparative dans Microbenchmark et Hilt.
Recouvrir tous les parcours utilisateur importants
Il est important de recouvrir avec précision tous les parcours utilisateur importants lors de la génération de profils de référence. Les parcours utilisateur non couverts ne seront pas améliorés par les profils de référence. Les profils de référence les plus efficaces incluent tous les parcours utilisateur courants de démarrage ainsi que les parcours utilisateur dans l'application sensibles aux performances, tels que les listes déroulantes.