kotlin-tooling-native-build-performance
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseKotlin/Native Build Performance
Kotlin/Native 构建性能优化
Turn "the iOS build is slow" into a measured diagnosis and a small set of safe
fixes. Two rules apply throughout:
- Never trade away required release behavior. A faster local loop must not change what CI publishes.
- Measure before and after with the same command and the same build state. An unmeasured fix is a guess.
将“iOS构建速度慢”转化为可量化的诊断结果和一组安全的修复方案。全程遵循两条原则:
- 绝不牺牲必要的发布行为。更快的本地构建流程不得改变CI发布的内容。
- 修复前后使用相同的命令和相同的构建状态进行测量。未经测量的修复只是猜测。
Step 0: Classify the Slow Scenario
步骤0:分类缓慢构建场景
Establish four facts before editing anything: where (local or CI),
what (debug feedback loop or release/distribution artifact), state
(first build, clean, warm, or no-op), and phase (which tasks dominate the
log). Then match the dominant symptom:
| Symptom in the build log | Likely cause | Read |
|---|---|---|
| Building distribution artifacts for development | artifacts-and-targets |
| Kotlin/Native compiler distribution downloaded on every CI run | | caching-and-gradle |
| Long pause before the first task starts | Configuration phase, no configuration cache | caching-and-gradle |
| All iOS targets build when only one simulator is needed | Broad task ( | artifacts-and-targets |
| Generated-code work on the native path | exports-and-generated-code |
| Small source edit recompiles and relinks everything | Compiler caches disabled, or missing incrementality | caching-and-gradle, experimental |
Machine overloaded while several | Parallel native linking | caching-and-gradle, worker-limit caveat |
在进行任何修改前,先明确四个事实:构建环境(本地或CI)、构建目标(调试反馈流程或发布/分发产物)、构建状态(首次构建、清理后构建、热构建或无操作构建),以及耗时阶段(构建日志中占主导的任务)。然后匹配对应的主要症状:
| 构建日志中的症状 | 可能原因 | 参考文档 |
|---|---|---|
本地开发流程中出现 | 为开发环境构建分发产物 | artifacts-and-targets |
| 每次CI运行都下载Kotlin/Native编译器分发包 | | caching-and-gradle |
| 首个任务启动前长时间停顿 | 配置阶段未启用配置缓存 | caching-and-gradle |
| 仅需构建一个模拟器目标,却构建了所有iOS目标 | 使用了宽泛的任务( | artifacts-and-targets |
| 原生路径上存在生成代码的工作 | exports-and-generated-code |
| 微小的源码修改导致全量编译和链接 | 编译器缓存被禁用,或缺少增量构建支持 | caching-and-gradle, experimental |
多个 | 并行原生链接 | caching-and-gradle, worker-limit caveat |
Step 1: Audit and Measure
步骤1:审计与测量
-
Run the static audit from the project root:bash
scripts/audit-native-build.sh /path/to/projectIt is read-only and printsfindings (disabled caches, broad local tasks,file:line, broad KSP configuration, missing CItransitiveExportcache), each pointing at the reference file with the fix. Findings are leads, not verdicts — confirm each against project policy..konan -
Find the command the user actually waits for: a script, a CI step, or the Gradle invocation inside an Xcode build phase. Optimize that command, not a task you picked yourself.
-
Run it twice when practical. The first build downloads Kotlin/Native components and fills caches; only the second and later runs are representative. Attribute time per task before blaming the compiler:properties
kotlin.build.report.output=file # writes build/reports/kotlin-build/Gradle'sor--scanwork too.--profile -
If you cannot run the build (no macOS host, no Xcode), analyze logs, build scans, or checked-in metrics instead — and state explicitly that the conclusion is static.
-
从项目根目录运行静态审计脚本:bash
scripts/audit-native-build.sh /path/to/project该脚本为只读模式,会输出格式的问题发现(如禁用的缓存、宽泛的本地任务、file:line使用、宽泛的KSP配置、CI中缺失transitiveExport缓存),每个发现都会指向包含修复方案的参考文档。这些发现是排查方向而非最终结论——需结合项目策略逐一确认。.konan -
找出用户实际等待的命令:可能是脚本、CI步骤,或是Xcode构建阶段中的Gradle调用。要优化的是这个命令,而非你自行选择的任务。
-
可行的话运行两次命令。首次构建会下载Kotlin/Native组件并填充缓存;只有第二次及之后的运行结果才具有代表性。在归咎于编译器之前,先统计每个任务的耗时:properties
kotlin.build.report.output=file # writes build/reports/kotlin-build/Gradle的或--scan参数也可用于统计。--profile -
如果无法运行构建(无macOS主机、无Xcode),则分析日志、构建扫描结果或已提交的指标——并明确说明结论是基于静态分析得出的。
Step 2: Fix in Safe Order
步骤2:按安全顺序修复
Apply fixes one at a time, re-measuring as you go:
- Restore healthy defaults — remove cache/daemon workarounds, enable
Gradle build and configuration caches, keep warm in CI, update Kotlin: references/caching-and-gradle.md
~/.konan - Build only what the feedback loop needs — one specific task per loop, correct integration method, justified target matrix: references/artifacts-and-targets.md
- Cut export and generated-code cost — drop , narrow
transitiveExport, scope KSP work to the native compilations that need it: references/exports-and-generated-code.mdexport(...) - Experimental switches last, with the user's agreement: references/experimental.md
每次应用一个修复方案,并重新测量:
- 恢复健康默认配置——移除缓存/守护进程的临时 workaround,启用Gradle构建缓存和配置缓存,在CI中保持缓存温暖,更新Kotlin版本:references/caching-and-gradle.md
~/.konan - 仅构建反馈流程所需的内容——每次流程仅执行一个特定任务,使用正确的集成方式,合理设置目标矩阵:references/artifacts-and-targets.md
- 降低导出和生成代码的成本——移除,缩小
transitiveExport范围,将KSP工作限定在需要它的原生编译任务中:references/exports-and-generated-code.mdexport(...) - 最后使用实验性开关(需用户同意):references/experimental.md
Worked Example
实战示例
A developer on an Apple Silicon Mac complains that "every shared-module
change costs 12 minutes". Their loop runs .
A build scan of the second (warm) run shows:
./gradlew :shared:assembleXCFramework:shared:linkReleaseFrameworkIosArm64 348s
:shared:linkReleaseFrameworkIosX64 341s
:shared:compileKotlinIosX64 96s
:shared:linkDebugFrameworkIosSimulatorArm64 41s
:shared:compileKotlinIosSimulatorArm64 38s
configuration phase 64sReasoning chain:
- The loop is local + debug + warm, but ~690s goes to — release linking is an order of magnitude slower than debug and only CI needs it. Replace the local command with
linkRelease*(or the Xcode embed task if Xcode drives the build). (artifacts-and-targets):shared:linkDebugFrameworkIosSimulatorArm64 - All work serves Intel simulators; ask whether the team still supports them before removing the target. (artifacts-and-targets)
iosX64 - 64s of configuration on every run disappears behind
once trialed. (caching-and-gradle)
org.gradle.configuration-cache=true - Expected loop after the change: ~40s compile + ~40s link on warm builds — confirm by re-running the new command twice and comparing.
- CI keeps untouched; note that explicitly in the report.
assembleXCFramework
一位使用Apple Silicon Mac的开发者抱怨“每次修改共享模块都要花费12分钟”。他们的构建流程运行。第二次(热)构建的扫描结果显示:
./gradlew :shared:assembleXCFramework:shared:linkReleaseFrameworkIosArm64 348s
:shared:linkReleaseFrameworkIosX64 341s
:shared:compileKotlinIosX64 96s
:shared:linkDebugFrameworkIosSimulatorArm64 41s
:shared:compileKotlinIosSimulatorArm64 38s
configuration phase 64s推理过程:
- 该构建流程是本地+调试+热构建,但约690秒耗时在任务上——发布版本的链接速度比调试版本慢一个数量级,且只有CI才需要它。将本地命令替换为
linkRelease*(如果由Xcode驱动构建,则替换为Xcode的嵌入任务)。(artifacts-and-targets):shared:linkDebugFrameworkIosSimulatorArm64 - 所有任务是为Intel模拟器服务的;在移除该目标前,先确认团队是否仍支持Intel模拟器。(artifacts-and-targets)
iosX64 - 每次运行花费的64秒配置时间,在启用后会消失(需先试用验证)。(caching-and-gradle)
org.gradle.configuration-cache=true - 修改后的预期构建流程:热构建下约40秒编译+40秒链接——通过重新运行新命令两次并对比结果来确认。
- CI仍保持不变;需在报告中明确说明这一点。
assembleXCFramework
Verify
验证
- Re-run the exact baseline command; compare warm build against warm build, not warm against cold.
- Second run with the configuration cache reports it is being reused.
- The local development log no longer contains ,
linkRelease*, or removed generator tasks.*ReleaseXCFramework - CI still produces every required release artifact, unchanged.
- Tests pass and the app still runs from Xcode.
- reports no findings you have not consciously accepted and documented.
scripts/audit-native-build.sh
- 重新运行完全相同的基准命令;对比热构建与热构建的结果,而非热构建与冷构建。
- 第二次运行配置缓存时,报告显示缓存已被复用。
- 本地开发日志中不再包含、
linkRelease*或已移除的生成器任务。*ReleaseXCFramework - CI仍能生成所有所需的发布产物,且产物未发生变化。
- 测试通过,应用仍可从Xcode正常运行。
- 报告中没有你未主动接受并记录的问题。
scripts/audit-native-build.sh
Report Your Changes
报告修改内容
Close with a short performance note:
- The slow scenario (local/CI, debug/release, cold/warm) and the measured evidence — or a statement that the analysis was static.
- Each change, and why it is safe for release behavior.
- The before/after commands the user can run to confirm the win.
- Remaining tradeoffs: experimental flags enabled, targets removed under a policy assumption, worker limits, or generated-code work deferred.
- Links to the relevant official documentation below.
以简短的性能说明收尾:
- 缓慢构建的场景(本地/CI、调试/发布、冷/热构建)及量化证据——或说明分析是基于静态数据得出的。
- 每项修改,以及为何该修改不会影响发布行为的安全性。
- 用户可运行的修复前后命令,以确认优化效果。
- 剩余的权衡:启用的实验性标志、基于策略假设移除的目标、worker限制,或延迟的生成代码工作。
- 下方相关官方文档的链接。
Official Documentation
官方文档
| Topic | Link |
|---|---|
| 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 |
| 主题 | 链接 |
|---|---|
| 提升Kotlin/Native编译速度 | https://kotlinlang.org/docs/native-improving-compilation-time.html |
| Kotlin Gradle插件编译与缓存 | https://kotlinlang.org/docs/gradle-compilation-and-caches.html |
| iOS集成方式 | https://kotlinlang.org/docs/multiplatform-ios-integration-overview.html |
| 与Xcode直接集成 | https://kotlinlang.org/docs/multiplatform/multiplatform-direct-integration.html |
| 构建最终原生二进制文件和XCFrameworks | https://kotlinlang.org/docs/multiplatform/multiplatform-build-native-binaries.html |
| Kotlin/Native二进制选项 | https://kotlinlang.org/docs/native-binary-options.html |
| Kotlin Multiplatform中的KSP | https://kotlinlang.org/docs/ksp-multiplatform.html |