HeadlinesBriefing favicon HeadlinesBriefing.com

Buildprof 可视化 Bun 编译时间

Hacker News •
×

我开发了 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,是因为来自 Jarred Sumner 的这条推文,主管 Bun JavaScript 运行时的首席架构师,一直在我脑海中挥之不去:具体来说,他声称 Bun 的新 Rust 编译比旧的 Zig 编译在 Linux 上快了 5 倍多。这真的让我困扰。在我的经验中,Zig 项目通常比同等复杂度的 Rust 项目编译得更快。这种直觉足以让我觉得有一个谜题需要解开。这还被推文中另一个重要但易被忽视的细节进一步加剧:Zig 编译使用了全 LTO,而 Rust 编译使用了薄 LTO。编译器通常 largely 相对独立地优化单独的编译单元。链接时优化 (LTO) 让它们能够跨这些边界进行优化。全 LTO 将这些单元合并到一个大的优化任务中,而薄 LTO 则保留了更多的分离,以便大部分工作可以并行运行。基于过去的经验,这种差异对编译时间可能产生巨大影响。这条推文虽然提到了这一点,但我好奇它实际上解释了多少头条级别的改进。我开始尝试复现这些数字。数字确实复现了。但现在怎么办?我检查了 Bun 1.3.14 和 Bun 1.4.0,并编写了脚本在 6 核 12 线程的 Linux VM 上重放它们的 Linux x64 CI 编译。这些脚本保留了编译步骤和它们的依赖关系,在单台机器上运行一切。我的计时结果与 Jarred 的结果处于同一个量级:Linux x64 build Zig 时代 30m06s,Rust 时代 5m37s。我的单机 CI-profile replay 24m24s 5m40s。好的,所以差距在我机器上也显现出来了。但两次测量之间除了语言之外还有很多变化;到底是什么导致了这一点?是 Zig 编译器真的耗时那么长吗?或者可能是 Full LTO 链接?又或者是 Bun 编译中我还没想到的其他东西?这就是我的分析和开发者工具大脑介入的时候了。通常,当我试图理解为什么某些东西慢时,我想要一个追踪:发生了什么、什么时候发生以及花了多长时间。对于这些编译拥有那样的追踪会很酷,把它们放在时间线上,看看它们的时间实际花在哪里。但编译涉及许多不同的工具,每个工具对正在发生的事情有自己的想法。我可以记录什么才能让我跨所有工具看到情况?编译是进程树。当你运行 cargo build 或 zig build 时,感觉就像运行一个程序。构建系统会计算需要重建什么、这些部分之间的顺序以及什么可以并行运行。但通常,它本身不执行所有这些工作;它会启动编译器、代码生成器、归档器、链接器和任意脚本。这些可以启动更多程序,进而启动更多…… 不同的构建系统用不同的方式描述这项工作。Cargo 看到 crate,Ninja 看到构建边缘,CMake 为另一个构建系统生成说明。从操作系统