Performance des builds Kotlin/Native
Transformez « le build iOS est lent » en un diagnostic mesuré et un petit ensemble de correctifs sûrs. Deux règles s'appliquent partout :
- Ne sacrifiez jamais le comportement de release requis. Une boucle locale plus rapide ne doit pas changer ce que CI publie.
- Mesurez avant et après avec la même commande et le même état du build. Un correctif non mesuré n'est qu'une supposition.
Étape 0 : Classifier le scénario lent
Établissez quatre faits avant de modifier quoi que ce soit : où (local ou CI), quoi (boucle de debug ou artefact release/distribution), état (premier build, clean, warm, ou no-op), et phase (quelles tâches dominent le log). Ensuite, associez le symptôme dominant :
| Symptôme dans le log du build | Cause probable | Consulter |
|---|---|---|
Tâches linkRelease* ou *ReleaseXCFramework dans une boucle de développement local |
Construction d'artefacts de distribution pour le développement | artifacts-and-targets |
| Distribution Kotlin/Native compiler téléchargée à chaque exécution CI | ~/.konan non préservé entre les exécutions |
caching-and-gradle |
| Longue pause avant le démarrage de la première tâche | Phase de configuration, pas de configuration cache | caching-and-gradle |
| Tous les targets iOS se construisent quand un seul simulator est nécessaire | Tâche large (build, assemble, assemble*XCFramework) ou targets inutilisés |
artifacts-and-targets |
Tâches ksp* avant compileKotlinIos* |
Travail de code généré sur le chemin natif | exports-and-generated-code |
| La modification d'une petite source recompile et relie tout | Caches compilateur désactivés, ou incrementality manquante | caching-and-gradle, experimental |
Machine surchargée pendant que plusieurs tâches link* s'exécutent à la fois |
Linking natif parallèle | caching-and-gradle, caveat worker-limit |
Étape 1 : Audit et mesure
-
Exécutez l'audit statique depuis la racine du projet :
scripts/audit-native-build.sh /path/to/projectC'est en lecture seule et affiche les trouvailles
file:line(caches désactivés, tâches locales larges,transitiveExport, large configuration KSP, cache CI.konanmanquant), chacune pointant vers le fichier de référence avec le correctif. Les trouvailles sont des indices, non des verdicts — confirmez chacun par rapport à la politique du projet. -
Trouvez la commande que l'utilisateur attend réellement : un script, une étape CI, ou l'invocation Gradle à l'intérieur d'une phase de build Xcode. Optimisez cette commande, pas une tâche que vous avez choisie vous-même.
-
Exécutez-la deux fois si possible. Le premier build télécharge les composants Kotlin/Native et remplit les caches ; seuls les deuxième et les suivants sont représentatifs. Attribuez le temps par tâche avant de blâmer le compilateur :
kotlin.build.report.output=file # écrit dans build/reports/kotlin-build/Le
--scanou--profilede Gradle fonctionnent aussi. -
Si vous ne pouvez pas exécuter le build (pas d'hôte macOS, pas de Xcode), analysez les logs, les build scans, ou les métriques archivées à la place — et déclarez explicitement que la conclusion est statique.
Étape 2 : Corriger dans un ordre sûr
Appliquez les correctifs un à la fois, en remesure au fur et à mesure :
- Restaurer les défauts sains — supprimer les contournements cache/daemon, activer les caches Gradle build et configuration, garder
~/.konanwarm dans CI, mettre à jour Kotlin : references/caching-and-gradle.md - Construire seulement ce dont la boucle de feedback a besoin — une tâche spécifique par boucle, méthode d'intégration correcte, matrice de targets justifiée : references/artifacts-and-targets.md
- Réduire le coût de l'export et du code généré — supprimer
transitiveExport, réduireexport(...), limiter le travail KSP aux compilations native qui en ont besoin : references/exports-and-generated-code.md - Commutateurs expérimentaux en dernier, avec l'accord de l'utilisateur : references/experimental.md
Exemple travaillé
Un développeur sur un Mac Apple Silicon se plaint que « chaque changement de module partagé coûte 12 minutes ». Sa boucle exécute ./gradlew :shared:assembleXCFramework. Un build scan de la deuxième exécution (warm) affiche :
:shared:linkReleaseFrameworkIosArm64 348s
:shared:linkReleaseFrameworkIosX64 341s
:shared:compileKotlinIosX64 96s
:shared:linkDebugFrameworkIosSimulatorArm64 41s
:shared:compileKotlinIosSimulatorArm64 38s
configuration phase 64s
Chaîne de raisonnement :
- La boucle est local + debug + warm, mais ~690s vont à
linkRelease*— le linking de release est d'un ordre de grandeur plus lent que debug et seule CI en a besoin. Remplacez la commande locale par:shared:linkDebugFrameworkIosSimulatorArm64(ou la tâche d'embed Xcode si Xcode pilote le build). (artifacts-and-targets) - Tout le travail
iosX64sert les simulateurs Intel ; demandez si l'équipe les supporte toujours avant de supprimer le target. (artifacts-and-targets) - 64s de configuration à chaque exécution disparaissent derrière
org.gradle.configuration-cache=trueune fois testé. (caching-and-gradle) - Boucle attendue après le changement : ~40s compile + ~40s link sur les builds warm — confirmez en réexécutant la nouvelle commande deux fois et en comparant.
- CI garde
assembleXCFrameworkintact ; notez-le explicitement dans le rapport.
Vérifier
- [ ] Réexécutez la commande de baseline exacte ; comparez le build warm contre warm, pas warm contre cold.
- [ ] La deuxième exécution avec le configuration cache signale qu'il est réutilisé.
- [ ] Le log de développement local ne contient plus
linkRelease*,*ReleaseXCFramework, ou les tâches générateur supprimées. - [ ] CI produit toujours chaque artefact release requis, inchangé.
- [ ] Les tests passent et l'app s'exécute toujours depuis Xcode.
- [ ]
scripts/audit-native-build.shne signale aucune trouvaille que vous n'avez pas consciemment acceptée et documentée.
Rapportez vos changements
Terminez par une brève note de performance :
- Le scénario lent (local/CI, debug/release, cold/warm) et la preuve mesurée — ou une déclaration que l'analyse était statique.
- Chaque changement, et pourquoi il est sûr pour le comportement de release.
- Les commandes avant/après que l'utilisateur peut exécuter pour confirmer le gain.
- Compromis restants : drapeaux expérimentaux activés, targets supprimés sous une hypothèse de politique, limites worker, ou travail de code généré différé.
- Liens vers la documentation officielle pertinente ci-dessous.
Documentation officielle
| Sujet | Lien |
|---|---|
| Improving Kotlin/Native compilation time | https://kotlinlang.org/docs/native-improving-compilation-time.html |
| Kotlin Gradle plugin compilation and caches | https://kotlinlang.org/docs/gradle-compilation-and-caches.html |
| iOS integration methods | https://kotlinlang.org/docs/multiplatform-ios-integration-overview.html |
| Direct integration with Xcode | https://kotlinlang.org/docs/multiplatform/multiplatform-direct-integration.html |
| Building final native binaries and XCFrameworks | https://kotlinlang.org/docs/multiplatform/multiplatform-build-native-binaries.html |
| Kotlin/Native binary options | https://kotlinlang.org/docs/native-binary-options.html |
| KSP with Kotlin Multiplatform | https://kotlinlang.org/docs/ksp-multiplatform.html |