移动/游戏
当有已批准的 Xcode 构建优化方案时…
在 Xcode/Swift/SPM 项目中对已批准的构建优化项进行实施、编译验证、重新基准测试和回归判断。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
当有已批准的 Xcode 构建优化方案时特定
解决思路
- 仅应用已获得开发者明确批准的变更。
- 每次只应用一个逻辑修复,使变更可审查、可回滚。
- 应用变更后重新运行基准测试以验证是否改善。
- 准确报告改动了什么、涉及哪些文件以及测得的差异。
- 如果变更没有带来改善或导致回归,立即标记。
- 输入可以是位于 `.build-benchmark/optimization-plan.md` 且已勾选批准框的优化计划,也可以是开发者明确描述要应用的修复指令(例如“为 Debug 设置 `DEBUG_INFORMATION_FORMAT` 为 `dwarf`”)。
- 当基于优化计划工作时,阅读批准清单,只实现已勾选项。
- 修改 `project.pbxproj` 中的值,使其符合 `references/build-settings-best-practices.md` 中的建议。
- 为 Debug 设置 `DEBUG_INFORMATION_FORMAT = dwarf`。
- 为 Debug 设置 `SWIFT_COMPILATION_MODE = singlefile`。
- 启用 `COMPILATION_CACHE_ENABLE_CACHING = YES`。
- 为 Debug 启用 `EAGER_LINKING = YES`。
- 对齐跨 target 的设置以消除 module variants。
- 编辑 `project.pbxproj` 时,通过匹配 target 名称和 configuration 名称来定位正确的 `buildSettings` 块。
- 应用后用 `xcodebuild -showBuildSettings` 验证变更。
- 修复在增量构建或 Debug 构建中浪费时间的 run script phases。
- 添加 input 和 output 文件声明,以便 Xcode 跳过未变化的脚本。
- 为仅 Release 使用的脚本添加配置守卫:`[[ "$CONFIGURATION" != "Release" ]] && exit 0`。
- 当 input/output 列表很长时,将其移入 `.xcfilelist` 文件。
- 当已声明 inputs 和 outputs 时,启用 `Based on dependency analysis`。
- 应用可减少类型检查器和编译器开销的代码改动;可参考 `references/fix-patterns.md` 中的前后对比模式。
- 为复杂表达式添加显式类型标注。
- 将过长的链式或嵌套表达式拆分为有类型的中间变量。
- 当类没有被子类化时,将其标记为 `final`。
- 对仅内部使用的符号收紧访问控制(`private`/`fileprivate`)。
- 将庞大的 SwiftUI `body` 属性提取为更小的组合子视图。
- 用独立的类型化 helper 替换深度嵌套的 result-builder 代码。
- 为传给泛型函数的闭包添加显式返回类型。
- 将共享类型移到更低层模块,以消除循环依赖或向上依赖。
- 按功能领域拆分超大模块(200+ 文件)。
- 将 protocol 定义提取到轻量级接口模块。
- 移除不必要的 `@_exported import` 使用。
- 对齐导入相同 package 的各 target 的构建选项,防止 module variant 重复。
- 将跟踪分支的依赖固定到 tag 版本或 commit hash,以实现确定性解析。
- 应用版本固定变更前,运行 `git ls-remote --tags <url>` 确认 tag 存在。
- 如果上游没有 tag,则固定到特定 revision hash。
- 继续前,用 `xcodebuild -resolvePackageDependencies` 验证固定版本能成功解析。
- 阅读已批准的优化计划或开发者指令。
- 对每个已批准项,确定要修改的确切文件和位置。
- 应用变更。
- 验证变更可编译:运行一次快速 `xcodebuild build` 确认未引入错误。
- 所有已批准变更应用后,使用与原始 baseline 相同的输入重新基准测试:`python3 scripts/benchmark_builds.py --project App.xcodeproj --scheme MyApp --configuration Debug --destination "platform=iOS Simulator,name=iPhone 16" --output-dir .build-benchmark`。
- 将变更后的 median 与 baseline 比较并报告差异。
- 不要把所有变慢的数字都当作真正回归;建议回滚前评估完整情况。
- 对于 `COMPILATION_CACHE_ENABLE_CACHING = YES` 这类变更,标准 clean build 可能因缓存填充开销略慢,但 cached clean build 会显著更快;由于 cached clean build 反映真实开发者工作流(切换分支、拉取变更、带热缓存的 Clean Build Folder),标准 clean 略慢加 cached clean 更快是净改善,不是回归。首次成本更高但后续构建受益的变更同理。
- 一起评估 standard clean、cached clean(如有)和 incremental 构建。只有当对开发者日常工作流重要的指标变差时,才是明确回归;如果只有很少执行的真正 cold build 变慢,可能可以接受。
- 单个变更自身回归,若它能带来更大的累计改善,仍可能值得保留(例如启用编译缓存使某类构建变慢,但为其他所有构建解锁缓存收益)。
- 某些 build settings 是 Apple 推荐的现代默认值;无论即时基准是否显示可测改善,都应应用并保留。它们不是投机优化,而是与 Apple 工具链方向一致,并会在未来 Xcode 版本中复利增值。
- 一旦应用,以下最佳实践设置应始终保留:`COMPILATION_CACHE_ENABLE_CACHING = YES`(Apple 正在积极投入,缓存随 Xcode 版本改进并在真实工作流中复利)。
- 一旦应用,保留 `EAGER_LINKING = YES`(Debug),它允许链接器与编译重叠。
- 一旦应用,保留 `SWIFT_USE_INTEGRATED_DRIVER = YES`,它消除进程间调度开销。
- 一旦应用,保留 `DEBUG_INFORMATION_FORMAT = dwarf`(Debug),避免不必要的 dSYM 生成。
- 一旦应用,保留 `SWIFT_COMPILATION_MODE = singlefile`(Debug),实现增量重新编译。
- 一旦应用,保留 `ONLY_ACTIVE_ARCH = YES`(Debug),本地构建没有理由构建所有架构。
- 报告这些设置时使用类似措辞:“已应用推荐构建设置。未测得即时基准改善,但这与 Apple 推荐配置一致,并为未来 Xcode 改进做好准备。”
- 仅对非最佳实践的投机性变更(例如源码重构、链接实验、脚本阶段修改、依赖重构)考虑建议回滚。
- 如果累计 pass 显示所有测量构建类型(standard clean、cached clean、incremental)的 wall-clock 都回归,建议回滚所有投机性变更,除非开发者明确要求出于非性能原因保留特定项。
- 对每个单独的投机性变更:如果 median 没有改善,也没有 cached/incremental 收益,标记 `Recommend revert` 并附上测得的差异。
- 区分“仅减少离群值”(改善最差情况但未改善 median)和“median 改善”(改善典型开发者等待时间)。
- 当变更在一种构建类型和另一种之间权衡时(例如标准 clean 更慢但 cached clean 更快),清楚呈现两个数字并让开发者决定。表述为:“标准 clean 构建慢了 X.Xs,但 cached clean 构建(真实日常工作流)快了 Y.Ys。”
- 报告时先用平实语言给出 wall-clock 结果,例如:“你的 clean build 现在耗时 X.Xs(之前 Y.Ys)——快了 Z.Zs。”和“你的 incremental build 现在耗时 X.Xs(之前 Y.Ys)——快了 Z.Zs。”
- 随后包含:变更后 clean build wall-clock median。
- 变更后 incremental build wall-clock median。
- 两者的绝对和百分比 wall-clock 差异。
- 如果基准噪声高,给出置信度说明。
- 每个修复修改的文件列表。
- 与原始建议的任何偏差。
- 如果累计任务指标改善但 wall-clock 没有改善,明确说明:“编译器工作量减少了,但构建等待时间没有改善。当 Xcode 将这些任务与其他同样长的任务并行执行时,这是预期现象。”
- 如果修复未产生可测量的 wall-time 改善,注明 `No measurable wall-time improvement`,并建议保留(例如为了代码质量)还是回滚。
- 对于因非基准原因有价值的变更(确定性 package 解析、分支切换缓存),标注:“此变更预计不会改善等待时间。收益是 [确定性构建 / 更快切换分支 / 降低 CI 成本]。”
- 注意:在已测试项目(87 到 1,991 个 Swift 文件)中,`COMPILATION_CACHE_ENABLE_CACHING` 测得 clean build 快 5-14%。在缓存跨构建持久存在的真实开发者工作流中收益会复利:切换分支、拉取变更、以及带持久 DerivedData 的 CI。benchmark 脚本会自动检测该设置并运行 cached clean 阶段进行验证。
- 优化 pass 完成后,生成结构化执行报告,清楚总结尝试了什么、什么有效、最终状态是什么。
- 报告结构包含:`## Execution Report`、`### Baseline`(clean build median、cached clean build median(如适用)、incremental build median)。
- 报告结构包含 `### Changes Applied` 表格,列为:`#`、`Change`、`Actionability`、`Measured Result`、`Status`。
- 报告结构包含 `### Final Cumulative Result`:clean build median、cached clean build median、incremental build median、`**Net result:** Faster / Slower / Unchanged`。
- 报告结构包含 `### Blocked or Non-Actionable Findings`:`Finding: reason it could not be addressed from the repo`。
- 状态值含义:`Kept`——变更改善或维持了构建时间,已保留。
- 状态值含义:`Kept (best practice)`——变更是推荐构建设置;无论即时基准结果如何都保留。
- 状态值含义:`Reverted`——变更导致构建时间回归,已回滚。
- 状态值含义:`Blocked`——由于项目结构、Xcode 行为或外部约束,变更无法应用。
- 状态值含义:`No improvement`——变更可编译但没有可测量 wall-time 收益;注明是出于非性能原因保留还是回滚。
- 如果实现过程中发现问题超出本技能范围:项目级分析缺口移交给 `xcode-project-analyzer`。
- 如果实现过程中发现问题超出本技能范围:编译热点分析移交给 `xcode-compilation-analyzer`。
- 如果实现过程中发现问题超出本技能范围:package graph 问题移交给 `spm-build-analysis`。
- 查阅 `references/fix-patterns.md` 获取具体的前后修复模式。
- 查阅 `references/build-settings-best-practices.md` 获取 build settings 最佳实践。
- 查阅 `references/recommendation-format.md` 获取推荐格式。
适用边界 · 注意事项
- 没有已批准的优化计划或明确开发者指令时,不应应用构建优化变更。
- 无法运行 Xcode/xcodebuild 或没有可比较的原始 baseline 时,不适合用本技能验证改善。
- 不适用于非 Xcode/Swift/SPM 项目;也不要在未确认上游 tag 或版本可解析前固定分支依赖。