在 Linux 的 nightly 版本上使用 `rust-lld` 提高链接速度

2024 年 5 月 17 日 · Rémy Rakic 代表 编译器性能工作组 发布

总结:rustc 将在 x86_64-unknown-linux-gnu 平台上的 nightly 版本默认使用 rust-lld,以显著减少链接时间。

背景介绍

链接时间通常是编译时间的重要组成部分。当 rustc 需要构建二进制文件或共享库时,它通常会调用系统上安装的默认链接器来完成此操作(这可以在命令行上或通过编译代码的目标进行更改)。

链接器承担着重要的工作,需要考虑稳定性、向后兼容性等等。由于这些及其他原因,在最流行的操作系统上,它们通常是较旧的程序,设计于计算机只有单核的时代。因此,它们在现代机器上往往速度较慢。例如,在 Linux 上以调试模式构建 ripgrep 13 时,大约一半的时间实际上花费在链接器上。

然而,存在不同的链接器,通常改善链接时间的建议是使用这些更新、更快的链接器之一,例如 LLVM 的 lld 或 Rui Ueyama 的 mold

Rust 的一些 wasm 和 aarch64 目标已经默认使用 lld。使用 rustup 时,rustc 会附带一个版本的 lld 用于此目的。当 CI 构建用于编译器的 LLVM 时,它也会构建并打包该链接器。为了避免与用户机器上已安装的任何 lld 冲突,它被称为 rust-lld

由于链接时间的改进非常显著,因此在最流行的目标中将其设为默认值会很有益处。这已经被讨论了很长时间,例如在问题 #39915#71515 中,并且 rustc 已经提供了 nightly 标志来使用 rust-lld

到目前为止,我们相信已经在 CI、crater 和我们的基准测试基础设施上完成了所有可能的内部测试。我们现在希望扩大测试范围并收集实际使用反馈和用例。因此,我们将为 x86_64-unknown-linux-gnu 平台的 nightly 构建启用 rust-lld 作为默认使用的链接器。

优势

虽然这也使得编译器未来可以使用更多的链接器特性,但最直接的好处是链接时间的极大改善。

以下是上述 ripgrep 示例的更多详细信息:链接时间缩短了 7 倍,使端到端编译时间减少了 40%。

Before/after comparison of a ripgrep debug build

大多数二进制文件应该会看到一些改进,但对于更大的二进制文件或涉及调试信息的情况,改进尤其显著。这些情况通常在链接器中遇到瓶颈。

这是我们基准测试完整结果的链接

如果测试进展顺利,我们就可以为 x86_64-unknown-linux-gnu 用户默认稳定使用这个更快的链接器,之后可能会考虑其他目标。

可能的缺点

根据我们之前的测试,我们并不期望在实践中发生问题。对于绝大多数情况,它都是即插即用的替代品,但 lld 与 GNU ld 并非 bug-for-bug 兼容。

无论如何,如果发生任何问题,可以禁用使用 rust-lld:使用 -Z linker-features=-lld 标志可以回退到使用系统的默认链接器。

某些依赖于这些差异的 crate 可能需要额外的链接参数。例如,我们在 crater 运行中看到不到 20 个 crate 由于 封装符号 的默认设置不同而导致链接失败:这些可能需要 -Clink-arg=-Wl,-z,nostart-stop-gc 来匹配传统的 GNU ld 行为。

部分性能上的显著提升来自于并行处理,这在资源受限的环境中可能不理想。

总结

从明天的 rustup nightly (nightly-2024-05-18) 开始,rustc 将在 x86_64-unknown-linux-gnu nightly 版本上使用 rust-lld,以大幅提高链接速度。如果您遇到问题,请通过在 GitHub 上开启一个议题告知我们。

如果发生这种情况,您可以使用 -Z linker-features=-lld 标志回退到默认链接器。可以通过将其添加到常用的 RUSTFLAGS 环境变量中,或者添加到项目的 .cargo/config.toml 配置文件中,如下所示:

[target.x86_64-unknown-linux-gnu]
rustflags = ["-Zlinker-features=-lld"]