Une observation locale dans Instruments indique parfois « environ une seconde pour le premier affichage », alors que la version issue de la fusion se révèle nettement plus lente lors des tests à distance. Le problème vient généralement moins de l’outil que de l’absence de limites claires dans ce qui est mesuré : attente réseau, analyse du JSON, écritures en base de données et validation de l’interface sont regroupées dans une durée globale. La moindre variation de l’état de la machine suffit alors à rendre les résultats incomparables. Une méthode plus fiable consiste à délimiter les traitements métier avec os_signpost, puis à répéter l’enregistrement d’un scénario fixe avec xctrace sur un Mac dans le cloud.
Définir d’abord un parcours critique reproductible
Au lieu de demander d’emblée « quelles sont les performances globales de l’application ? », choisissez une action utilisateur dont les points de départ et d’arrivée sont stables. Par exemple, après l’ouverture de la page des commandes, mesurez le parcours entre le début de la lecture du cache local et la fin de l’association des données aux premières cellules de la liste. Décomposez-le en trois intervalles :
| Intervalle | Début | Fin | Adapté au contrôle |
|---|---|---|---|
load_cache |
Lancement de la lecture du cache | Retour des données structurées | Oui |
decode_payload |
Début du décodage | Création des objets métier | Oui |
render_first_batch |
Soumission du premier lot de modèles | Fin de la mise en page des premières vues | Oui |
wait_remote_api |
Envoi de la requête externe | Réception de la réponse | Diagnostic uniquement |
Le temps passé sur le réseau externe dépend du routage et de l’état du serveur ; il ne doit donc pas bloquer directement une fusion. Le contrôle doit porter en priorité sur des traitements internes au processus, exécutés avec des entrées fixes et pouvant être déclenchés de manière reproductible.
Un intervalle ne doit répondre qu’à une seule question. Si un même marqueur englobe à la fois le réseau, le disque et le rendu, un dépassement ne permettra toujours pas de déterminer ce qu’il faut corriger.
Ajouter des marqueurs stables et corrélables
Utilisez un subsystem et une category fixes, et ne construisez pas les noms d’intervalles à partir de données utilisateur. Pour mesurer des opérations concurrentes, créez un OSSignpostID distinct pour chaque action métier afin d’éviter d’associer le début d’une tâche à la fin d’une autre.
import os
enum PerfTrace {
static let log = OSLog(
subsystem: "com.example.mobile",
category: "CriticalPath"
)
static func begin(_ name: StaticString) -> OSSignpostID {
let id = OSSignpostID(log: log)
os_signpost(.begin, log: log, name: name, signpostID: id)
return id
}
static func end(_ name: StaticString, id: OSSignpostID) {
os_signpost(.end, log: log, name: name, signpostID: id)
}
}
Le code appelant doit impérativement utiliser defer pour fermer l’intervalle. Sinon, les branches d’erreur peuvent laisser des intervalles avec un début, mais sans fin :
let traceID = PerfTrace.begin("decode_payload")
defer { PerfTrace.end("decode_payload", id: traceID) }
let models = try decoder.decode([Item].self, from: fixtureData)
N’inscrivez aucun jeton, contenu de fichier ou identifiant utilisateur dans le texte des signposts. Les données de performance sont intégrées aux artefacts de build ; les champs doivent donc conserver une faible cardinalité et pouvoir être archivés sans risque.
Fixer les conditions d’exécution de xctrace
Commencez par vérifier dans l’interface graphique que le nom du modèle et les intervalles sont bien visibles, puis passez à la ligne de commande. L’application testée doit utiliser un jeu de données fixe, un modèle de simulateur déterminé et la même configuration de build. Arrêtez les anciens processus avant chaque enregistrement afin d’éviter que l’état d’une exécution précédente ne fausse les résultats.
set -euo pipefail
OUT="$PWD/perf-artifacts"
mkdir -p "$OUT"
xcrun xctrace record \
--template "Points of Interest" \
--time-limit 45s \
--output "$OUT/critical-path.trace" \
--launch -- "$APP_PATH" \
-PerfScenario first-batch \
-PerfFixture "$PWD/Fixtures/items.json"
xcrun xctrace export \
--input "$OUT/critical-path.trace" \
--toc > "$OUT/toc.xml"
Les expressions XPath et la structure des tables de xctrace export peuvent changer selon la version de l’outil. Le script d’analyse doit donc vérifier que le schéma attendu existe avant de lire les intervalles. N’utilisez pas durablement comme interface un script awk dépendant de la position des colonnes. Privilégiez l’analyse du XML exporté et, lors des mises à niveau, conservez une trace minimale pour effectuer un test de contrat.
Lors d’une exécution dans l’environnement distant de LemonVM, consignez d’abord la sortie de xcodebuild -version, la version du système, la configuration de build et le hachage du jeu de données de test. Après un changement de nœud ou de chaîne d’outils, établissez une nouvelle référence au lieu de comparer directement des mesures provenant d’environnements différents.
Établir les seuils à partir de plusieurs échantillons
Une exécution unique peut facilement être perturbée par le chargement initial, l’indexation en arrière-plan ou l’état thermique de la machine. Il est recommandé d’effectuer d’abord une exécution de préchauffage, puis au moins cinq enregistrements valides. Le contrôle doit comparer les médianes, tout en appliquant une limite plus tolérante à l’échantillon le plus lent.
Si la médiane de référence est de 420 ms, le seuil d’alerte peut être fixé à 1.15 fois cette valeur, soit 483 ms. Ce ratio ne doit toutefois pas être appliqué indistinctement à tous les intervalles. Une petite fonction de quelques dizaines de millisecondes et une préparation de données durant plusieurs secondes n’ont pas le même profil de variation.
Le script de décision doit distinguer au minimum trois catégories de résultats :
- Impossible de générer la trace : défaillance de l’infrastructure, sans conclure à une régression des performances.
- Intervalle cible absent : échec du scénario ou du marquage, nécessitant une correction du test.
- Échantillons valides au-dessus du seuil : échec du contrôle des performances avec archivage des preuves.
Le fichier de référence doit être examiné avec le code et contenir le nom de l’intervalle, le nombre d’échantillons, la médiane, la limite autorisée, la version de la chaîne d’outils et le hachage du jeu de données. Toute modification augmentant un seuil doit être justifiée dans la description du commit afin d’éviter que les limites ne soient relevées sans jamais être abaissées.
Éliminer les fausses régressions courantes
Si la première exécution est lente, mais que les suivantes sont stables, la cause est généralement la préparation des caches ou de l’édition de liens dynamique. Les mesures de démarrage à froid et celles du parcours à chaud doivent alors être séparées. Si toutes les exécutions sont lentes, examinez ensuite les changements apportés au code. Si une seule exécution est anormale, vérifiez d’abord si une indexation, une sauvegarde, un simulateur supplémentaire ou un build parallèle était actif à ce moment-là.
Vérifiez également les points suivants :
- les données Release et Debug ne doivent pas être mélangées ;
- chaque version système du simulateur doit disposer de sa propre référence ;
- le même état métier doit être restauré avant chaque test ;
- les tâches de performance ne doivent pas s’exécuter en parallèle de builds volumineux ;
- la trace, les résultats d’analyse et la sortie d’erreur standard doivent être archivés ensemble ;
- si l’intervalle ne peut pas être extrait, il est interdit d’enregistrer
0 mspar défaut ; - après une expiration du délai, l’application testée et les processus d’enregistrement restants doivent être arrêtés.
Si le test dépend d’une session graphique, ajoutez une vérification préalable de sa visibilité avant le lancement de la tâche. Si le test porte uniquement sur l’analyse de données ou la couche de stockage, utilisez autant que possible une cible de test indépendante afin de réduire le bruit lié à l’état de l’interface.
Produire des échecs directement exploitables
La sortie du contrôle ne doit pas se limiter à « échec des performances ». Elle doit au minimum afficher le nom de l’intervalle, la médiane actuelle, la référence, le pourcentage d’évolution, le nombre d’échantillons valides et le chemin de la trace. Par exemple :
FAIL render_first_batch
median_ms=512
baseline_ms=420
change=21.9%
samples=7
trace=perf-artifacts/run-06.trace
À la lecture du résultat, la personne chargée de la revue doit pouvoir télécharger directement la trace correspondante, accéder à l’intervalle concerné et déterminer s’il s’agit d’une régression du code métier ou d’une anomalie de l’environnement de mesure. Un contrôle de performance stable ne cherche pas à obtenir exactement les mêmes chiffres à chaque exécution, mais à rendre explicables et vérifiables les variations observées avec les mêmes entrées, la même chaîne d’outils et les mêmes conditions d’exécution. Il est plus efficace de commencer par un parcours à forte valeur, d’observer ses variations dans la durée, puis d’ajouter progressivement des intervalles, plutôt que d’introduire d’un seul coup des dizaines d’indicateurs peu fiables.
Questions fréquentes
Faut-il utiliser la moyenne ou la médiane pour le seuil de performance ?
Utilisez la médiane de plusieurs exécutions valides et ajoutez une limite sur un percentile élevé ou sur la valeur maximale. La moyenne est trop sensible à une perturbation système isolée.
Un échec d’enregistrement xctrace est-il une régression de performance ?
Non. L’échec de collecte, l’absence d’intervalle et le dépassement du seuil doivent produire des codes de sortie distincts. La performance ne se juge qu’après une mesure valide.
Tous les intervalles os_signpost doivent-ils être contrôlés en CI ?
Non. Limitez le contrôle aux parcours dont les bornes et les entrées sont reproductibles. Les mesures dépendant d’une action humaine ou d’un service externe en direct restent des indicateurs de diagnostic.
Choisissez un Mac dans le cloud LemonVM selon la durée de votre mission
Lemon M4 et Lemon M4 Pro sont disponibles à Singapour, Tokyo, Séoul, Hong Kong et dans l’ouest des États-Unis, avec une facturation à la journée, à la semaine, au mois ou au trimestre.