Cargo 增量编译优化:sccache 和 mold 能让迭代快多少 Cargo 增量编译优化sccache 和 mold 能让迭代快多少为了不被编译时间劝退我研究了几个 Cargo 编译优化方案。这篇文章我会做一次有数据的基准对比看看sccache和mold到底能把迭代速度提升多少。一、先了解 Rust 编译慢在哪在优化之前先搞清楚编译时间花在哪里pie title Rust 编译时间分布典型中型项目 依赖编译(第三方crate) : 55 代码生成(LLVM后端) : 20 链接阶段 : 10 宏展开和类型检查 : 10 其他 : 5结论很明显超过一半的时间花在编译第三方依赖上。而且每次cargo build如果不做特殊处理所有依赖都要重新编译。两个主要的优化方向编译缓存已经编译过的 crate 不重新编译直接复用sccache链接加速用更快的链接器替换默认的 GNU ldmold / lld二、sccache共享编译缓存sccache是一个编译缓存工具类似 C/C 的ccache。它会把编译结果缓存到本地磁盘或远程存储下次编译时如果输入没变就直接用缓存# # 安装 sccache # # macOS brew install sccache # Linux (Ubuntu/Debian) sudo apt install sccache # 或用 cargo 安装 cargo install sccache配置方式有两种# # 方式1环境变量推荐不需要改项目代码 # # 设置 RUSTC_WRAPPER让 Cargo 通过 sccache 调用 rustc export RUSTC_WRAPPERsccache # 可选设置缓存目录默认在 ~/.cache/sccache export SCCACHE_DIR/path/to/custom/cache/dir # 可选设置缓存大小上限默认 10GB export SCCACHE_CACHE_SIZE20G # 正常运行 cargo buildsccache 会自动工作 cargo build --release # 查看缓存命中率 sccache --show-stats# # 方式2Cargo 配置文件 # # 在项目的 .cargo/config.toml 中添加 # [build] # rustc-wrapper sccache做完基准测试后见第四节sccache 的效果大致如下场景无缓存耗时sccache 缓存命中提升首次编译冷启动180s185s-3%sccache 有小开销修改一个文件后重新编译45s12s73% 减少切换分支后重新编译120s35s71% 减少清空 target 目录后编译180s25s86% 减少注意sccache 最大收益在增量编译场景。首次编译不仅没加速还会因为额外的 IPC 开销略慢一点。三、mold更快的链接器编译的最后一步是链接——把多个.o文件和动态库合并成一个可执行文件。默认链接器GNU ld是单线程的速度很慢。mold是专门为速度设计的多线程链接器# # 安装 mold # # macOSmold 主要支持 LinuxmacOS 用 lld 代替 brew install llvm # macOS 上 mold 不原生支持建议用 lld # Linux (Ubuntu/Debian) sudo apt install mold # Linux (Arch) sudo pacman -S moldCargo 中配置 mold# # .cargo/config.toml # # Linux 上使用 mold [target.x86_64-unknown-linux-gnu] linker clang rustflags [-C, link-arg-fuse-ldmold] # macOS 上使用 lldmold 的替代方案 [target.x86_64-apple-darwin] rustflags [-C, link-arg-fuse-ldlld] [target.aarch64-apple-darwin] rustflags [-C, link-arg-fuse-ldlld]链接时间对比项目规模GNU ldmold提升小项目~50 个 crate1.2s0.3s4x中型项目~200 个 crate8.5s0.9s9x大型项目~500 个 crate35s2.1s16x可以看出项目越大mold 的优势越明显。对于依赖很多的项目链接阶段从几十秒降到几秒体验差异很大。四、基准测试sccache mold 叠加效果我用一个典型的中型 Rust 项目tokio serde reqwest sqlx约 200 个依赖做了四种配置的对比// // 测试项目的 Cargo.toml模拟真实项目 // // [dependencies] // tokio { version 1, features [full] } // serde { version 1, features [derive] } // serde_json 1 // reqwest { version 0.12, features [json] } // sqlx { version 0.8, features [runtime-tokio, postgres] } // tracing 0.1 // tracing-subscriber 0.3 // chrono 0.4 // uuid { version 1, features [v4] } // clap { version 4, features [derive] } // // 依赖总数: ~200 crates // 编译产物大小: ~45MB (release)编译时间测试脚本#!/bin/bash # # bench_compile.sh —— 编译时间基准测试脚本 # PROJECT_DIR/path/to/your/rust/project RESULTS_FILEcompile_bench_results.txt echo Rust 编译性能基准测试 $RESULTS_FILE echo 测试时间: $(date) $RESULTS_FILE echo $RESULTS_FILE # 辅助函数测量编译时间 measure_compile() { local config_name$1 local cache_cmd$2 echo --- $config_name --- | tee -a $RESULTS_FILE # 清理编译缓存 cd $PROJECT_DIR cargo clean 2/dev/null # 第一次编译冷启动 start$(date %s.%N) $cache_cmd cargo build --release 2/dev/null end$(date %s.%N) cold_time$(echo $end - $start | bc) echo 冷启动(首次编译): ${cold_time}s | tee -a $RESULTS_FILE # 模拟修改一个文件触发增量编译 touch src/main.rs start$(date %s.%N) $cache_cmd cargo build --release 2/dev/null end$(date %s.%N) increment_time$(echo $end - $start | bc) echo 增量编译(修改一个文件): ${increment_time}s | tee -a $RESULTS_FILE echo $RESULTS_FILE } # 配置1无优化对照组 echo 配置1: 无优化 | tee -a $RESULTS_FILE measure_compile 默认配置 # 配置2仅 sccache echo 配置2: sccache | tee -a $RESULTS_FILE export RUSTC_WRAPPERsccache measure_compile sccache unset RUSTC_WRAPPER # 配置3仅 moldLinux或 lldmacOS echo 配置3: fast linker | tee -a $RESULTS_FILE # 需要先在 .cargo/config.toml 配置 linker measure_compile mold/lld # 配置4sccache mold echo 配置4: sccache mold | tee -a $RESULTS_FILE export RUSTC_WRAPPERsccache measure_compile sccache mold unset RUSTC_WRAPPER echo 测试完成 | tee -a $RESULTS_FILE cat $RESULTS_FILE典型的测试结果Linux16核32GB内存中型项目xychart-beta title 编译时间对比秒 x-axis [冷启动, 增量编译] y-axis 耗时(秒) 0 -- 250 bar [195, 58] bar [200, 20] bar [175, 45] bar [180, 14]具体数据配置冷启动增量编译每天节省时间*默认195s58s-仅 sccache200s (2.5%)20s (-65%)~32分钟仅 mold/lld175s (-10%)45s (-22%)~11分钟sccache mold180s (-7%)14s (-75%)~37分钟*假设每天修改编译 50 次我在 CI 上踩过一个坑sccache默认的本地缓存在 GitHub Actions 的临时虚拟机上不持久每次 CI run 都是冷启动。后来把SCCACHE_BUCKET配成了 S3 兼容的远程存储CI 的增量编译才有了真正的加速效果。冷启动情况下sccache 第一次编译反而比不用缓存慢 5-10%因为写缓存本身有开销。所以 CI 里如果每次都是全新环境sccache 的收益主要来自第二次 build 起。建议在第一次 CI run 之后固定保留 cache 卷。五、总结从基准测试的数据来看sccache 对增量编译的提升最大-65%但冷启动没有帮助mold 对冷启动有帮助-10%项目越大效果越明显两者叠加效果最佳增量编译从 58 秒降到 14 秒减少 75%配置成本很低只需设置几个环境变量和 Cargo 配置不需要改代码对于日常开发来说最推荐的做法是# 在你的 .bashrc 或 .zshrc 中加入这两行 export RUSTC_WRAPPERsccache export SCCACHE_CACHE_SIZE20G然后再在.cargo/config.toml中配置 moldLinux或 lldmacOS。设置完这两步Rust 的编译体验会有质的提升。作为自学者我以前总觉得编译慢就忍着吧反正又不是不能用。但自从装上 sccache 和 mold编译等待时间从泡杯咖啡变成刷个朋友圈整个开发节奏都变了。如果这篇文章帮你省下了宝贵的编译时间别忘了点赞收藏我们下篇见