开发者发现 Nix、Bazel 和 buck2 底层共享内涵模型
来源标题:The Intensional Model in Disguise
开发者通过构建一个存储、两个前端的原型发现,Nix、Bazel 和 buck2 表面差异虽大,底层都采用内容寻址的内涵模型(intensional model)。Nix 将其称为内容寻址派生(CA derivations),Bazel 和 buck2 未命名该模型但同样使用内容寻址。三者相似程度比作者最初预想的更深。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
尽管表面上看起来差异很大,Nix、Bazel 和 buck2 在底层其实惊人地相似。不信?
我在一个 store 里构建了一个原型、两个前端来证明这一点。不过结果发现,它们的相似之处比我意识到的还要深。
进一步思考后,我意识到外延与内涵模型(intensional model)才是更本质的共同点。什么是内涵模型?
提示 我以前写过关于 Nix 的内涵模型,更常见的叫法是内容寻址(content-addressed,CA)derivation。它在 Nix 里已经“实验性”了好多年。🧪
在内涵模型中,输出按内容寻址,动作也按内容引用其输入。而外延模型(extensional model)则按产生输出的配方和输入来寻址输出。Nix 也把它称为“内容寻址 derivation”(CA derivation)。Bazel 和 buck2 没有给自己的模型起名字,但它们同样使用内容寻址。
想想这个巧合:Eelco 的博士论文发表于 2006 年,同年 Google 开始开发 Blaze,也就是 Bazel 的前身 [引用]。
让我们用一个简单的 C 例子仔细看看这三个系统,看看它们如何都实现了内涵模型。🥸
// greet.h void greet(const char name); // greet.c #include <stdio.h> #include "greet.h" / say hi */ void greet(const char *name) { printf("hello, %s\n", name); // main.c #include "greet.h" int main(void) { greet("world"); return 0;
在传统的外延模型(输入寻址)中,构建动作上游任何位置的改动都会使该动作的输出失效。即使改动 与输出无关(比如源文件里改了个注释),也会如此。相反,内涵模型(内容寻址)只有在输出本身的内容发生变化时,才会使动作的输出失效。
在我们的 C 例子中,如果把 greet.c 里的注释从 say hi 改成 say hello,编译出的目标文件 greet.o 仍然会逐字节完全相同,因为注释不影响编译输出。在理想世界里,hello 的链接就不需要重做,因为它的输入内容没有变化。
{ pkgs ? import <nixpkgs> {}, ca ? false }: let extra = pkgs.lib.optionalAttrs ca { __contentAddressed = true; }; in rec {
The cp is needed otherwise the source file's store path ends up
in the object file, which is a leak of the extensional model.
greet = pkgs.runCommandCC "greet" extra '' cp ${./greet.h} greet.h; cp ${./greet.c} greet.c mkdir -p $out cc -c greet.c -o $out/greet.o ''; hello = pkgs.runCommandCC "hello" extra '' cp ${./greet.h} greet.h; cp ${./main.c} main.c mkdir -p $out/bin cc main.c ${greet}/greet.o -o $out/bin/hello '';
默认情况下,Nix 使用外延(输入寻址)模型。如果我们修改注释并重新构建:
$ nix-build -A hello building '/nix/store/3hwrlpnji5bh5bajdg52s7zbg7ihs6j6-greet.drv'... building '/nix/store/2k5is7yim1j8pb3qjz89bw45cqzcy2y5-hello.drv'.../nix/store/122jc3iz6qkrgm907kdz7rrk3lp4qrma-hello
所有东西都重新构建了。😩
外延模型中的 store path 是配方的哈希,而 hello 的配方又包含了 greet 的配方。递归真有趣!
上游任意位置改动一个字节,下游每个路径都会随之改变。🌀
greet.c (comment edited) gdrv greet.drv hash of recipe src->gdrv in recipe hdrv hello.drv hash of recipe gdrv->hdrv in recipe hout /nix/store/…-hello new path hdrv->hout
警告 这两个 hello 二进制甚至不是逐字节相同的:cc-wrapper 会把 $out/lib 放进 RUNPATH,所以二进制里包含了它自己的 store path。外延模型泄漏到了字节里。🙃
如果我们启用内容寻址 derivation 来构建,会看到 greet 被重新构建,但 hello 没有。🎉
$ nix-build --extra-experimental-features ca-derivations -A hello --arg ca true building '/nix/store/qpa1q7g5yjvhdbnfap4y3r2glzyflyv4-greet.drv'... resolved derivation: '/nix/store/1xnbxrrprs50879ivzmjpv7cbk44w258-hello.drv' -> '/nix/store/rs2j6r2gvz2vi7nfvqj1s3yxx4lqpa3m-hello.drv'.../nix/store/l2nrycgcg97vrz4nb59icwz9i04ys8jg-hello
你可以阅读《demystifying Nix’s intensional model》了解 CA derivation 工作原理的更详细解释,简单来说,Nix 会重写 hello.drv,让它指向 greet 的内容寻址输出,而不是 greet.drv。解析后的 rs2j6r…-hello.drv 与我们在修改前构建的是同一个。
由于没有“输入”发生变化,Nix 避免了重新构建。
这与 Bazel 和 buck2 的行为相同,它们默认就是内涵式的。
cc_library( name = "greet", srcs = ["greet.c"], hdrs = ["greet.h"], ) cc_binary( name = "hello", srcs = ["main.c"], deps = [":greet"], )
如果我们修改注释并用 Bazel 或 buck2 重新构建,会看到编译运行了,但链接是 action cache 命中。
$ bazel build //:hello --explain=explain.log INFO: 2 processes: 1 action cache hit, 1 internal, 1 linux-sandbox. $ cat explain.log Executing action 'Compiling greet.c': action changed since cached execution. $ buck2 build //:hello Commands: 1 (cached: 0, remote: 0, local: 1)BUILD SUCCEEDED $ buck2 log what-ran build root//:greet (c_compile greet.c (pic)) local ... clang -c ./greet.c ...
Bazel 和 buck2 没有输入寻址模式。它们始终使用内涵模型。一个 action 的 key 是其命令行(用于强制重建)的哈希加上其输入的内容摘要。
greet.c (comment edited) cc compile key = hash(argv, digest(greet.c)) src->cc obj greet.o same digest cc->obj link link key = hash(argv, digest(greet.o), …) obj->link by content out hello cache hit link->out
改动只会传播到字节真正发生变化的地方。这个优化叫做 early cutoff,正是内涵模型的核心卖点。
Nix 的 CA derivation 与 Bazel 的 action cache 之间存在清晰的映射。
解析后的 derivation 就是一个 Bazel/buck2 action。🤯
Nix CA cluster_reapi REAPI rdrv resolved hello.drv real realisations rdrv->real lookup path /nix/store/<content hash>-hello real->path act
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。