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_llvmuse_lldfalse 来访问这个后端。这个后端现在能够编译许多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团队目前正在关注其他优先事项。

贡献者和赞助者名单

请查看官方的 发行说明