Zig 主分支刚合入一组构建系统重构。最扎眼的数字是:zig build -h 在示例场景里,wall time 从约 150ms 降到约 14.3ms,CPU cycles 和 instructions 都下降约 95%。

但这个数字不能当广告词用。它测的是 --help,而且踩中了缓存序列化配置的路径。真正该看的不是“快了多少”,而是 Zig 把 build.zig 从一个能统管配置和执行的脚本,改成了更窄的角色。

改了什么:build.zig 退到配置阶段

旧机制很直接:build.zig 和构建系统实现一起编进同一个 Debug 进程。脚本生成构建图,build runner 接着执行。

新机制切成两段。build.zig 被编成小型 configurer,输出二进制配置文件。maker 读取这份配置,再执行构建图。maker 以 Release 模式编译,并按 Zig 版本进入全局缓存。

维度旧机制新机制
配置阶段build.zig + 构建系统一起编译build.zig 编成小型 configurer
构建图留在内存里序列化为二进制配置文件
执行阶段build runner 同进程执行maker 读取配置后执行
编译模式整体 Debugconfigurer Debug,maker Release
缓存边界更容易重复编译、重复执行maker 按 Zig 版本进入全局缓存
参数透传脚本可通过 b.args 观察观察能力移除,推荐 run_cmd.addPassthruArgs()

这次最直接受益的,是两类人。

一类是经常改 build.zig 的项目维护者。脚本变动后,重复编译和重复执行的范围有机会缩小。尤其是配合 --watch--fuzz--webui 的项目,开发循环会更敏感地吃到收益。

另一类是工具作者,尤其是 ZLS 这类第三方工具。序列化配置如果能被直接消费,工具就不必长期维护一套 fork build runner。这里仍要收住:目前只能说作者预期会减少这类负担,不能写成 ZLS 已经完成适配。

对普通项目作者,动作很明确:检查 build.zig 里有没有依赖 b.args 观察透传参数的写法。若有,就要改向 run_cmd.addPassthruArgs()。如果项目把 build.zig 当小型控制中心用,0.17 前后最好预留一次迁移窗口,而不是等 CI 先炸。

为什么重要:构建脚本不再统治构建过程

我更在意的是边界变化。

过去的 build.zig 很灵活。它既是配置,也是程序,还能在构建过程里观察更多东西。灵活的另一面,是缓存难做,工具难接,构建事实也难稳定下来。

新机制把事情拆开:build.zig 生成配置,maker 执行构建图。脚本退后半步,构建系统拿回执行权。

这不是单纯抠几十毫秒。它是在给 Zig 后面的工具链复杂度提前修路。IDE、语言服务器、持续构建、watch 模式,都需要一份可复用、可分析的构建事实。只靠脚本副作用,工具只能猜。

这条路在构建系统历史里并不新。Make、Ninja、Bazel 都绕不开同一个问题:构建如果只是任意脚本的副作用,系统就很难判断什么该重跑,什么能复用。所谓“名不正则言不顺”。构建事实没有固定形状,缓存就没有稳固落点。

Zig 这次做对的地方,是没有把性能优化停在表层。它先把权责切开:配置是配置,执行是执行。速度来自这个边界,而不是魔法。

代价也在这里。

b.args 被拿掉,说明构建脚本的自由度开始收紧。过去脚本可以窥探透传参数,现在不能了。好处是改这些透传参数时,不必重新从源码构建脚本;坏处是某些旧写法要改。

我不太买账“少一个观察能力就是倒退”。工具链成熟的过程,常常就是把随意性换成可缓存、可复现、可分析的结构。脚本越强,系统越弱;脚本愿意退位,生态才有空间长出更可靠的工具。

接下来观察什么:0.17 的迁移痛感

这次变更还在主分支。0.17.0 预计几周内发布,0.17.1 仍会继续修补。它不是稳定版尘埃落定,而是发布前的现实压力测试。

接下来要看三个点。

观察点为什么关键对项目作者的动作
b.args 迁移成本这是已知破坏点搜索旧写法,改用 run_cmd.addPassthruArgs()
--watch / --fuzz / --webui 体验最容易体现缓存边界收益在真实项目里测,不要只看 zig build -h
ZLS 等工具适配决定序列化配置能否变成生态收益等工具链说明,不要假设已经支持

这也是我对这次重构的判断:方向是对的,结账还没开始。

如果 0.17.0 把迁移痛感压住,0.17.1 又能及时补坑,用户会把它记成一次有效的基础设施治理。若迁移文档不清、边角 API 继续割手,这件事就会变成另一种记忆:构建系统又教育了用户一次。

回到开头那个 14.3ms。它有价值,但不是万能结论。它只说明在特定缓存路径下,拆分 configurer 和 maker 能把重复成本压得很低。

更大的信号是:Zig 开始把构建系统当基础设施管,而不是当脚本入口修。对一个还在快速演进的语言来说,这比一次漂亮 benchmark 更要紧。