mcpField
A
axiom-build-performance

axiom-build-performance

github.com/charleswiltgen开发1.2k171 次安装今天更新
代码仓库

关于 axiom-build-performance

axiom-build-performance 是一份面向 iOS 开发者的 Xcode 构建性能分析与优化指南。它强调先测量基线,再依据构建时间线和瓶颈调整编译、脚本与并行设置。

主要功能

  1. 01测量构建基线并分析时间线
  2. 02定位编译、链接和脚本瓶颈
  3. 03优化 Swift 类型检查耗时
  4. 04调整增量编译和架构设置
  5. 05说明构建缓存与模块构建

适用场景

  1. 01调查增量构建耗时过长
  2. 02查找 Swift 编译缓慢的代码
  3. 03缩短 CI/CD 的干净构建时间
  4. 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+)

打开方式:

  1. 构建项目(Cmd+B)
  2. 打开报告导航器(Cmd+9)
  3. 选择最新的构建
  4. 显示辅助编辑器(Cmd+Option+Return)
  5. 构建时间线会显示在构建日志旁边

需要关注的内容:

关键路径(构建速度的上限)

关键路径是 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

最佳配置:

配置设置原因
Debugsinglefile(增量)仅重新编译已更改的文件
Releasewholemodule最大限度优化
// ❌ 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;

修复方法:

  1. Project → Build Settings
  2. 筛选:“Compilation Mode”
  3. 将 Debug 设为“Incremental”
  4. 将 Release 设为“Whole Module”

预期影响:增量 Debug 构建速度提升 40-60%。


模式 4:仅构建活动架构(影响很大)

症状:Debug 构建会针对多个架构(x86_64 + arm64)进行编译。

检查:

grep "ONLY_ACTIVE_ARCH" project.pbxproj

修复方法:

配置设置原因
DebugYES仅针对当前设备(arm64 或 x86_64)构建
ReleaseNO构建通用二进制文件

修复方法:

  1. Build Settings → “Build Active Architecture Only”
  2. 将 Debug 设为 YES
  3. 保持 Release 为 NO

预期影响:Debug 构建速度提升 40-50%(架构数量减半)。


模式 5:调试信息格式(影响中等)

症状:Debug 构建不必要地生成 dSYM。

最佳配置:

配置设置原因
Debugdwarf内嵌调试信息,速度更快
Releasedwarf-with-dsym单独生成 dSYM,用于崩溃报告
# Check current
grep "DEBUG_INFORMATION_FORMAT" project.pbxproj

修复方法:

  1. Build Settings → “Debug Information Format”
  2. 将 Debug 设为“DWARF”
  3. 将 Release 设为“DWARF with dSYM File”

预期影响:每次 Debug 构建节省 3-5 秒。


模式 6:目标并行化(WWDC 2018-408)

症状:Build Timeline 显示目标依次构建,而它们本可以并行构建。

检查方案配置:

  1. Product → Scheme → Edit Scheme
  2. Build 标签页
  3. 勾选“Parallelize Build”
  4. 确认目标顺序允许并行化

依赖关系图示例:

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

诊断变体:

  1. 使用 Timing Summary 进行构建:Product → Perform Action → Build with Timing Summary
  2. 筛选构建日志:在筛选框中输入“modules report”
  3. 查看显示变体数量的 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)。无需配置。


测量与验证

前后对比

必要步骤:

  1. 基准测量(更改前):

    xcodebuild clean build -scheme YourScheme 2>&1 | tee baseline.log
    
  2. 每次只应用一项优化

  3. 测量改进效果:

    xcodebuild clean build -scheme YourScheme 2>&1 | tee optimized.log
    
  4. 对比:

    # 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 秒

应用的优化:

  1. 调试编译模式:singlefile(节省 89 秒)
  2. 构建活动架构:YES(节省 45 秒)
  3. 有条件地上传 dSYM 脚本(每次增量构建节省 6.3 秒)

结果:

  • 清理构建:156 秒(快 36%)
  • 增量构建:5.7 秒(快 52%)

示例 2:多框架项目

基准:

  • 5 个框架串行构建
  • 总计:189 秒

应用的优化:

  1. 在 scheme 中启用并行构建
  2. 修复不必要的依赖项
  3. 发出模块优化(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。


故障排除

问题:优化后构建仍然很慢

检查:

  1. 测量前是否执行了清理?(xcodebuild clean)
  2. 测量的构建类型是否正确?(Debug 还是 Release)
  3. 机器是否因过热而降频?(活动监视器 → CPU 标签页)
  4. 是否有其他应用在使用 CPU?(测量期间退出 Xcode、Docker 和虚拟机)

问题:Build Timeline 未显示并行化

检查:

  1. 是否在 Scheme 中勾选了 Parallelize Build?
  2. 这些目标是否真正独立?(检查依赖关系图)
  3. 目标是否存在不必要的显式依赖关系?

问题:未显示类型检查警告

检查:

  1. 是否将标志添加到了正确的配置中?(Debug,而非 Release)
  2. 语法是否正确?-warn-long-function-bodies 100(带连字符)
  3. 是否构建了正确的 Scheme?
  4. 是否执行了全量构建以强制重新编译?

进阶:分析构建日志

提取编译耗时
# 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 中验证改进效果。

评论

支持 Markdown