用 sccache 把 Rust 增量编译压到 3 秒的一次记录

环境是 Linux x86_64,rustc 1.78.0,项目 41 个 crate,主二进制 crate 依赖一半以上。全量 cargo build 冷启动约 4 分 20 秒,改一行业务代码后 touch 再编,稳定在 40 秒上下。机器是 8 核 16G,/tmp 在 tmpfs 上。问题不在链接慢,主要是依赖 crate 反复重编——target/debug/deps 里的 rlib 每次被判定失效。

先确认到底谁在重编:

CARGO_LOG=cargo::core::compiler::fingerprint=info cargo build 2>&1 | grep dirty

输出里大量依赖 crate 被标 dirty,原因写的是 mtime。根因是 CI 拉代码时 git clone 出来文件 mtime 全是当前时间,本地 rsync 同步也把 mtime 打乱了。

做法分两步。第一步固定 mtime 判定的容忍度,在 .cargo/config.toml 里加:

[build]
incremental = true

第二步引 sccache,只缓存本地磁盘,不走远端:

[build]
rustc-wrapper = "/usr/local/bin/sccache"
export SCCACHE_DIR=/data/sccache
export SCCACHE_CACHE_SIZE=20G
sccache --start-server
sccache --show-stats

第一次全量编译仍然要 4 分多,但 sccache 把 300 多个编译单元写进了缓存。之后清掉 target 再编,命中率 92%,耗时 1 分 10 秒。日常改一行代码,走增量加 sccache 后稳定在 3 秒左右。

这样做能成立,是因为 sccache 的 key 是 rustc 参数、源码内容和环境变量的哈希,和 mtime 无关。代价有两个:一是磁盘,20G 缓存大概能撑两三个分支来回切,切多了会开始淘汰,命中率掉得很快;二是 RUSTFLAGS 或 feature 一变,key 全变,缓存等于白存,所以 CI 和本地必须用完全一致的 flags,否则两边互相污染不了但也互相帮不上。

会失效的地方我踩过几个。proc-macro crate 的编译产物依赖宿主环境,跨机器共享时版本对不上会直接编译失败,不是慢的问题。-C incremental 和 sccache 同时开时,增量目录本身不进缓存,改代码的那次编译省不下来,只有依赖部分受益。还有 build.rs 里读环境变量的,变量值不进 key,改了值不会触发重编,得手动 cargo clean -p。

最后一个没完全搞明白的:sccache --show-stats 里 non-cacheable 那栏一直有几十个单元,我猜是链接步骤和某些 proc-macro,但没逐个验证过。你们项目里这栏一般是多少,有没有办法压下去?

0
赞同度 100% · 0 条评论 · 7 阅读

0 条评论

支持 Markdown · 回复会实时推送给对方

还没有评论,写下第一条。