Skip to content

【Zig 日报】构建系统重构 #333

@jiacai2050

Description

@jiacai2050

一个大型分支刚刚合并:将配置器(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 在中文群体中的使用,有多种方式可以参与进来:

  1. 供稿,分享自己使用 Zig 的心得
  2. 改进 ZigCC 组织下的开源项目
  3. 加入微信群Telegram 群组

Metadata

Metadata

Assignees

No one assigned

    Labels

    日报daily report

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions