Xcode 并行构建
原标题:Demystify parallelization in Xcode builds
官方翻译:深入探索 Xcode 构建中的并行
推荐看看原视频,25 分钟,会有更多理论背景讲解。
看下面精简的图文总结的话,大概 3 分钟吧。
有一些自己的理解和表述,如有错误,欢迎指正。
构建时间线
在 Build Log 右上角开启 Assistant ,就可以看到某次构建的时间线。可以很直观方便看到整个构建流程各个任务的耗时大小、串行阻塞、并行数量等信息。
Shift + 滚动,是横向滚动。Alt + 滚动,是放大缩小。点击任务会跟日志里的关联。如图使用 Assistant On Bottom 的布局会更好看一些。
并行处理代码和资源
我们知道,每个 target 的 Build Phases 是可以上下拖动顺序的,Xcode 会按照这个顺序来执行。
但实际上编译代码和复制资源文件是互不相关的,可以并行。(不确定是否 Xcode 14 之前就有)
并行执行脚本
脚本本来也是按顺序执行的。
现在 Xcode 14 支持了并行执行脚本,通过设置 FUSE_BUILD_SCRIPT_PHASES 为 YES 开启。
沙箱执行脚本
但如果脚本之间有依赖关系呢,一个任务的输入是另一个任务的输出,如果盲目并行就会有预期之外的问题。
Xcode 14 支持了在沙箱环境执行脚本,通过设置 ENABLE_USER_SCRIPT_SANDBOXING 为 YES 开启。(隔壁 Bazel 在偷笑)
使得声明 input 和 output 成为必要条件,否则报错。
Swift 优化
集成构建系统
在 Xcode 14 中,Apple 将构建系统与编译器的集成度发挥到极致,尤其体现在 Swift 中。(隔壁 Bazel 在哭泣)
Xcode 14 之前,构建系统与 Swift Driver (负责 Swift 的编译、Module、链接等)是分离的。每个 Swift target 都要经过 Swift Driver ,生成 Module 以及分析其他 Module 的依赖。
Xcode 14 中,构建系统与 Swift Driver 完全集成。Xcode 作为中央调度器来管理所有的 Swift 任务。可以减少一些不必要的开销,看上去效果不明显。但这个集成是后面更进一步的优化的前提。
更早产生 Module
(Eager,原意为急切的、渴望的,这里翻译为更早,比较好理解)
之前,一个 Swift target A 需要先编译完成,产生 Module 文件,另一个依赖 A 的 Swift target B 才能解析这个 Module 文件之后才开始编译。
现在,Xcode 14 和 Swift 5.7 ,Swift target A 无需完全编译所有 Swift 文件,就可以同时产生 Module 文件,以供 Swift target B 使用。
这有点像 C 系语言的头文件,头文件本来就是拿来就用的,不需要等待与之对应的源文件编译。
更早链接
我们知道,一般来说,编译可以并行,但链接过程无法并行。
但这次 Apple 厉害了,Swift 的链接过程也进行了并行化。
这是怎么实现的呢?
通过打破链接依赖,不依赖 target A 的链接产物,而是依赖 target A 的 Module 产物,来完成链接。
但熟悉编译链接的同学会知道,没这么简单。所以这是有条件的,需要纯 Swift 的 target ,而且是动态链接的。
相关更新日志
- Xcode provides a new assistant editor for build logs that focuses on parallelism to help identify build performance issues. This visualization displays events as a grid of colored blocks where the vertical axis represents level of parallelism and the horizontal axis represents time. (47858322)
- Xcode 14 can now compile targets in parallel with their Swift target dependencies. (57116972)
- Swift driver, the component that orchestrates Swift front-end invocations, is now integrated into Xcode’s build system, allowing for fine granular dependencies to other build system tasks and explicit scheduling. (72440175)
- Swift-only framework and dynamic library targets can opt into a new build system optimization using the
EAGER
_LINKING
build setting. When you enable this, Xcode emits additional artifacts during Swift compilation, which allows Xcode to unblock linking of downstream targets earlier, increasing parallelism in builds. (82396635)
- The build system runs tasks from different build phases in parallel when input and output dependencies don’t enforce their ordering. You can opt into this new behavior for run script build phases using the
FUSE_BUILD_SCRIPT_PHASES
build setting. (82396977)
- You can now enable sandboxing for shell script build phases using the
ENABLE
_USER
_SCRIPT
_SANDBOXING
build setting. Sandboxing blocks access to files inside the source root of the project as well as the Derived Data directory unless you list those files as inputs or outputs. When enabled, the build fails with a sandbox violation if a script phase attempts to read from or write to an undeclared dependency, preventing incorrect builds. (90506067)
Loading Comments...