Skip to content

0.12.0 ​

2024/4/20,0.12.0 终于发布了,历时 8 个月,有 268 位贡献者,一共进行了 3688 次提交!

过去的发行说明都非常长,因为试图记录开发周期中的所有变化。本次更新则进行了适当删减,以便于读者和维护者阅读。

INFO

注意:存在一部分变动并未在本次的发行说明中提及,其中包括 API 的 Break Change。

WARNING

有关于各平台和架构的支持情况,本次更新并未进行更改,故在此处不再提及!

API 变更 ​

可以参考:0.12.0 升级指南

Autodoc 重构 ​

目前我们已经可以使用全新的 Autodoc,新的实现带来了更棒的文档阅读体验!

旧的实现 ​

文件结构为:

sh
   5987 src/Autodoc.zig
   435 src/autodoc/render_source.zig
 10270 lib/docs/commonmark.js
  1245 lib/docs/index.html
  5242 lib/docs/main.js
  2146 lib/docs/ziglexer.js
 25325 total

编译产物结构(演示使用的标准库文档)为:

sh
272K commonmark.js
3.8M data-astNodes.js
360K data-calls.js
767K data-comptimeExprs.js
2.2M data-decls.js
896K data-exprs.js
 13K data-files.js
  45 data-guideSections.js
 129 data-modules.js
  15 data-rootMod.js
 294 data-typeKinds.js
3.2M data-types.js
 38K index.html
158K main.js
 36M src/ (470 .zig.html files)
 78K ziglexer.js

总输出大小为 47M,经过 gzip 处理后为 5.7M!

  • src/Autodoc.zig 用于处理 ZIR 代码(zig 编译时产生的中间代码),输出 json 以便 js 使用,这就导致很多代码无法通过某些数据重建 AST 语法树

  • lib/docs/commonmark.js 是一个第三方的 markdown 实现,但它的特性有点太多了,例如,我们并不希望在文档注释中使用 HTML 标签,因为这样会让源代码的注释无比丑陋,应当只渲染源代码和 markdown。

  • lib/docs/ziglexer.js 是 js 中针对 zig 的语义标记实现。事实上,zig 已经在标准库中公开了由 zig 实现的语义标记。有趣的是,andrewrk 看到这个东西被添加进来的时候应该两眼一黑,具体可以见 Issue 16306 Issue 16490 。

  • src/autodoc/render_source.zig 是一种将 .zig 文件转换为带有语法高亮但不是交互式的 .zig.html 文件。

新的实现 ​

文件结构为:

sh
   942 lib/docs/main.js
   403 lib/docs/index.html
   933 lib/docs/wasm/markdown.zig
   226 lib/docs/wasm/Decl.zig
  1500 lib/docs/wasm/markdown/Parser.zig
   254 lib/docs/wasm/markdown/renderer.zig
   192 lib/docs/wasm/markdown/Document.zig
   941 lib/docs/wasm/main.zig
  1038 lib/docs/wasm/Walk.zig
  6630 total

编译产物结构(演示使用的标准库文档)为:

sh
 12K index.html
 32K main.js
192K main.wasm
 12M sources.tar

总输出大小为:12M,经过 gzip 处理后为 2.3M!

可以看到,新的实现的代码和产物都很简洁,产物刚刚好只有 4 个文件,并且文件大小直接缩减到了 1/4 。

新的实现不仅仅是表面看上去更简单,实际上它较之旧实现更加的强大,因为新的实现并不会处理 ZIR,而是直接读取解析源代码文件(sources.tar 就是打包的源代码),这意味着它拥有完整的源代码信息,不需要进行一些乱七八糟的推断。

此方案是使用 zig 编写的 WebAssembly 模块,这允许其重用编译器中的组件,例如分词器,解析器和其他用于对 zig 代码进行操作的一些程序。

通过 HTTP 请求sources.tar 文件并解压后,会将其直接送入 Wasm 模块的内存中,针对 tar 文件使用 std.tar 进行解析,源代码就地进行解析,并额外进行一些计算添加 hash 表。

针对目前的实现,优化空间可以使用多线程来加速解析,但目前来看单线程已经足够快了,似乎没有必要。

携带标准库文档 ​

在 zig 的 0.11.0 中,zig 发行时会带有一个 docs/std/ 目录,其中包含着旧 autodoc 实现构建产物(足足 47M 大小)。

在本次重写中,直接删除这些构建产物,作为代替,提供了 zig std 命令,这允许使用自带的 autodoc 生成器来打开一个浏览器窗口查看文档说明,首次使用此命令时,会先执行一次针对 lib/compiler/std-docs.zig 的编译操作。

HTTP 服务器会动态创建请求的文件,包括 main.wasm 重建(如果任何源文件发生更改)和构造 sources.tar,这意味着在查看文档时,对文档文件或 autodoc 系统本身的任何源更改都会立即反映出来。在 URL 前面加上 /debug URL 会使用 WebAssembly 模块的调试版本。

这意味着贡献者可以通过在浏览器窗口中按刷新来测试对 Zig 标准库文档以及 autodocs 功能的更改,只需要 Zig 的二进制发行版。

总之,这使得 zig 的安装大小从 317M 减少到了 268M(-15%)。

编译器的 ReleaseSmall 版本从 10M 缩小到 9.8M(-1%)。

构建文档时间 ​

Autodocs 生成现在作为编译器 pipeline 的一部分完成,而不是在最后添加。它也不再依赖于 pipeline 的其他部分。

以下是构建标准库文档所需要的时间:

sh
Benchmark 1 (3 runs): old/zig test /home/andy/dev/zig/lib/std/std.zig -fno-emit-bin -femit-docs=docs
  measurement          mean ± σ            min … max           outliers         delta
  wall_time          13.3s  ±  405ms    12.8s  … 13.6s           0 ( 0%)        0%
  peak_rss           1.08GB ±  463KB    1.08GB … 1.08GB          0 ( 0%)        0%
  cpu_cycles         54.8G  ±  878M     54.3G  … 55.8G           0 ( 0%)        0%
  instructions        106G  ±  313K      106G  …  106G           0 ( 0%)        0%
  cache_references   2.11G  ± 35.4M     2.07G  … 2.14G           0 ( 0%)        0%
  cache_misses       41.3M  ±  455K     40.8M  … 41.7M           0 ( 0%)        0%
  branch_misses       116M  ± 67.8K      116M  …  116M           0 ( 0%)        0%
Benchmark 2 (197 runs): new/zig build-obj -fno-emit-bin -femit-docs=docs ../lib/std/std.zig
  measurement          mean ± σ            min … max           outliers         delta
  wall_time          24.6ms ± 1.03ms    22.8ms … 28.3ms          4 ( 2%)        ⚡- 99.8% ±  0.3%
  peak_rss           87.3MB ± 60.6KB    87.2MB … 87.4MB          0 ( 0%)        ⚡- 91.9% ±  0.0%
  cpu_cycles         38.4M  ±  903K     37.4M  … 46.1M          13 ( 7%)        ⚡- 99.9% ±  0.2%
  instructions       39.7M  ± 12.4K     39.7M  … 39.8M           0 ( 0%)        ⚡-100.0% ±  0.0%
  cache_references   2.65M  ± 89.1K     2.54M  … 3.43M           3 ( 2%)        ⚡- 99.9% ±  0.2%
  cache_misses        197K  ± 5.71K      186K  …  209K           0 ( 0%)        ⚡- 99.5% ±  0.1%
  branch_misses       184K  ± 1.97K      178K  …  190K           6 ( 3%)        ⚡- 99.8% ±  0.0%

过去需要 13 秒以上,而现在只需要 25 毫秒。

Autodoc 新特性 ​

  • 更加可靠的链接:由于拥有完整的源代码,故我们拥有所有元信息,并且可以编写更强大的代码来从它们出现的上下文中查找标识符。

  • 交互式源列表:只要按下 u,你可以立刻查看当前声明或者定义的源代码(通过更改 location 哈希实现)!

  • 更加清晰的列表

  • 搜索支持注释

  • 更加友好的错误集合展示(支持通过其他的声明进行跳转)

  • 函数说明也会展示错误类型说明

  • 正确的类型检测(旧实现可能会猜错)

  • 浏览历史的正确记录(依赖于 popstate 事件 和历史记录 api)

INFO

更加全面的信息,可以查看 commit diff。

编译器 ​

x86 后端 ​

x86 后端现在通过了行为测试套件的 1765/1828(97%),与 LLVM 后端相比。由于它提供了显著更快的编译速度,所以在开发过程中有时会发现它很有用:

sh
Benchmark 1 (8 runs): zig-0.12.0 build-exe hello.zig
  measurement          mean ± σ            min … max           outliers         delta
  wall_time           667ms ± 26.7ms     643ms …  729ms          1 (13%)        0%
  peak_rss            175MB ± 19.3MB     168MB …  223MB          1 (13%)        0%
  cpu_cycles         3.42G  ±  532M     3.21G  … 4.74G           1 (13%)        0%
  instructions       6.20G  ± 1.05G     5.83G  … 8.79G           1 (13%)        0%
  cache_references    241M  ± 19.9M      234M  …  291M           1 (13%)        0%
  cache_misses       48.3M  ± 1.26M     47.7M  … 51.4M           1 (13%)        0%
  branch_misses      35.3M  ± 4.07M     33.7M  … 45.4M           1 (13%)        0%
Benchmark 2 (26 runs): zig-0.12.0 build-exe hello.zig -fno-llvm -fno-lld
  measurement          mean ± σ            min … max           outliers         delta
  wall_time           196ms ± 5.77ms     187ms …  208ms          0 ( 0%)        ⚡- 70.6% ±  1.7%
  peak_rss           88.7MB ±  721KB    87.8MB … 90.4MB          2 ( 8%)        ⚡- 49.3% ±  4.3%
  cpu_cycles          842M  ± 6.01M      836M  …  866M           1 ( 4%)        ⚡- 75.4% ±  6.0%
  instructions       1.60G  ± 9.62K     1.60G  … 1.60G           0 ( 0%)        ⚡- 74.1% ±  6.5%
  cache_references   56.6M  ±  378K     56.0M  … 57.3M           0 ( 0%)        ⚡- 76.6% ±  3.2%
  cache_misses       8.43M  ±  104K     8.30M  … 8.79M           2 ( 8%)        ⚡- 82.5% ±  1.0%
  branch_misses      7.20M  ± 30.2K     7.15M  … 7.28M           2 ( 8%)        ⚡- 79.6% ±  4.4%

当为 x86_64 目标编译时,可以通过传递 CLI 选项 -fno-llvm -fno-lld,或者在 std.Build.Step.Compile 上设置构建系统标志 use_llvm 和 use_lld 为 false 来访问这个后端。这个后端现在能够编译许多 Zig 项目,包括编译器本身。

直到使用它作为默认后端前的任务:

  • 100% 行为测试通过
  • 改进调试信息
  • 提高运行时性能

Windows 资源 ​

Zig 现在支持编译(和交叉编译)Windows 资源脚本(.rc文件)和 .manifest 文件,并将结果.res 文件链接到 PE/COFF 二进制文件的资源表中。

查看 Zig is now also a Windows resource compiler,了解这个功能的一些用例和使用细节。

链接器 ​

Zig 现在支持 x86_64、aarch64 的 ELF 链接,并部分支持 riscv64。对 LLD 的依赖预计将在下一个发布周期中被删除。可以使用 -fno-lld 标志来使用当前不是默认的 Zig 链接器。

缓存系统 ​

这个相当烦人的错误现在已经修复了:error: StreamTooLong when recompiling; duplicate source files in cache manifest。

修复方法是对缓存清单中列出的文件进行去重,这使得缓存命中速度显著提高。数据点:使用静态 musl libc 构建 hello world 缓存命中

sh
Benchmark 1 (61 runs): master/zig build-exe hello.c -target native-native-musl -lc
  measurement          mean ± σ            min … max           outliers         delta
  wall_time          81.4ms ± 1.76ms    77.7ms … 87.1ms          1 ( 2%)        0%
  peak_rss           64.6MB ± 77.7KB    64.4MB … 64.7MB          0 ( 0%)        0%
  cpu_cycles         97.2M  ± 1.04M     95.1M  …  101M           1 ( 2%)        0%
  instructions        153M  ± 11.1K      152M  …  153M           0 ( 0%)        0%
  cache_references   2.21M  ± 97.1K     2.05M  … 2.54M           2 ( 3%)        0%
  cache_misses        529K  ± 24.4K      486K  …  600K           4 ( 7%)        0%
  branch_misses       409K  ± 6.45K      397K  …  437K           1 ( 2%)        0%
Benchmark 2 (189 runs): cache-dedup/zig build-exe hello.c -target native-native-musl -lc
  measurement          mean ± σ            min … max           outliers         delta
  wall_time          25.8ms ± 1.26ms    23.9ms … 30.7ms         11 ( 6%)        ⚡- 68.4% ±  0.5%
  peak_rss           65.2MB ± 61.8KB    65.1MB … 65.4MB          2 ( 1%)        💩+  1.0% ±  0.0%
  cpu_cycles         41.2M  ±  608K     40.1M  … 46.3M           4 ( 2%)        ⚡- 57.6% ±  0.2%
  instructions       64.3M  ± 12.6K     64.3M  … 64.4M           2 ( 1%)        ⚡- 57.8% ±  0.0%
  cache_references   1.28M  ± 34.5K     1.21M  … 1.35M           0 ( 0%)        ⚡- 41.9% ±  0.7%
  cache_misses        348K  ± 18.6K      297K  …  396K           0 ( 0%)        ⚡- 34.2% ±  1.1%
  branch_misses       199K  ± 1.34K      197K  …  206K           6 ( 3%)        ⚡- 51.2% ±  0.2%

Bug 修复 ​

本次发布周期一共修复了 502 个 bug。

Comptime 指针访问 ​

Zig 在编译期访问指针有几个长期存在的错误。当试图以特殊的方式访问指针,例如加载数组的切片或重新解引用内存时,你有时会遇到一个错误的编译错误,编译器会要求 comptime 解引用需要具有明确定义的布局的类型。

#19630 的合并解决了这个问题。在 0.12.0 中,编译器在进行与编译期内存有关的复杂操作时,不再报告错误的编译错误。这个改变也包括对编译期 @bitCast 逻辑的一些修复;特别是,包含指针的位转换聚合不再错误地强制在运行时进行操作。

当前版本包含的 Bugs ​

Zig 目前存在一些 已知的 bugs,甚至包含一些 编译错误!

Zig 目前仍不成熟,即使是使用 0.12.0 也可能需要参与 zig 开发才能使之顺利在某些项目开发上正常工作!

当 Zig 达到 1.0.0 时,一级支持 将获得一个额外的错误策略作为要求。

工具链 ​

LLVM 17 ​

当前发布的新版本已经更新至 LLVM 17.0.6!

Zig 现在直接生成 LLVM bitcode 模块文件,然后将这些文件传递给 LLVM。这意味着一个没有 LLVM 库的 Zig 编译器仍然可以生成 .bc 文件,然后这些文件可以传递给 clang 进行编译。

musl 1.2.4 ​

尽管 musl v1.2.5 现在在上游可用,但这个版本的 Zig 仍然提供 v1.2.4。预计 Zig 的下一个版本将使用新的的 musl。

glibc 2.38 ​

当进行交叉编译时,现在可以使用 glibc 版本 2.35、2.36、2.37 和 2.38。

mingw-w64 ​

根据 Martin Storsjö 的建议,Zig 现在跟踪 mingw-w64 的最新主分支提交。

路线图 ​

0.13.0 发布周期的主要主题将是 编译速度。

在 0.13.0 发布周期中尽量实现一些里程碑特性:

  • 将 x86 后端作为调试模式的默认后端。
  • 链接器支持 COFF,移除对 LLD 的依赖。
  • 启用增量编译以快速重建。
  • 引入并发性到语义分析以进一步提高编译速度。

目前的思路是,优先考虑更快的编译将提高编译器本身的开发速度,从而在接下来的发布周期中修复更多的错误并完成更多的功能。

加快编译速度也可能会对语言本身做出一些调整。

Async/Await 特性进度 ​

在 0.11.0 版本发布后,异步函数被冻结。由于多个尚未解决的问题,尚不知未来如何处理该特性:

  • LLVM 无法优化它们。
  • 第三方调试器无法调试它们。
  • 取消问题。
  • 异步函数指针阻止了堆栈大小的知晓。

这些问题是可以克服的,但需要时间,Zig 团队目前正在关注其他优先事项。

贡献者和赞助者名单 ​

请查看官方的 发行说明!