入门 The Cargo Team 2026-09-13 15:48:21 · 0 阅读

第13章 优化构建性能:Cargo配置与源码组织指南

Cargo 的配置选项和源代码组织方式可以改善构建性能,但这需要在构建性能与其他方面之间做出取舍——具体取决于哪些方面对你更重要。

和优化运行时性能一样,一定要针对你实际关心的工作流程来测量这些改动。我们提供的只是一般性建议,你的情况可能不同,某些做法在你的使用场景下反而可能让构建变慢。

值得考虑的典型工作流程包括:

  • 开发过程中的编译反馈(改完代码后运行 cargo check
  • 开发过程中的测试反馈(改完代码后运行 cargo test
  • CI 构建

Cargo 和编译器配置

Cargo 的默认配置是在多个方面之间寻求平衡,包括可调试性、运行时性能、构建性能、二进制体积等。本节介绍几种修改默认配置的方法,目标是在构建性能上做到最优。

覆盖默认配置的常见位置有:

减少生成的调试信息

建议:在 Cargo.toml.cargo/config.toml 中添加:

[profile.dev]
debug = "line-tables-only"

[profile.dev.package."*"]
debug = false

[profile.debugging]
inherits = "dev"
debug = true

这样做会:

  • 修改 dev 配置文件(开发命令的默认配置):
    • 仅保留工作区成员生成有用 panic 回溯所需的最少 调试信息
    • 避免为依赖项生成任何调试信息
  • 提供可选入口,通过 --profile debugging 开启调试模式

注意: 重新评估 dev 配置文件的工作正在 #15931 中跟进。

权衡:

  • ✅ 更快的代码生成速度(cargo build
  • ✅ 更短的链接时间
  • target 目录占用磁盘空间更小
  • ❌ 需要完全重新编译才能获得高质量的调试体验

使用替代代码生成后端

建议:

  • 安装 Cranelift 代码生成后端的 rustup 组件
    $ rustup component add rustc-codegen-cranelift-preview --toolchain nightly
    
  • 在你的 Cargo.toml.cargo/config.toml 中添加:
    [profile.dev]
    codegen-backend = "cranelift"
    
  • 使用 -Z codegen-backend 运行 Cargo,或在 .cargo/config.toml 中启用 codegen-backend 特性。
    • 这是必需的,因为该特性目前仍处于不稳定状态。

这将使 dev 配置文件 使用 Cranelift 代码生成后端 来生成机器代码,而非默认的 LLVM 后端。Cranelift 后端的代码生成速度应快于 LLVM,从而提升构建性能。

权衡:

  • ✅ 更快的代码生成速度(cargo build
  • 必须使用 nightly Rust 和 不稳定的 Cargo 特性
  • ❌ 生成代码的运行时性能较差
    • 加速了 cargo test 的构建部分,但可能增加其测试执行部分的时间
  • ❌ 仅支持特定目标平台
  • ❌ 可能不支持所有 Rust 特性(例如 unwinding)

启用实验性并行前端

建议:在你的 .cargo/config.toml 中添加以下内容:

[build]
rustflags = "-Zthreads=8"

这条 rustflags 会启用 Rust 编译器的并行前端,并指定使用 n 个线程。n 的值应根据系统可用的核心数来定,但收益会边际递减。我们建议最多使用 8 个线程。

权衡:

  • ✅ 构建速度更快(包括 cargo checkcargo build
  • 必须使用 nightly Rust 和一个不稳定的 Rust 特性

使用替代链接器

可以考虑安装并配置替代链接器,例如 LLDmoldwild。以在 Linux 上配置 mold 为例,你可以在 .cargo/config.toml 中添加:

[target.'cfg(target_os = "linux")']
# mold, if you have GCC 12+
rustflags = ["-C", "link-arg=-fuse-ld=mold"]

# mold, otherwise
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=/path/to/mold"]

虽然依赖项可以并行构建,但所有依赖项的链接工作会在构建的最后一次性完成。这往往会导致链接过程占据构建时间的很大一部分,尤其是在增量重建时。通常,Rust 默认使用的链接器已经相当快,换用其他链接器的提升可能微乎其微;但也不尽然。例如,除了 x86_64-unknown-linux-gnu 之外的 Linux 目标平台仍在使用较慢的 Linux 系统链接器(详见 rust#39915)。

权衡:

  • ✅ 链接时间更短
  • ❌ 可能无法支持所有使用场景,特别是当你依赖 C 或 C++ 库时

为整个 workspace 解析 features

可以考虑在项目的 .cargo/config.toml 中添加:

[resolver]
feature-unification = "workspace"

调用 cargo 时,feature 的激活取决于你选中的 workspace 成员。但在开发一个应用时,你可能需要构建和测试其中的多个包,而不同包可能会为共同的依赖激活不同的 feature 集合,从而引发不必要的重新构建。使用 feature-unification 后,无论当前构建或测试的是哪个包,依赖都会激活相同的 feature 集合,从而复用更多已构建的依赖。

权衡:

  • ✅ 在 workspace 中构建不同包时减少重新构建
  • 需要 nightly 版 Rust 和不稳定的 Cargo 特性
  • ❌ 某个包激活了某个 feature,可能掩盖其他本应激活它却没有激活的包中的 bug
  • ❌ 如果 --workspace 的 feature 统一对你不适用,那这个选项也不适用

减少构建的代码

移除未使用的依赖

建议:定期用以下命令检查并清理未使用的依赖:

$ cargo +nightly check -Zcargo-lints --workspace --all-targets

以下情况可能产生误报:

  • 依赖是否被使用由 build.rsRUSTFLAGS 动态控制

此外,还应定期查看隐藏的 cargo::unused_dependencies 结果:

$ CARGO_LOG=cargo::diagnostics::rules::unused_dependencies=debug cargo +nightly check -Zcargo-lints --workspace --all-targets

该命令会显示以下情况下可能未使用的依赖:

  • 来自 registry 和 git 的依赖
  • 你的 package.rust-version 过旧、无法使用 [lints.cargo]
  • 你的依赖可能只是用来约束某个传递依赖的版本(这种情况下应改用 [target."cfg(false)".dependencies]
  • 当你的依赖可能被用于激活传递性依赖上的某个特性时
  • 你的 [dev-dependencies],因为目前尚无方法确保这些依赖的所有使用者都已构建

修改代码时,很容易忽略某个依赖已不再使用,进而将其移除。

权衡利弊:

  • ✅ 加快完整构建和链接速度
  • 检查未使用依赖时,需要 nightly Rust 版本及一个< a href="#lintscargo">不稳定的 Cargo 特性
  • ❌ 需要从误报中甄别未使用的依赖,这需要额外精力

移除依赖中未使用的特性

建议:定期使用第三方工具(如cargo-features-managercargo-unused-features)审查并移除依赖中未使用的特性。

修改代码时,很容易忽略依赖的某个特性已不再使用,进而将其移除。 这样做可以减少需要构建的传递性依赖数量, 或减少被构建 crate 内的代码量。 移除特性时需格外谨慎,因为特性还可能用于实现期望的行为或性能优化, 这些影响未必能通过编译或测试直观地发现。

权衡利弊:

  • ✅ 加快完整构建和链接速度
  • ❌ 可能会将某些特性误报为未使用

评论 (0)