Xcode 26 Compilation Caching 值得开吗?2026 CI 判断
📋 本文目录
症状:Clean Build 或频繁切换分支后,远程 Mac CI 仍然反复编译相同源文件,但流水线总时长没有稳定下降。
最快解法:先用诊断信息确认 Compilation Caching 是否命中,再用同一提交、同一 Scheme 和同一节点做开启/关闭对照;只有能够长期保留缓存的 Runner,才适合灰度放量。
适用负载与初步结论
Xcode 26 的 Compilation Caching 是可选择启用的编译能力。Apple 官方资料确认,它可以缓存特定源文件输入产生的编译结果,在相同输入再次参与编译时尝试复用;频繁切换分支和执行 Clean Build 的工作流,更值得进入验证范围。Xcode 26 Release Notes
但“启用缓存”不等于“所有项目都会变快”。真正影响结果的是输入是否重复、构建参数是否稳定、Runner 是否保留工作目录,以及编译是否确实占据流水线的大部分时间。
你可以先按三种结论分流:
- ✅ 进入灰度测试:频繁切换分支,Clean Build 较多,同一节点能够保留工作目录和缓存目录,Scheme、SDK 与构建参数也相对稳定。
- ⚠️ 暂缓全量启用:Runner 每次任务后销毁,缓存无法跨任务保留,或者签名、测试、依赖下载和脚本已经占据主要时间。
- ❌ 暂不启用:磁盘空间紧张、共享节点隔离不足,或者每个任务都会动态修改大量输入和构建参数。
这篇文章适合三类人:频繁切换分支或执行 Clean Build 的 Apple 平台开发者;维护自托管远程 Mac Runner 的 DevOps 工程师;以及需要控制共享节点稳定性、发布复现和容量规划的研发平台负责人。
截至 2026 年 9 月 3 日,Apple 系统要求页面列出 Xcode 26.6 为稳定版本,并列出 Xcode 27 beta 4 作为测试版本;Xcode 26.6 要求 macOS Tahoe 26.2 或更高版本。Xcode 系统要求 本文只讨论 Xcode 26 的已确认行为,不把测试版行为套用到稳定版。
命中证据与缓存边界
四种机制分别取证
在 CI 里,下面四种机制经常被统称为“缓存”,但它们解决的问题不同:
- Compilation Caching:复用编译器针对特定源文件输入产生的编译结果。
- DerivedData:保存中间构建产物、索引和其他 Xcode 工作数据。
- 依赖下载缓存:复用包管理器或依赖系统已经下载的源码、二进制包。
- 增量构建:根据源文件和依赖关系,只重新处理发生变化的目标。
Apple 的 Build Settings Reference 列出了 COMPILATION_CACHE_ENABLE_CACHING,并提供 COMPILATION_CACHE_ENABLE_DIAGNOSTIC_REMARKS,用于输出与缓存编译任务有关的诊断信息。Build Settings Reference
因此,DerivedData 没有被删除,不代表 Compilation Caching 已经命中;依赖下载变快,也不能算编译缓存产生了收益。你需要把诊断日志、编译任务和流水线阶段分别核对。
三类重复构建样本
建议固定一个项目提交,连续记录三类任务:
- 首次构建:建立完整基线,确认编译、链接、签名、测试和脚本分别占用多少时间。
- 相同输入复跑:不改变提交、Scheme、SDK、构建参数和节点,观察是否出现缓存诊断,以及同类源文件是否减少实际编译。
- 切回历史分支:先构建分支 A,再切到分支 B,最后切回 A,观察原本相同的输入能否复用。
不要只看流水线顶部的总耗时。你需要在日志中确认 Compilation Caching 的诊断信息,再结合 xcodebuild 的构建计时摘要判断编译阶段发生了什么。Apple 官方文档支持使用 -showBuildTimingSummary 输出构建时间信息。Xcode 增量构建计时文档
一个有效命中至少应满足以下条件:
- 相同输入的重复编译任务出现缓存相关诊断;
- 编译阶段的任务数量或实际耗时出现可重复变化;
- 变化在多次任务中方向一致,而不是只发生一次;
- 无缓存构建仍然能够独立完成;
- 缓存开启不会改变最终归档、测试或签名结果。
如果只能看到 DerivedData 目录继续增长,或者流水线总时长偶尔变短,但没有编译阶段证据,就不能判定为命中。
编译收益与全链路收益
两条时间轴分开统计
Compilation Caching 直接影响的是编译相关任务,而不是整条 CI 流水线。即使编译阶段变快,以下环节仍可能让总时长变化不明显:
- Swift 或 Objective-C 编译;
- 模块准备与依赖扫描;
- 链接和二进制处理;
- 代码签名与导出;
- 单元测试、UI 测试和模拟器启动;
- 依赖解析、下载与脚本任务;
- 上传制品或发布归档。
你应把收益拆成两个指标:
编译收益:只比较编译阶段的耗时、任务数量和缓存诊断。
流水线收益:比较从任务开始到成功结束的完整耗时,同时记录签名、测试、脚本和依赖下载的占比。
不要使用没有来源的固定提升比例做决策。Apple 确认了适用工作负载和相关设置,但没有承诺所有项目都会获得统一的性能收益。任何耗时、提升百分比和节点性能结论,都必须来自你的实测或可靠测试资料。
方案对照表
| 判断维度 | 更适合开启 | 更适合暂缓或关闭 |
|---|---|---|
| 源文件输入 | 多次构建反复处理相同源文件 | 每次任务都生成或修改大量输入 |
| 分支行为 | 经常切换分支并回到旧分支 | 分支切换少,任务大多是一次性构建 |
| Clean Build | Clean Build 后会重复构建相同目标 | 主要依赖普通增量构建 |
| Runner 生命周期 | 工作目录和缓存目录可持久化 | 每次任务结束后销毁整个环境 |
| 构建参数 | Scheme、SDK、架构和编译参数稳定 | 参数按任务动态变化 |
| 磁盘条件 | 有容量监控和清理策略 | 共享磁盘空间紧张,无法隔离账户 |
| 发布任务 | 可保留无缓存发布基线 | 所有发布任务都依赖同一缓存状态 |
表格只能帮助你筛选测试对象,不能替代命中验证。只要缓存无法在后续任务中保留,Compilation Caching 的理论收益就可能在 Runner 重建后消失;只要构建参数变化导致输入集合不同,缓存目录存在也不意味着会命中。
存储生命周期与节点边界
持久化节点、临时 Runner 与共享节点
持久化远程 Mac 可以保留工作目录、DerivedData 和相关缓存,更适合多分支开发、重复构建和长期运行的自托管任务。但你必须监控磁盘增长、账户隔离、缓存归属和清理后的恢复能力。
临时 Runner 每次任务都从干净环境开始,初始状态更容易统一,也适合强调复现性的任务。不过,缓存通常难以跨任务保留。启用前必须确认所谓“缓存复用”是否真的跨越了任务生命周期。
多人共享节点 还要额外考虑缓存污染。不同项目、账户或工具链共用一个节点时,缓存目录权限、路径、分支和清理策略可能互相影响。不要把“目录存在”当作“缓存安全可复用”的证明。
你可以结合 远程 Mac 构建节点的存储与重启验收 检查工作目录、重启策略和磁盘交付方式。若节点重启后会自动重置工作区,就应把它按临时 Runner 处理,而不是按持久化缓存节点估算收益。
清理与停止条件
清理不能简单写成“定期删除缓存”。你至少要记录:
- 缓存目录的增长趋势;
- 最近一次命中时间;
- 目录对应的项目、分支和工具链;
- 清理后第一次构建的耗时;
- 清理后第二次相同输入构建的耗时;
- 清理是否影响归档、测试和发布任务。
出现以下任一情况,应暂停扩大缓存范围:
- 磁盘压力开始影响编译、模拟器或归档;
- 缓存诊断与构建结果不一致;
- 无缓存构建失败,说明项目可能依赖隐式产物;
- 多个账户之间出现权限或路径污染;
- 重启后缓存保留状态与预期不符;
- 发布任务只能在“有缓存”状态下成功。
Build Settings 会影响源码编译、链接、调试信息生成以及最终打包,因此缓存验收不能只覆盖编译命令,还要覆盖归档和发布流程。
发布复现与灰度范围
三条发布基线
Compilation Caching 进入共享 CI 后,建议保留三条基线:
- 缓存开启基线:观察常规分支构建的命中和耗时。
- 缓存关闭基线:判断真实加速来源,也作为异常时的快速复测路径。
- 全新克隆基线:验证项目没有依赖某个节点上残留的隐式产物。
发布归档不应只依赖开发分支的加速结果。你需要在关闭缓存的情况下完成一次全新克隆、依赖恢复、编译、测试和归档;再在开启缓存的情况下重复相同流程,比较产物一致性和失败记录。
Apple 的性能测试文档强调了固定测试输入、记录结果和识别回归的重要性。这个思路同样适合 CI 缓存验收:固定提交和构建参数,保存日志与计时结果,并为异常变化设置停止条件。Apple 性能测试文档
如果项目使用显式模块依赖,还应关注不同模块的构建选项是否一致。模块选项差异可能导致看似相同的输入无法复用相同结果;这时应先检查构建设置和模块依赖关系,再判断缓存是否失效。
灰度放量清单
- [ ] 固定项目提交、Scheme、SDK、架构和构建参数。
- [ ] 记录节点型号、macOS 版本、Xcode 版本和 Runner 工作目录策略。
- [ ] 为 Compilation Caching 启用诊断信息,并保存完整构建日志。
- [ ] 使用
xcodebuild -showBuildTimingSummary输出构建阶段计时。 - [ ] 分别执行首次构建、相同输入复跑和历史分支切回。
- [ ] 单独记录编译、链接、签名、测试、脚本和依赖下载耗时。
- [ ] 检查缓存目录、DerivedData 和依赖下载缓存的大小变化。
- [ ] 重启节点后重复相同输入构建,确认缓存是否仍在。
- [ ] 完成一次无缓存全新克隆构建。
- [ ] 完成一次无缓存发布归档,并保存结果作为回滚基线。
- [ ] 为共享节点设置关闭入口和无缓存复测路径。
- [ ] 只有在命中证据、构建结果和磁盘状态均稳定后扩大灰度范围。
决策评分与回退路径
你可以用 5 个维度做内部评分,每项从 0 到 2 分:
- 输入重复度:相同源文件是否经常被再次编译;
- 节点持久性:缓存目录能否跨任务、跨重启保留;
- 参数稳定性:Scheme、SDK 和编译选项是否稳定;
- 证据完整度:是否有诊断信息和 Build Timing Summary;
- 运维可控性:是否能监控容量、清理缓存和执行无缓存回测。
建议按结果处理:
- 8~10 分:可在持久化远程 Mac CI 节点灰度启用。
- 5~7 分:只对开发分支或非发布任务启用,继续收集数据。
- 0~4 分:暂缓启用,先解决 Runner 生命周期、参数漂移或磁盘管理问题。
这个评分是上线门槛,不是性能承诺。最终判断仍应回到单位成功构建的时间、失败重试次数、磁盘维护成本和发布复现能力,而不是缓存目录是否存在。
如果你正在规划节点容量,可以参考 Mac mini M4 价格与服务器方案指南,把持久化磁盘、重启机制和多任务隔离纳入总成本,而不是只比较处理器参数。
常见问题
Clean Build 的缓存价值
Clean Build 并不自动保证缓存命中,但它属于值得测试的工作流。你要确认 Clean Build 后重新处理的源文件输入是否与之前一致,并用诊断日志和构建计时证明结果被复用。若 Clean Build 后仍需执行大量链接、签名和测试,总流水线时间可能不会明显变化。
xcodebuild 的命中确认
在 CI 中,xcodebuild 负责执行任务,-showBuildTimingSummary 负责输出阶段计时;两者结合才能定位编译阶段是否变化。诊断设置用于确认缓存参与了哪些任务,Build Timing Summary 用于确认这些任务是否真的影响了编译耗时。缺少其中一项时,不应仅凭总时长判断。
Runner 重启后的状态
Runner 重启后能否保留 Compilation Caching,取决于缓存所在磁盘、节点重置脚本和账户目录策略。持久化节点可能保留缓存,但仍需实测;临时节点如果重启后重新初始化系统或工作目录,则不能把缓存收益计入长期模型。
磁盘清理边界
清理前先记录目录、归属项目和最近命中情况,再按项目或工具链进行定向清理。清理完成后必须做无缓存构建,验证项目没有依赖残留产物。共享节点还要检查权限、并发任务和其他项目是否受到影响。
多分支项目的默认策略
多分支项目不建议未经测试就全量开启。若分支之间反复切换、输入高度重复,并且节点可以持久化缓存,可以先让开发分支进入灰度;发布归档保留独立的无缓存基线,确认复现和回滚都没有问题后再扩大范围。
对比你当前的 CI 方案与 MacDate 的远程 Mac 方案时,真正需要关注的不是“缓存开关”本身,而是节点能否长期保留工作目录、重启后状态是否可验收,以及磁盘清理和账户隔离是否可控。临时 Runner 的缓存经常随环境销毁,Linux 云主机又无法直接替代需要 Xcode 和 macOS 工具链的构建任务;如果你自行维护 Mac mini,还要承担硬件在线、远程接入和故障恢复成本。
如果你只是需要临时扩展构建容量、验证 Compilation Caching,或先搭建一台可持久化的远程 Mac CI 节点,租赁 MacDate 的真实 Mac 主机会比立刻购买硬件更容易做小范围验证。反过来,长期稳定的重负载、必须接入物理设备或需要完全掌控硬件的场景,仍应优先评估自购 Mac。
最后更新于 2026 年 9 月 3 日,设置名称、Xcode 版本和适用范围核对自本文引用的 Apple 官方 Release Notes、系统要求、Build Settings Reference、构建计时和性能测试文档。Apple 调整缓存默认行为、发布新的 Xcode 稳定版或节点交付方式变化时,应重新执行同项目对照测试。