首页 时政热点 科技头条 智能AI 安全攻防 数码硬件 开发者生态 归档 标签 关于

我制作了一个构建可视化工具来了解 Bun 的编译时间

2026-09-12 1 阅读 约6分钟阅读 lalitmaganti
分享:
字号:
我构建了 buildprof (Github),这是一个开源跟踪工具,可以显示在 Linux 上编译软件时时间都花在哪里。这是一个实时视频,展示了 ripgrep 的干净构建:观看 buildprof 演示 有时,构建速度很慢,因为需要编译大量代码。但通常情况下,存在可修复的问题:并行性差、重复工作、依赖项下载或大量编译器/链接器调用。 buildprof 使所有这些都清晰可见,因此您可以看到哪些内容值得研究和优化。您可以通过将 buildprof -- 放在您已经使用的任何构建命令前面来运行它: buildprof -- make -j16 buildprof -- Cargo build buildprof -- ninja -C out/target buildprof -- just build buildprof -- ./dev/custom-build-script.sh buildprof 记录您的构建命令启动的每个进程,包括它们的子进程(及其子进程…),并将它们放在一个时间线上。时间从左向右移动,条形宽度显示持续时间,子进程出现在启动它们的任何内容下方。我之所以创建 buildprof,是因为 Bun JavaScript 运行时的首席架构师 Jarred Sumner 的这条推文一直萦绕在我的脑海中:具体来说,Bun 的新 Rust 版本在 Linux 上比旧 Zig 版本快 5 倍的说法确实让我困扰。根据我的经验,Zig 项目的编译速度通常比类似复杂性的 Rust 项目快得多。这种直觉足以让我觉得有一个谜团需要解开。推文中另一个重要但很容易被忽略的细节进一步加剧了这一点:Zig 构建使用 Full LTO,而 Rust 构建使用 ThinLTO。编译器通常在很大程度上孤立地优化单独的编译单元。 1 链接时优化 (LTO) 让他们能够跨越这些边界进行优化。 Full LTO 将这些单元整合到一项大型优化作业中,而 ThinLTO 则保留了更多的分离性,因此大部分工作可以并行运行。根据过去的经验,这种差异会对构建时间产生巨大影响。该推文顺便提到了这一点,但我想知道它解释了多少标题改进。我首先尝试重现这些数字。复制的数字。但现在怎么办? # 我检查了 Bun 1.3.14 和 Bun 1.4.0 并编写了一些脚本来在 6 核、12 线程 Linux VM 上重放它们的 Linux x64 CI 构建。这些脚本保留了构建步骤及其依赖关系,在一台机器上运行所有内容。 2 我的时间安排与 Jarred 大致相同:Linux x64 构建 Zig 时代 Rust 时代 Bun 报告的 CI 中位数 30m06s 5m37s 我的单机 CI 配置文件重播 24m24s 5m40s 好的,所以差距也出现在我的机器上。但除了语言之外,这两种测量方法之间也发生了很多变化。那么到底是什么造成的呢?是 Zig 编译器花费了所有额外的时间吗?或者也许是完整的 LTO 链接?或者也许在 Bun 的构建中还有其他一些我根本没想过要看的东西?这就是我的分析和开发人员工具大脑发挥作用的地方。通常,当我试图理解为什么某些事情很慢时,我想要跟踪:发生了什么、何时发生以及花了多长时间。对于这些构建来说,将它们放在时间线上并查看它们的时间实际花在了哪里,这真的很酷。但构建涉及许多不同的工具,每个工具对正在发生的事情都有自己的想法。我可以记录什么让我看到所有这些?构建是进程树 # 当您输入cargo build 或zig build 时,感觉就像您正在运行一个程序。构建系统会计算出需要重建的内容、这些部分之间的顺序以及可以并行运行的内容。但一般来说,它本身并不执行所有这些工作;它启动编译器、代码生成器、归档器、链接器和任意脚本。哪个可以启动更多程序,哪些程序可以启动更多……不同的构建系统以不同的方式描述其工作方式。 Cargo 看到板条箱,Ninja 看到构建边缘,CMake 为另一个构建系统生成指令。然而,从操作系统的角度来看,它们(大多数)看起来像是启动其他进程的进程。 3 例如,Rust 版本可能包含这样的链:cargo └── rustc └── cc └──collect2 └── ld.lld 如果我们记录每个子进程的开始和结束时间,我们可以将它们放在时间线上。该链在 buildprof 中如下所示: 还有几个很好的属性可以在这一层可视化构建: 它与构建系统无关:Cargo、Ninja、Zig、Make 和大多数其他构建系统通过生成进程来完成大部分工作,因此我们不需要为每个构建系统编写特殊的集成。它自然包括自定义脚本:这包括构建系统之上的脚本(存储库设置、依赖项获取)和其之下的脚本(代码生成器、资产处理器)。我们可以在构建步骤之间跟踪文件:记录每个进程读取和写入哪些文件,让我们了解哪些步骤为其他步骤生成输入。这甚至可以跨构建系统工作!这个加夫
这篇文章对您有帮助吗?

订阅66必读

每日精选科技资讯,直达你的邮箱