在 Linux 上使用 `rust-lld` 加速 nightly 版本的链接时间

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

简而言之:rustc 将在 nightly 版本的 `x86_64-unknown-linux-gnu` 上默认使用 `rust-lld`,以显著减少链接时间。

一些背景

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

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

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

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

由于链接时间的改进非常显著,因此在最流行的目标中使用它作为默认设置将是一个不错的选择。这个问题已经讨论了很长时间,例如在问题 #39915#71515 中,rustc 已经提供了 nightly 标志来使用 `rust-lld`。

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

优势

虽然这也能让编译器在未来使用更多链接器功能,但最直接的优势是链接时间大幅改善。

以下是上面提到的 ripgrep 示例的更多细节:链接时间减少了 7 倍,导致端到端编译时间减少了 40%。

Before/after comparison of a ripgrep debug build

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

以下是 链接 到我们基准测试的完整结果。

如果测试顺利,我们就可以稳定地将此更快的链接器作为 `x86_64-unknown-linux-gnu` 用户的默认链接器,然后再考虑其他目标。

可能的缺点

根据我们之前的测试,我们并不期望在实践中出现问题。它在绝大多数情况下都是直接替换,但 `lld` 与 GNU ld 并不完全兼容。

无论如何,如果出现任何问题,可以使用 `rust-lld` 可以禁用:使用 `-Z linker-features=-lld` 标志恢复使用系统的默认链接器。

一些依赖于这些差异的板条箱可能需要额外的链接参数。例如,我们在 crater 运行中看到 <20 个板条箱无法链接,因为关于 封装符号 的默认设置不同:这些板条箱可能需要 `-Clink-arg=-Wl,-z,nostart-stop-gc` 来匹配传统的 GNU ld 行为。

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

总结

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

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

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