关于 axiom-build-performance
axiom-build-performance 是一份面向 iOS 开发者的 Xcode 构建性能分析与优化指南。它强调先测量基线,再依据构建时间线和瓶颈调整编译、脚本与并行设置。
主要功能
- 01测量构建基线并分析时间线
- 02定位编译、链接和脚本瓶颈
- 03优化 Swift 类型检查耗时
- 04调整增量编译和架构设置
- 05说明构建缓存与模块构建
适用场景
- 01调查增量构建耗时过长
- 02查找 Swift 编译缓慢的代码
- 03缩短 CI/CD 的干净构建时间
- 04排查构建性能回退
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ charleswiltgen/ axiom --skill axiom-build-performance安装后在 Claude Code 中输入/axiom-build-performance确认已加载。
详细介绍
构建性能优化
概述
系统地分析和优化 Xcode 构建性能。核心原则:先测量再优化,然后优先优化关键路径。
何时使用此技能
- 构建时间显著增加
- 增量构建耗时过长
- 想要分析构建时间线
- 需要找出编译缓慢的 Swift 代码
- 优化 CI/CD 构建时间
- 调查构建性能回归
- 启用 Xcode 26 编译缓存
- 减少显式构建模块中的模块变体
- 了解三阶段构建流程(扫描 → 模块 → 编译)
快速见效:先运行 Agent
如需自动扫描并快速改善:
/axiom:optimize-build
build-optimizer Agent 会扫描常见问题并提供即时修复。使用此技能进行深入分析。
构建性能工作流
第 1 步:测量基线(必需)
原因:无法改进未测量的内容。基线可以避免安慰剂式优化。
# Clean build (eliminates all caching)
xcodebuild clean build -scheme YourScheme
# Measure time
time xcodebuild build -scheme YourScheme
# Or use Xcode UI
Product → Perform Action → Build with Timing Summary
记录:
- 总构建时间
- 增量构建时间(修改一个文件后重新构建)
- 耗时最长的阶段(编译、链接还是脚本)
基线示例:
Clean build: 247 seconds
Incremental (1 file change): 12 seconds
Longest phase: Compile Swift sources (189s)
第 2 步:分析构建时间线(Xcode 14+)
打开方式:
- 构建项目(Cmd+B)
- 打开报告导航器(Cmd+9)
- 选择最新的构建
- 显示辅助编辑器(Cmd+Option+Return)
- 构建时间线会显示在构建日志旁边
需要关注的内容:
关键路径(构建速度的上限)
关键路径是 CPU 核心数不受限制时的最短构建时间。它由最长的依赖任务链决定。
┌─────────────────────────────────────────┐
│ Critical Path: A → B → C → D (120s) │
│ │
│ Task A: 30s ─────────┐ │
│ Task B: 40s ├─→ D: 20s │
│ Task C: 30s ─────────┘ │
│ │
│ Even with 100 CPUs, build takes 120s │
└─────────────────────────────────────────┘
目标:打破依赖关系,缩短关键路径。
时间线中的警示信号
垂直空白区域:任务正在等待输入
Timeline:
████████░░░░░░░░████████ ← Bad: idle cores waiting
████████████████████████ ← Good: continuous work
较长的水平条:单个任务耗时过长
Task A: ████████████████████ (45 seconds) ← Investigate
Task B: ███ (3 seconds) ← Fine
目标按顺序构建:目标在不必要地等待
Framework: ████████░░░░░░░░░░ ← Waiting
App: ░░░░░░░░░░████████ ← Delayed
Better (parallel):
Framework: ████████
App: ░░░░████████████
第 3 步:找出瓶颈(决策树)
编译是否是最慢的阶段? ├─ 是 → 检查类型检查性能(第 4 步) └─ 否 → 链接是否缓慢? ├─ 是 → 检查链接依赖(第 5 步) └─ 否 → 脚本是否缓慢? ├─ 是 → 优化构建阶段脚本(第 6 步) └─ 否 → 检查并行化(第 7 步)
优化模式
模式 1:类型检查性能(中高影响)
症状:“Compile Swift sources”耗时超过构建时间的 50%。
诊断:
启用编译器警告以找出耗时较长的函数:
// Add to Debug build settings → Other Swift Flags
-warn-long-function-bodies 100
-warn-long-expression-type-checking 100
构建 → Xcode 会显示警告:
MyView.swift:42: Function body took 247ms to type-check (limit: 100ms)
LoginViewModel.swift:18: Expression took 156ms to type-check (limit: 100ms)
修复缓慢的类型检查:
// ❌ SLOW - Complex type inference (247ms)
func calculateTotal(items: [Item]) -> Double {
return items
.filter { $0.isActive }
.map { $0.price * $0.quantity }
.reduce(0, +)
}
// ✅ FAST - Explicit types (12ms)
func calculateTotal(items: [Item]) -> Double {
let activeItems: [Item] = items.filter { $0.isActive }
let prices: [Double] = activeItems.map { $0.price * $0.quantity }
let total: Double = prices.reduce(0, +)
return total
}
常见的缓慢模式:
- 复杂的链式操作,缺少中间类型
- 深层嵌套的闭包
- 较大的字面量(字典、数组)
- 复杂表达式中的运算符重载
预期影响:受影响文件的编译速度提升 10%–30%。
模式 2:优化构建阶段脚本(高影响)
症状:构建时间线显示 Debug 构建中的脚本阶段耗时较长。
常见原因:
- 在 Debug 构建中运行 dSYM/Crashlytics 上传
- 每次构建都处理资源
- 未启用缓存的代码生成脚本
修复方法:让脚本根据条件运行
# ❌ BAD - Runs in ALL configurations (adds 6+ seconds to debug builds)
#!/bin/bash
firebase crashlytics upload-symbols
# ✅ GOOD - Skip in Debug
#!/bin/bash
if [ "${CONFIGURATION}" = "Release" ]; then
firebase crashlytics upload-symbols
fi
# Example savings: 6.3 seconds per incremental debug build
脚本阶段沙盒(Xcode 14+)
启用此功能可防止数据竞争并改善并行化:
Build Settings → User Script Sandboxing → YES
原因:这会要求你显式声明输入和输出,从而支持并行执行。
# Script phase with proper inputs/outputs
Input Files:
$(SRCROOT)/input.txt
$(DERIVED_FILE_DIR)/checksum.txt
Output Files:
$(DERIVED_FILE_DIR)/output.html
# Now Xcode knows dependencies and can parallelize safely
并行执行脚本:
Build Settings → FUSE_BUILD_SCRIPT_PHASES → YES
⚠️ 警告:仅当所有脚本都正确声明了输入和输出时才启用。否则会出现数据竞争。
预期影响:每次增量 Debug 构建可节省 5–10 秒。
模式 3:编译模式设置(关键)
症状:增量构建会重新编译整个模块。
检查当前设置:
# In project.pbxproj
grep "SWIFT_COMPILATION_MODE" project.pbxproj
最佳配置:
| 配置 | 设置 | 原因 |
|---|---|---|
| Debug | singlefile(增量) | 仅重新编译已更改的文件 |
| Release | wholemodule | 最大限度优化 |
// ❌ BAD - Whole module in Debug
SWIFT_COMPILATION_MODE = wholemodule; // ALL configs
// ✅ GOOD - Incremental for Debug
Debug: SWIFT_COMPILATION_MODE = singlefile;
Release: SWIFT_COMPILATION_MODE = wholemodule;
修复方法:
- Project → Build Settings
- 筛选:“Compilation Mode”
- 将 Debug 设为“Incremental”
- 将 Release 设为“Whole Module”
预期影响:增量 Debug 构建速度提升 40-60%。
模式 4:仅构建活动架构(影响很大)
症状:Debug 构建会针对多个架构(x86_64 + arm64)进行编译。
检查:
grep "ONLY_ACTIVE_ARCH" project.pbxproj
修复方法:
| 配置 | 设置 | 原因 |
|---|---|---|
| Debug | YES | 仅针对当前设备(arm64 或 x86_64)构建 |
| Release | NO | 构建通用二进制文件 |
修复方法:
- Build Settings → “Build Active Architecture Only”
- 将 Debug 设为 YES
- 保持 Release 为 NO
预期影响:Debug 构建速度提升 40-50%(架构数量减半)。
模式 5:调试信息格式(影响中等)
症状:Debug 构建不必要地生成 dSYM。
最佳配置:
| 配置 | 设置 | 原因 |
|---|---|---|
| Debug | dwarf | 内嵌调试信息,速度更快 |
| Release | dwarf-with-dsym | 单独生成 dSYM,用于崩溃报告 |
# Check current
grep "DEBUG_INFORMATION_FORMAT" project.pbxproj
修复方法:
- Build Settings → “Debug Information Format”
- 将 Debug 设为“DWARF”
- 将 Release 设为“DWARF with dSYM File”
预期影响:每次 Debug 构建节省 3-5 秒。
模式 6:目标并行化(WWDC 2018-408)
症状:Build Timeline 显示目标依次构建,而它们本可以并行构建。
检查方案配置:
- Product → Scheme → Edit Scheme
- Build 标签页
- 勾选“Parallelize Build”
- 确认目标顺序允许并行化
依赖关系图示例:
App ──┬──→ Framework A
└──→ Framework B
Framework A ──→ Utilities
Framework B ──→ Utilities
时间线(不佳——串行):
Utilities: ████████░░░░░░░░░░░░░░
Framework A: ░░░░░░░░████████░░░░░░
Framework B: ░░░░░░░░░░░░░░░░████████
App: ░░░░░░░░░░░░░░░░░░░░░░████
时间线(良好——并行):
Utilities: ████████
Framework A: ░░░░░░░░████████
Framework B: ░░░░░░░░████████
App: ░░░░░░░░░░░░░░░░████
预期影响:提升幅度与独立目标的数量成正比(例如,2 个目标并行构建约快 2 倍)。
模式 7:发出模块优化(Xcode 14+、Swift 5.7+)
说明:Swift 模块会与编译分开生成,从而更快地解除下游目标的阻塞。
之前(Xcode 13):
Framework: Compile ████████████ → Emit Module █
App: ░░░░░░░░░░░░░░░░░░░░░░░░░█████████
↑
Waiting for Framework compilation to finish
之后(Xcode 14+):
Framework: Compile ████████████
Emit Module ███
App: ░░░░░░███████████
↑
Starts as soon as module emitted
自动启用:无需配置,在 Xcode 14+ 和 Swift 5.7+ 中即可使用。
预期影响:多目标构建中的空闲时间减少 20-40%。
模式 8:急切链接(Xcode 14+)
说明:如果模块已就绪,即使尚未完成所有编译,也可以开始链接。
影响:进一步缩短依赖链中的关键路径。
自动启用:在 Xcode 14+ 中自动生效。
模式 9:编译缓存(Xcode 26+,关键)
说明:Xcode 26 引入编译缓存,可在多次干净构建之间复用先前编译的产物。
Build Settings:
Build Settings → COMPILATION_CACHE_ENABLE_CACHING → YES
工作原理:
- 根据输入文件内容和编译器标志缓存编译结果
- 支持跨干净构建复用——即使执行
xcodebuild clean后,也可复用缓存的产物 - 对于常进行干净构建的 CI/CD,可显著缩短构建时间
启用时机:
- 经常进行干净构建的 CI/CD 流水线
- 共享构建产物的团队
- 依赖关系稳定的项目
验证:
# Build with caching enabled
xcodebuild build -scheme YourScheme \
COMPILATION_CACHE_ENABLE_CACHING=YES
# Check build log for cache information
当前限制(Xcode 26):
- 尚不能缓存 Swift Package Manager 依赖项
- CompileStoryboard、CompileXIB、DataModelCompile、Ld 任务不可缓存
- 首次运行时需要时间填充缓存
预期影响:初始缓存填充后,干净构建速度提升 20-40%(适用项目最高可提升 70%+)。
模式 10:显式构建模块(Xcode 16+,影响显著)
说明:Xcode 将模块编译拆分为显式构建任务,而不是隐式按需编译。在 Xcode 26 中默认对 Swift 启用。
隐式模块的问题(Xcode 16 之前):
编译器遇到导入语句时,会按需构建模块:
Compile A.swift ─── needs UIKit ───→ (builds UIKit.pcm) ───→ continues
Compile B.swift ─── needs UIKit ───→ (waits for A to finish) ───→ uses cached
Compile C.swift ─── needs UIKit ───→ (waits) ───→ uses cached
问题:
- 一个任务会阻塞其他等待同一模块的任务
- 非确定性:谁先到达,谁就负责构建
- 构建失败难以复现(取决于任务顺序)
显式构建模块解决方案:
Xcode 现在将编译分为三个阶段:
Phase 1: SCAN Phase 2: BUILD MODULES Phase 3: COMPILE
┌──────────────────┐ ┌──────────────────────┐ ┌──────────────────┐
│ Scan A.swift │ │ Build UIKit.pcm │ │ Compile A.swift │
│ Scan B.swift │ → │ Build Foundation.pcm │ → │ Compile B.swift │
│ Scan C.swift │ │ Build SwiftUI.pcm │ │ Compile C.swift │
└──────────────────┘ └──────────────────────┘ └──────────────────┘
(fast) (parallel) (parallel)
优点:
- 构建更可靠:依赖关系明确,构建图具有确定性
- 调度效率更高:构建系统能准确知道所需内容
- 调试更便捷:调试器会复用已构建的模块(无需单独重新构建)
- 模块任务清晰可见:在构建日志中查看“Compile Clang Module”和“Compile Swift Module”
启用/禁用(如有需要):
Build Settings → Explicitly Built Modules → YES (default in Xcode 26 for Swift)
模块变体(WWDC 2024-10171)
同一模块可能会使用不同设置多次构建:
Build Log:
Compile Clang module 'UIKit' (hash: abc123) ← Variant 1
Compile Clang module 'UIKit' (hash: def456) ← Variant 2
Compile Swift module 'UIKit' (hash: ghi789) ← Variant 3
变体的常见成因:
- 不同目标之间的预处理器宏不同
- 混用 C 和 Objective-C 语言模式
- C 语言版本不同(C11 与 C17)
- 某些目标禁用了 ARC
诊断变体:
- 使用 Timing Summary 进行构建:
Product → Perform Action → Build with Timing Summary - 筛选构建日志:在筛选框中输入“modules report”
- 查看显示变体数量的 Clang 和 Swift 模块报告
减少变体(在项目/工作区级别统一设置):
# Check for macro differences
grep "GCC_PREPROCESSOR_DEFINITIONS" project.pbxproj
# Move target-specific macros to project level where possible
Project → Build Settings → Preprocessor Macros → [unify here]
示例(摘自 WWDC 2024-10171):
Before: 4 UIKit variants (2 Swift × 2 Clang)
After: 2 UIKit variants (unified settings)
Impact: Fewer module builds = faster incremental builds
预期影响:减少重复的模块编译,构建速度提升 10-30%。
注意:Swift Build(Xcode 26+):Xcode 现在使用 Apple 的开源构建引擎 Swift Build。它可实现更可预测的构建、更好的 SPM 集成,以及跨平台支持(Linux、Windows、Android)。无需配置。
测量与验证
前后对比
必要步骤:
-
基准测量(更改前):
xcodebuild clean build -scheme YourScheme 2>&1 | tee baseline.log -
每次只应用一项优化
-
测量改进效果:
xcodebuild clean build -scheme YourScheme 2>&1 | tee optimized.log -
对比:
# Extract build time from logs grep "Build succeeded" baseline.log grep "Build succeeded" optimized.log
示例:
Baseline: Build succeeded (247.3 seconds)
Optimized: Build succeeded (156.8 seconds)
Improvement: 90.5 seconds (36.6% faster)
构建时间线可视化验证
优化前:
- 查找垂直空白区域(空闲核心)
- 较长的水平条(耗时任务)
- 目标串行构建
优化后:
- 时间线应当更加“饱满”
- 水平条更短
- 目标并行构建
关键路径:应当明显缩短。
实际优化示例
示例 1:大型 iOS 应用(50 多个源文件)
基准:
- 清理构建:247 秒
- 增量构建(1 个文件):12 秒
应用的优化:
- 调试编译模式:singlefile(节省 89 秒)
- 构建活动架构:YES(节省 45 秒)
- 有条件地上传 dSYM 脚本(每次增量构建节省 6.3 秒)
结果:
- 清理构建:156 秒(快 36%)
- 增量构建:5.7 秒(快 52%)
示例 2:多框架项目
基准:
- 5 个框架串行构建
- 总计:189 秒
应用的优化:
- 在 scheme 中启用并行构建
- 修复不必要的依赖项
- 发出模块优化(Xcode 14 中自动启用)
结果:
- 总计:94 秒(快 50%)
- 关键路径从 189 秒缩短至 94 秒
常见陷阱
陷阱 1:未测量就进行优化
错误做法:“我觉得这样有帮助” → 更改设置 → 不测量效果。
问题:产生安慰剂式的改进、浪费时间,还可能未察觉实际性能倒退。
解决办法:始终先测量 → 一次只更改一项 → 再测量。
陷阱 2:优化 Release 构建速度
错误做法:将 Release 设置为增量编译以实现“更快的构建”。
为什么不好:Release 构建应针对运行时性能优化,而非构建速度。你会将 Release 构建交付给用户。
修复方法:只针对速度优化 Debug 构建。保持 Release 针对运行时进行优化。
陷阱 3:为了并行化而破坏依赖关系
错误做法:移除合理的依赖关系以“让构建并行”。
为什么不好:会导致构建错误、未定义行为和竞态条件。
修复方法:只并行化真正独立的目标。使用 Build Timeline 找出安全的机会。
陷阱 4:启用 FUSE_BUILD_SCRIPT_PHASES,却不启用沙盒
错误做法:启用并行脚本,却不声明输入和输出。
为什么不好:会导致数据竞争、非确定性构建失败和错误的构建结果。
修复方法:先启用 ENABLE_USER_SCRIPT_SANDBOXING = YES 并修复所有错误,然后再启用 FUSE_BUILD_SCRIPT_PHASES。
故障排除
问题:优化后构建仍然很慢
检查:
- 测量前是否执行了清理?(
xcodebuild clean) - 测量的构建类型是否正确?(Debug 还是 Release)
- 机器是否因过热而降频?(活动监视器 → CPU 标签页)
- 是否有其他应用在使用 CPU?(测量期间退出 Xcode、Docker 和虚拟机)
问题:Build Timeline 未显示并行化
检查:
- 是否在 Scheme 中勾选了 Parallelize Build?
- 这些目标是否真正独立?(检查依赖关系图)
- 目标是否存在不必要的显式依赖关系?
问题:未显示类型检查警告
检查:
- 是否将标志添加到了正确的配置中?(Debug,而非 Release)
- 语法是否正确?
-warn-long-function-bodies 100(带连字符) - 是否构建了正确的 Scheme?
- 是否执行了全量构建以强制重新编译?
进阶:分析构建日志
提取编译耗时
# Find slowest files to compile
xcodebuild -workspace YourApp.xcworkspace \
-scheme YourScheme \
clean build \
OTHER_SWIFT_FLAGS="-Xfrontend -debug-time-function-bodies" 2>&1 | \
grep ".[0-9]ms" | \
sort -nr | \
head -20
输出:
247.3ms MyViewModel.swift:42:1 func calculateTotal
156.8ms LoginView.swift:18:3 var body
89.2ms NetworkManager.swift:67:1 func handleResponse
...
操作:为耗时最长的函数添加显式类型。
提取构建阶段耗时
# From build log
Build target 'MyApp' (project 'MyApp')
Compile Swift source files (128.4 seconds)
Link MyApp (12.3 seconds)
Run custom shell script (6.7 seconds)
操作:优先优化耗时最长的阶段。
检查清单:构建性能审计
在确认构建已优化之前:
测量
- 已测量基准数据(清理 + 增量构建)
- 已在 Build Timeline 中验证改进
- 已记录基准数据与优化后的对比
编译设置
- Debug 使用增量编译
- Build Active Architecture = YES(仅限 Debug)
- Debug 使用 DWARF(而非 dSYM)
- 已启用类型检查警告
- 已修复类型检查耗时较长(>100ms)的函数
并行化
- 已在 Scheme 中启用 Parallelize Build
- 没有不必要的目标依赖关系
- 构建阶段脚本设为有条件执行(尽可能在 Debug 中跳过)
- 若使用并行脚本,已启用脚本沙盒
Xcode 26+(如适用)
- 已为 CI/CD 启用编译缓存(
COMPILATION_CACHE_ENABLE_CACHING) - 已检查模块变体(构建日志中的 Modules Report,参见模式 10)
- 已在项目级别统一构建设置,以减少模块变体
- 已启用显式构建模块(Xcode 26 中 Swift 的默认设置)
资源
WWDC:2018-408、2022-110364、2024-10171、2025-247
文档:/xcode/improving-the-speed-of-incremental-builds、/xcode/building-your-project-with-explicit-module-dependencies
工具:Xcode Build Timeline(Xcode 14+)、Build with Timing Summary(Product → Perform Action)、Modules Report(Xcode 16+)、Instruments Time Profiler
请记住:构建性能优化的关键在于系统化测量和有针对性的改进。优先优化关键路径,测量所有指标,并在 Build Timeline 中验证改进效果。