1. Avant de commencer
Dans cet atelier de programmation, vous découvrirez comment utiliser la bibliothèque macrobenchmark. Vous évaluerez le temps de démarrage de l'application, qui est une métrique clé en termes d'engagement utilisateur, et le temps de rendu, qui indique à quel niveau des à-coups peuvent se produire dans l'application.
Ce dont vous avez besoin
- Android Studio Dolphin (2021.3.1) ou version ultérieure
- Connaissance de Kotlin
- Principes de base des tests sur Android
- Un appareil Android physique avec Android 6 (niveau d'API 23) ou version ultérieure
Objectifs de l'atelier
- Ajouter un module d'analyse comparative à une application existante
- Mesurer le temps de démarrage et le temps de rendu de l'application
Points abordés
- Mesurer les performances des applications de manière fiable
2. Configuration
Pour commencer, clonez le dépôt GitHub à partir de la ligne de commande à l'aide de la commande suivante :
$ git clone https://github.com/googlecodelabs/android-performance.git
Vous pouvez également télécharger deux fichiers ZIP :
Ouvrir le projet dans Android Studio
- Dans la fenêtre "Welcome to Android Studio" (Bienvenue dans Android Studio), sélectionnez Open an existing Project (Ouvrir un projet existant).
- Sélectionnez le dossier
[Download Location]/android-performance/benchmarking
(conseil : assurez-vous de sélectionner le répertoirebenchmarking
contenantbuild.gradle
.) - Une fois qu'Android Studio a importé le projet, assurez-vous de pouvoir exécuter le module
app
pour créer l'exemple d'application qui fera l'objet de l'analyse comparative.
3. Présentation de Jetpack Macrobenchmark
La bibliothèque Jetpack<Macrobenchmark mesure les performances des interactions essentielles de l'utilisateur final, comme le démarrage, les interactions avec l'interface utilisateur et les animations. Elle vous permet de contrôler directement l'environnement de performances que vous testez. Grâce à elle, vous pouvez contrôler la compilation, le démarrage et l'arrêt de votre application afin de mesurer directement le démarrage de l'application, les temps de rendu et les sections du code tracé.
Jetpack Macrobenchmark vous permet d'effectuer les opérations suivantes :
- Mesurer l'application plusieurs fois avec des modèles de lancement et des vitesses de défilement déterministes
- Atténuer les écarts de performances en calculant la moyenne des résultats sur plusieurs cycles de test
- Contrôler l'état de compilation de votre application, un facteur important pour la stabilité des performances
- Vérifier les performances réelles avec la reproduction locale des optimisations à l'installation effectuées par le Google Play Store
Les instrumentations qui utilisent cette bibliothèque n'appellent pas directement le code de votre application. À la place, elles naviguent dans l'application comme le ferait un utilisateur, en appuyant sur des options, en effectuant des clics, en balayant l'écran, etc. La mesure s'effectue sur l'appareil au cours de ces interactions. Si vous souhaitez évaluer directement certaines parties du code de l'application, reportez-vous plutôt à Jetpack Microbenchmark.
L'élaboration d'une analyse comparative est semblable à l'écriture d'un test d'instrumentation, sauf que vous n'avez pas besoin de vérifier l'état de l'application. Ces analyses utilisent la syntaxe JUnit (@RunWith
, @Rule
, @Test
, etc.), mais les tests sont exécutés dans un processus distinct pour permettre le redémarrage ou la compilation préalable de l'application. Cette approche nous permet d'exécuter votre application sans interférer avec ses états internes, comme le ferait un utilisateur. Nous utilisons UiAutomator
pour interagir avec l'application cible.
Application exemple
Dans cet atelier de programmation, vous allez utiliser JetSnack à titre d'exemple. Il s'agit d'une application de commande de collations virtuelle qui utilise Jetpack Compose. Pour mesurer les performances d'une application, vous n'avez pas besoin de connaître son architecture. Vous devez comprendre le comportement de l'application et la structure de l'interface utilisateur afin de pouvoir accéder aux éléments de l'interface à partir des analyses comparatives. Exécutez l'application et familiarisez-vous avec les écrans de base en commandant les collations de votre choix.
4. Ajouter la bibliothèque Macrobenchmark
Macrobenchmark requiert l'ajout d'un module Gradle au projet. Pour ce faire, le moyen le plus simple consiste à utiliser l'assistant du module Android Studio.
Ouvrez la boîte de dialogue de création de module. Par exemple, effectuez un clic droit sur le projet ou le module dans le volet Project (Projet), puis sélectionnez New > Module (Nouveau > Module).
Sélectionnez Benchmark dans le volet Templates (Modèles). Assurez-vous que Macrobenchmark est sélectionné comme type de module "Benchmark", puis vérifiez que les détails correspondent à vos attentes :
- Target application (Application cible) : application à comparer
- Module name (Nom du module) : nom du module d'analyse comparative Gradle
- Package name (Nom du package) : nom du package pour les analyses comparatives
- Minimum SDK (SDK minimal) : vous devez disposer d'Android 6 (niveau d'API 23) ou version ultérieure.
Cliquez sur Terminer.
Modifications apportées par l'assistant de module
L'assistant de module apporte plusieurs modifications à votre projet.
Il ajoute un module Gradle nommé macrobenchmark
(ou le nom que vous avez sélectionné dans l'assistant). Ce module utilise le plug-in com.android.test
, qui indique à Gradle de ne pas l'inclure dans votre application. Il ne peut donc contenir que du code de test (ou des analyses comparatives).
L'assistant modifie également le module de l'application cible que vous avez sélectionné. Plus précisément, il ajoute le type de compilation benchmark
au module :app
build.gradle
, comme dans l'extrait de code suivant :
benchmark {
initWith buildTypes.release
signingConfig signingConfigs.debug
matchingFallbacks = ['release']
debuggable false
}
Ce type de compilation doit émuler votre type de compilation release
aussi fidèlement que possible. La différence avec le type de compilation release
est que signingConfig
est défini sur debug
, ce qui est nécessaire pour compiler l'application en local sans avoir besoin d'un keystore de production.
Cependant, comme l'option debuggable
est désactivée, l'assistant ajoute la balise <profileable>
à AndroidManifest.xml
pour permettre aux analyses comparatives de profiler votre application avec les performances des différentes versions.
<application>
<profileable
android:shell="true"
tools:targetApi="q" />
</application>
Pour en savoir plus sur le fonctionnement de <profileable>
, consultez la documentation.
Enfin, l'assistant crée un échafaudage pour comparer les temps de démarrage (nous y reviendrons à l'étape suivante).
Vous pouvez maintenant commencer à écrire les analyses comparatives.
5. Mesurer le démarrage de l'application
Le temps de démarrage de l'application, ou le temps nécessaire pour que l'on commence à pouvoir l'utiliser, est une métrique clé ayant un impact sur l'engagement utilisateur. L'assistant de module crée une classe de test ExampleStartupBenchmark
capable de mesurer le temps de démarrage de l'application. Elle se présente comme suit :
@RunWith(AndroidJUnit4::class)
class ExampleStartupBenchmark {
@get:Rule
val benchmarkRule = MacrobenchmarkRule()
@Test
fun startup() = benchmarkRule.measureRepeated(
packageName = "com.example.macrobenchmark_codelab",
metrics = listOf(StartupTimingMetric()),
iterations = 5,
startupMode = StartupMode.COLD,
){
pressHome()
startActivityAndWait()
}
}
Que signifient tous les paramètres ?
Lors de l'écriture d'une analyse comparative, votre point d'entrée est la fonction measureRepeated
de MacrobenchmarkRule
. Cette fonction s'occupe de tous les éléments de référence, mais vous devez spécifier les paramètres suivants :
packageName
: les analyses comparatives s'exécutent dans un processus distinct de l'application testée. Vous devez donc spécifier l'application à mesurer.metrics
: type d'informations que vous souhaitez mesurer pendant l'analyse comparative. Dans notre cas, nous nous intéresserons au temps de démarrage de l'application. Consultez la documentation pour découvrir d'autres types de métriques.iterations
: nombre de répétitions de l'analyse comparative. Plus le nombre d'itérations est élevé, plus les résultats sont stables, mais le temps d'exécution augmente. Le nombre idéal d'itérations dépend du niveau de bruit de cette métrique spécifique dans votre application.startupMode
: permet de définir la façon dont votre application doit démarrer au début de l'analyse comparative.COLD
,WARM
etHOT
sont disponibles. Nous utiliseronsCOLD
, qui représente la majeure partie du travail que l'application doit effectuer.measureBlock
(dernier paramètre lambda) : cette fonction vous permet de définir les actions que vous souhaitez mesurer pendant l'analyse comparative (déclencher une activité, cliquer sur des éléments de l'interface utilisateur, faire défiler l'écran, balayer l'écran, etc.). Macrobenchmark collectera les métriques (metrics
) définies dans ce bloc.
Écrire les actions de l'analyse comparative
Macrobenchmark réinstalle et redémarre votre application. Veillez à ce que les interactions soient indépendantes de l'état de cette dernière. Macrobenchmark fournit plusieurs fonctions et paramètres utiles pour interagir avec votre application.
La plus importante est startActivityAndWait()
. Cette fonction lance votre activité par défaut et attend qu'elle affiche le premier frame avant de continuer à suivre les instructions de l'analyse comparative. Si vous souhaitez démarrer une autre activité ou modifier l'intent de départ, vous pouvez utiliser le paramètre facultatif intent
ou block
.
La fonction pressHome()
est également utile. Elle vous permet de rétablir une condition de base de l'analyse comparative sans avoir à anéantir votre application à chaque itération (par exemple, lorsque vous utilisez StartupMode.HOT
).
Pour toutes les autres interactions, vous pouvez utiliser le paramètre device
, qui permet de rechercher des éléments de l'interface utilisateur, de faire défiler l'écran, d'afficher un contenu spécifique, etc.
Maintenant que nous avons défini une analyse comparative de départ, nous allons l'exécuter à l'étape suivante.
6. Exécuter l'analyse comparative
Avant d'exécuter le test d'analyse comparative, assurez-vous d'avoir sélectionné la bonne variante de compilation dans Android Studio :
- Sélectionnez le volet Build Variants (Variantes de compilation).
- Remplacez Active Build Variant (Variante de compilation par active) par benchmark (analyse comparative).
- Attendez qu'Android Studio soit synchronisé.
Si vous ne le faites pas, l'analyse comparative échouera au moment de l'exécution et affichera une erreur indiquant que vous ne devez pas comparer une application debuggable
:
java.lang.AssertionError: ERRORS (not suppressed): DEBUGGABLE WARNINGS (suppressed): ERROR: Debuggable Benchmark Benchmark is running with debuggable=true, which drastically reduces runtime performance in order to support debugging features. Run benchmarks with debuggable=false. Debuggable affects execution speed in ways that mean benchmark improvements might not carry over to a real user's experience (or even regress release performance).
Vous pouvez supprimer temporairement cette erreur avec l'argument d'instrumentation androidx.benchmark.suppressErrors = "DEBUGGABLE"
. Suivez la même procédure que pour l'étape Exécuter des analyses comparatives sur Android Emulator.
Vous pouvez désormais exécuter les analyses comparatives comme vous le feriez pour des tests d'instrumentation. Vous pouvez exécuter la fonction de test ou l'intégralité de la classe avec l'icône représentant une gouttière qui se trouve à côté.
Assurez-vous d'avoir sélectionné un appareil physique. Dans le cas contraire, les analyses comparatives sur l'émulateur Android échoueront lors de l'exécution et un avertissement vous indiquera que les résultats seront incorrects si vous procédez. Bien que, techniquement, vous puissiez procéder à l'exécution sur un émulateur, vous mesurez essentiellement les performances de la machine hôte. Si sa charge est importante, vos analyses comparatives seront plus lentes, et inversement.
Une fois que vous avez exécuté l'analyse comparative, l'application est recompilée, puis exécute vos analyses. Les analyses comparatives démarreront, s'arrêteront et même réinstalleront votre application plusieurs fois en fonction des iterations
que vous avez définies.
7. (Facultatif) Exécuter des analyses comparatives sur Android Emulator
Si vous ne disposez pas d'un appareil physique et que vous souhaitez tout de même exécuter les analyses comparatives, vous pouvez masquer l'erreur d'exécution avec l'argument d'instrumentation androidx.benchmark.suppressErrors = "EMULATOR"
.
Pour masquer l'erreur, modifiez la configuration de l'exécution :
- Sélectionnez "Modifier les configurations" dans le menu d'exécution :
- Dans la fenêtre ouverte, sélectionnez l'icône Options à côté de l'option "Arguments d'instrumentation".
- Ajoutez le paramètre supplémentaire d'instrumentation en cliquant sur ➕ et en saisissant les détails.
- Cliquez sur OK pour confirmer votre choix. Cet argument devrait apparaître à la ligne "Instrumentation arguments" (Arguments d'instrumentation).
- Cliquez sur OK pour confirmer la configuration d'exécution.
Si vous avez besoin de la conserver définitivement dans votre codebase, cela est possible depuis build.gradle
dans le module :macrobenchmark
:
defaultConfig {
// ...
testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = 'EMULATOR'
}
8. Interpréter les résultats de démarrage
Une fois l'analyse comparative terminée, les résultats s'affichent directement dans Android Studio, comme illustré dans la capture d'écran suivante :
Dans notre exemple, on peut voir que pour le temps de démarrage du Google Pixel 7, la valeur minimale est de 294,8 ms, la médiane est de 301,5 ms et la valeur maximale est de 314,8 ms. Notez que vous pouvez obtenir des résultats différents sur votre appareil lorsque vous effectuez les mêmes analyses comparatives. Les résultats dépendent de nombreux facteurs, par exemple :
- La puissance de l'appareil
- La version du système utilisée
- Les applications exécutées en arrière-plan
C'est pourquoi il est important de comparer les résultats sur le même appareil et, idéalement, dans le même état. Dans le cas contraire, vous constaterez d'importantes différences. Si vous ne pouvez pas garantir le même état, vous pouvez augmenter le nombre d'iterations
afin de traiter correctement les résultats atypiques.
Pour permettre de faire vos propres enquêtes, la bibliothèque Macrobenchmark enregistre des traces système pendant l'exécution des analyses comparatives. Pour plus de commodité, Android Studio marque chaque itération et les durées mesurées sous forme de liens vers la trace système. Vous pouvez ainsi facilement l'ouvrir pour examiner le problème.
9. Déclarer quand l'application sera prête à l'emploi (exercice facultatif)
L'application Macrobenchmark permet de mesurer automatiquement le temps nécessaire pour afficher le premier frame de l'application (timeToInitialDisplay
). Toutefois, il est courant que le chargement du contenu d'une application ne se termine qu'après l'affichage du premier frame. Il peut être utile de savoir combien de temps l'utilisateur doit attendre avant que l'application soit utilisable. C'est ce qu'on appelle le temps d'affichage complet : l'application a entièrement chargé le contenu, et l'utilisateur peut interagir avec celui-ci. La bibliothèque Macrobenchmark permet de détecter automatiquement cette durée, mais vous devez ajuster votre application pour indiquer quand tout est affiché avec la fonction Activity.reportFullyDrawn()
.
L'application exemple montre une barre de progression simple jusqu'à ce que les données soient chargées. Il est donc important d'attendre que les données soient prêtes et que la liste des collations soit créée et qu'elle apparaisse correctement. Modifions cet exemple d'application et ajoutons l'appel reportFullyDrawn()
.
Ouvrez le fichier Feed.kt
dans le package .ui.home
à partir du volet Project (Projet).
Dans ce fichier, recherchez le composable SnackCollectionList
qui est responsable de la composition de la liste de collations.
Vérifiez que les données sont prêtes. Vous savez que tant que le contenu ne sera pas prêt, vous obtiendrez une liste vide à partir du paramètre snackCollections
. Vous pouvez donc utiliser le composable ReportDrawnWhen
, qui se chargera du signalement dès que le prédicat sera vrai.
ReportDrawnWhen { snackCollections.isNotEmpty() }
Box(modifier) {
LazyColumn {
// ...
}
Vous pouvez également utiliser le composable ReportDrawnAfter{}
, qui accepte la fonction suspend
et attend la fin de cette fonction. Ainsi, vous pouvez attendre que certaines données soient chargées de manière asynchrone ou qu'une animation se termine.
Ensuite, vous devez ajuster ExampleStartupBenchmark
pour attendre l'affichage du contenu. Sinon, l'analyse comparative risque de se terminer avec le premier frame affiché et d'ignorer la métrique.
L'analyse comparative de départ n'attend que le premier frame affiché. Le délai d'attente lui-même est inclus dans la fonction startActivityAndWait()
.
@Test
fun startup() = benchmarkRule.measureRepeated(
packageName = "com.example.macrobenchmark_codelab",
metrics = listOf(StartupTimingMetric()),
iterations = 5,
startupMode = StartupMode.COLD,
) {
pressHome()
startActivityAndWait()
// TODO wait until content is ready
}
Dans le cas présent, vous pouvez attendre que la liste de contenu ait des enfants et ajouter ainsi wait()
comme dans l'extrait de code suivant :
@Test
fun startup() = benchmarkRule.measureRepeated(
//...
) {
pressHome()
startActivityAndWait()
val contentList = device.findObject(By.res("snack_list"))
val searchCondition = Until.hasObject(By.res("snack_collection"))
// Wait until a snack collection item within the list is rendered
contentList.wait(searchCondition, 5_000)
}
Pour expliquer ce qui se passe dans l'extrait :
- Nous trouvons la liste des collations grâce à
Modifier.testTag("snack_list")
. - Nous définissons la condition de recherche qui utilise
snack_collection
comme élément à attendre. - Nous utilisons la fonction
UiObject2.wait
pour attendre la condition dans l'objet UI avec un délai avant expiration de cinq secondes.
Vous pouvez maintenant exécuter à nouveau l'analyse comparative. La bibliothèque mesure automatiquement timeToInitialDisplay
et timeToFullDisplay
, comme dans la capture d'écran suivante :
Comme vous pouvez le constater, la différence entre TTID et TTFD dans notre cas est de 413 ms. Cela signifie que même si un premier frame s'affiche en 319,4 ms, les utilisateurs ne pourront pas faire défiler la liste avant un délai supplémentaire de 413 ms.
10. Effectuer une analyse comparative du temps de rendu
Une fois que les utilisateurs sont redirigés vers votre application, la deuxième métrique déterminante est la fluidité de l'application, ou ce que nous appelons "l'abandon ou non de frames". Pour mesurer cette métrique, nous utiliserons FrameTimingMetric
.
Imaginons que vous souhaitiez mesurer le comportement de défilement de la liste d'éléments et que vous ne souhaitiez rien mesurer avant ce scénario. Vous devez répartir l'analyse comparative en interactions mesurées et interactions non mesurées. Pour ce faire, nous utiliserons le paramètre lambda setupBlock
.
Dans les interactions non mesurées (définies dans setupBlock
), nous démarrerons l'activité par défaut. Dans les interactions mesurées (définies dans measureBlock
), nous identifierons l'élément de liste UI, ferons défiler la liste et attendrons que l'écran affiche le contenu. Si vous ne répartissez pas les interactions en deux parties, vous ne pourrez pas faire la différence entre les frames générés lors du démarrage de l'application et ceux générés lors du défilement de la liste.
Créer une analyse comparative du temps de rendu
Pour atteindre le flux mentionné, nous allons créer une classe ScrollBenchmarks
avec un test scroll()
qui contiendra l'analyse comparative du temps de rendu du frame. Commencez par créer la classe de test avec la règle d'analyse comparative et une méthode de test vide :
@RunWith(AndroidJUnit4::class)
class ScrollBenchmarks {
@get:Rule
val benchmarkRule = MacrobenchmarkRule()
@Test
fun scroll() {
// TODO implement scrolling benchmark
}
}
Ajoutez ensuite le squelette de l'analyse comparative avec les paramètres requis.
@Test
fun scroll() {
benchmarkRule.measureRepeated(
packageName = "com.example.macrobenchmark_codelab",
iterations = 5,
metrics = listOf(FrameTimingMetric()),
startupMode = StartupMode.COLD,
setupBlock = {
// TODO Add not measured interactions.
}
) {
// TODO Add interactions to measure list scrolling.
}
}
L'analyse comparative utilise les mêmes paramètres que l'analyse comparative startup
, à l'exception du paramètre metrics
et de setupBlock
. FrameTimingMetric
collecte les temps de rendu des frames générés par l'application.
À présent, remplissons setupBlock
. Comme mentionné précédemment, les interactions ne sont pas mesurées par l'analyse comparative dans ce lambda. Vous pouvez utiliser ce bloc pour ouvrir l'application et attendre que le premier frame soit affiché.
@Test
fun scroll() {
benchmarkRule.measureRepeated(
packageName = "com.example.macrobenchmark_codelab",
iterations = 5,
metrics = listOf(FrameTimingMetric()),
startupMode = StartupMode.COLD,
setupBlock = {
// Start the default activity, but don't measure the frames yet
pressHome()
startActivityAndWait()
}
) {
// TODO Add interactions to measure list scrolling.
}
}
À présent, écrivons measureBlock
(le dernier paramètre lambda). Tout d'abord, puisque l'envoi d'éléments à la liste de collations est une opération asynchrone, vous devez attendre que le contenu soit prêt.
benchmarkRule.measureRepeated(
// ...
) {
val contentList = device.findObject(By.res("snack_list"))
val searchCondition = Until.hasObject(By.res("snack_collection"))
// Wait until a snack collection item within the list is rendered
contentList.wait(searchCondition, 5_000)
// TODO Scroll the list
}
Si vous ne souhaitez pas mesurer la configuration initiale de la mise en page, vous pouvez attendre que le contenu soit prêt dans setupBlock
.
Définissez ensuite les marges de manoeuvre au niveau de la liste de collations. Vous devez effectuer cette opération. Dans le cas contraire, l'application risque de déclencher la navigation système et de se fermer au lieu de faire défiler le contenu.
benchmarkRule.measureRepeated(
// ...
) {
val contentList = device.findObject(By.res("snack_list"))
val searchCondition = Until.hasObject(By.res("snack_collection"))
// Wait until a snack collection item within the list is rendered
contentList.wait(searchCondition, 5_000)
// Set gesture margin to avoid triggering system gesture navigation
contentList.setGestureMargin(device.displayWidth / 5)
// TODO Scroll the list
}
En fin de compte, vous faites défiler la liste avec le geste fling()
(vous pouvez également utiliser scroll()
ou swipe()
selon la vitesse de défilement souhaitée) et attendez que l'interface utilisateur devienne inactive.
benchmarkRule.measureRepeated(
// ...
) {
val contentList = device.findObject(By.res("snack_list"))
val searchCondition = Until.hasObject(By.res("snack_collection"))
// Wait until a snack collection item within the list is rendered
contentList.wait(searchCondition, 5_000)
// Set gesture margin to avoid triggering gesture navigation
contentList.setGestureMargin(device.displayWidth / 5)
// Scroll down the list
contentList.fling(Direction.DOWN)
// Wait for the scroll to finish
device.waitForIdle()
}
La bibliothèque mesure les temps de rendu des frames générés par notre application lors de l'exécution des actions définies.
Vous pouvez maintenant utiliser l'analyse comparative.
Exécuter l'analyse comparative
Vous pouvez exécuter l'analyse comparative de la même manière que l'analyse comparative de départ. Cliquez sur l'icône en forme de gouttière à côté du test, puis sélectionnez Run ‘scroll() (Exécuter 'scroll()).
Pour en savoir plus sur l'exécution de l'analyse comparative, consultez l'étape Exécuter l'analyse comparative.
Comprendre les résultats
FrameTimingMetric
renvoie la durée des images en millisecondes (frameDurationCpuMs
) aux 50e, 90e, 95e et 99e centiles. Sur Android 12 (niveau d'API 31) ou version ultérieure, cette valeur indique également la durée pendant laquelle les frames ont dépassé la limite (frameOverrunMs
). Cette valeur peut être négative, indiquant qu'il restait du temps pour générer le frame.
Vous pouvez voir dans les résultats que la valeur médiane (P50) pour créer un frame sur un Google Pixel 7 était de 3,8 ms, soit 6,4 ms en dessous de la limite de temps de rendu. Toutefois, il est possible que certains frames aient été ignorés dans le centile supérieur à 99 (P99), car la production des frames a pris 35,7 ms, soit 33,2 ms de plus que la limite.
Comme pour les résultats sur le démarrage de l'application, vous pouvez cliquer sur iteration
pour ouvrir la trace système enregistrée pendant l'analyse comparative et examiner ce qui a contribué aux délais obtenus.
11. Félicitations
Félicitations, vous avez mené à bien cet atelier de programmation visant à mesurer les performances avec Jetpack Macrobenchmark.
Et maintenant ?
Consultez l'atelier de programmation Améliorer les performances de l'application avec les profils de référence. Consultez également le dépôt GitHub d'exemples de performances, qui contient le fichier Macrobenchmark et d'autres exemples de performances.