一个大型分支刚刚合并:将配置器(configurer)进程与构建器(maker)进程分离开来。
本篇开发日志本质上是即将发布的版本说明的预告,旨在提前通知那些希望协助测试新功能并提供反馈的用户,这些反馈将指引 Zig 项目未来的发展方向。
此前,build.zig 文件与构建系统实现代码全部被编译进一个臃肿的 Debug 模式进程中。当 build.zig 逻辑在内存中构建完构建图(build graph)后,“构建运行器”(build runner)代码便会执行它。
现在,build.zig 文件在 Debug 模式下被编译成一个小型的进程(即“配置器”)。当该逻辑完成构建图的内存构建后,它会被序列化为一个二进制配置文件。父级 zig build 进程能够识别该文件并将其缓存以供后续使用。在等待上述过程的同时,系统会异步地以 Release 模式编译构建图执行进程(即“构建器”)。一旦配置文件就绪且构建器进程编译完成,系统便会执行构建器进程,并将配置文件传递给它。得益于全局缓存,构建器进程每个 Zig 版本仅需编译一次。随后,构建器进程会执行包含在序列化配置文件中的构建图。
这一改动的主要动机在于从以下三个方面提升 zig build 的速度:
只有用户的 build.zig 逻辑会在每次更改时被重新编译,而不是将整个构建系统也一同编译。随着我们引入 --watch、--fuzz 和 --webui,这一点变得愈发重要。构建系统可以在不增加 zig build 耗时的情况下增加更多功能。
现在,当构建系统确定没有任何更改时,可以完全跳过 build.zig 逻辑的重运行。例如,如果你在 zig build 命令行中添加了 -freference-trace,它现在会使用与上次相同的配置,避免冗余地重运行 build.zig 逻辑。
现在,实际执行构建图的进程是在开启优化的前提下编译的。
为了证明第 2 点和第 3 点,以下是运行 zig build --help 前后的对比:
Benchmark 1 (34 runs): master/zig build -h
measurement mean ± σ min … max outliers delta
wall_time 150ms ± 5.52ms 145ms … 165ms 4 (12%) 0%
peak_rss 84.8MB ± 275KB 84.2MB … 85.1MB 0 ( 0%) 0%
cpu_cycles 593M ± 4.01M 588M … 608M 2 ( 6%) 0%
instructions 995M ± 52.5K 995M … 995M 0 ( 0%) 0%
cache_references 25.8M ± 165K 25.4M … 26.1M 0 ( 0%) 0%
cache_misses 651K ± 20.1K 619K … 697K 0 ( 0%) 0%
branch_misses 918K ± 7.44K 906K … 935K 0 ( 0%) 0%
Benchmark 2 (348 runs): branch/zig build -h
measurement mean ± σ min … max outliers delta
wall_time 14.3ms ± 744us 13.2ms … 23.3ms 8 ( 2%) ⚡- 90.4% ± 0.4%
peak_rss 78.5MB ± 562KB 77.1MB … 81.4MB 7 ( 2%) ⚡- 7.4% ± 0.2%
cpu_cycles 24.1M ± 821K 22.8M … 27.1M 3 ( 1%) ⚡- 95.9% ± 0.1%
instructions 43.7M ± 23.8K 43.7M … 43.8M 56 (16%) ⚡- 95.6% ± 0.0%
cache_references 1.46M ± 14.6K 1.40M … 1.50M 19 ( 5%) ⚡- 94.3% ± 0.1%
cache_misses 142K ± 4.87K 127K … 157K 2 ( 1%) ⚡- 78.1% ± 0.4%
branch_misses 126K ± 1.37K 120K … 129K 12 ( 3%) ⚡- 86.3% ± 0.1%
之所以效果如此显著,是因为以前每次执行 zig build 命令时,build.zig 逻辑都会被运行;而现在,构建系统直接使用了已缓存的序列化配置。
除了性能提升外,我预计 ZLS 等第三方工具将受益于直接读取序列化配置文件,而无需再维护构建运行器的分支版本。
此次变更大幅重构了 Zig 构建系统的内部机制。不过,从 API 的角度来看,除了上述 PR 中提到的例外情况,它基本保持了兼容。
对于大多数用户而言,这可能是他们会遇到的主要破坏性变更:
if (b.args) |args| {
run_cmd.addArgs(args);
}
⬇️
run_cmd.addPassthruArgs();
这移除了构建脚本的一项能力,因为它们无法再观察到这些参数了。作为交换,这意味着在更改这些参数时,构建脚本不再需要从源码重新编译。
如果你希望影响 Zig 的发展方向,现在正是将你的项目升级到开发版本并尝试这些更改的好时机。我们将在几周内发布 0.17.0 版本。不过,如果你没有时间,且发现 0.17.0 破坏了你的构建,请不必担心,后续在 0.17.1 版本中仍有充足的机会进行修复。
Devlog ⚡ Zig Programming Language
加入我们
Zig 中文社区是一个开放的组织,我们致力于推广 Zig 在中文群体中的使用,有多种方式可以参与进来:
- 供稿,分享自己使用 Zig 的心得
- 改进 ZigCC 组织下的开源项目
- 加入微信群、Telegram 群组
一个大型分支刚刚合并:将配置器(configurer)进程与构建器(maker)进程分离开来。
本篇开发日志本质上是即将发布的版本说明的预告,旨在提前通知那些希望协助测试新功能并提供反馈的用户,这些反馈将指引 Zig 项目未来的发展方向。
此前,build.zig 文件与构建系统实现代码全部被编译进一个臃肿的 Debug 模式进程中。当 build.zig 逻辑在内存中构建完构建图(build graph)后,“构建运行器”(build runner)代码便会执行它。
现在,build.zig 文件在 Debug 模式下被编译成一个小型的进程(即“配置器”)。当该逻辑完成构建图的内存构建后,它会被序列化为一个二进制配置文件。父级 zig build 进程能够识别该文件并将其缓存以供后续使用。在等待上述过程的同时,系统会异步地以 Release 模式编译构建图执行进程(即“构建器”)。一旦配置文件就绪且构建器进程编译完成,系统便会执行构建器进程,并将配置文件传递给它。得益于全局缓存,构建器进程每个 Zig 版本仅需编译一次。随后,构建器进程会执行包含在序列化配置文件中的构建图。
这一改动的主要动机在于从以下三个方面提升 zig build 的速度:
只有用户的 build.zig 逻辑会在每次更改时被重新编译,而不是将整个构建系统也一同编译。随着我们引入 --watch、--fuzz 和 --webui,这一点变得愈发重要。构建系统可以在不增加 zig build 耗时的情况下增加更多功能。
现在,当构建系统确定没有任何更改时,可以完全跳过 build.zig 逻辑的重运行。例如,如果你在 zig build 命令行中添加了 -freference-trace,它现在会使用与上次相同的配置,避免冗余地重运行 build.zig 逻辑。
现在,实际执行构建图的进程是在开启优化的前提下编译的。
为了证明第 2 点和第 3 点,以下是运行 zig build --help 前后的对比:
之所以效果如此显著,是因为以前每次执行 zig build 命令时,build.zig 逻辑都会被运行;而现在,构建系统直接使用了已缓存的序列化配置。
除了性能提升外,我预计 ZLS 等第三方工具将受益于直接读取序列化配置文件,而无需再维护构建运行器的分支版本。
此次变更大幅重构了 Zig 构建系统的内部机制。不过,从 API 的角度来看,除了上述 PR 中提到的例外情况,它基本保持了兼容。
对于大多数用户而言,这可能是他们会遇到的主要破坏性变更:
if (b.args) |args| {
run_cmd.addArgs(args);
}
⬇️
run_cmd.addPassthruArgs();
这移除了构建脚本的一项能力,因为它们无法再观察到这些参数了。作为交换,这意味着在更改这些参数时,构建脚本不再需要从源码重新编译。
如果你希望影响 Zig 的发展方向,现在正是将你的项目升级到开发版本并尝试这些更改的好时机。我们将在几周内发布 0.17.0 版本。不过,如果你没有时间,且发现 0.17.0 破坏了你的构建,请不必担心,后续在 0.17.1 版本中仍有充足的机会进行修复。
加入我们
Zig 中文社区是一个开放的组织,我们致力于推广 Zig 在中文群体中的使用,有多种方式可以参与进来: