很多人给 Rust 项目的 CI 挂上 sccache,是奔着「编译缓存 = 全量提速」去的:一次配置,以后每次构建都秒过。真跑起来才发现,命中率低得可怜,改一行业务代码照样从头编译大半天。
这不是配置写错了,而是对 sccache 能缓存什么有误解。它有两条写死的硬限制------增量编译的 crate 不能缓存、调用系统链接器的 crate 不能缓存 ------这两条直接决定了:sccache 在 Rust 项目里省下的,几乎全是第三方依赖那一层的编译时间,而不是你自己天天改的业务 crate。想让它真的发挥作用,尤其是在 Windows + MSVC 这种坑更多的环境里,得先搞清楚它到底怎么工作、边界在哪。
这篇文章把 sccache 的缓存原理、两条硬限制的成因,以及 Windows/MSVC runner 上的正确接入方式讲清楚,最后顺带回答一个小问题:sccache 这名字到底什么意思。
sccache 是什么:一个装在编译器前面的缓存层
sccache 的全称是 Shared Compilation Cache (共享编译缓存),出自 Mozilla 官方仓库的项目标题。它是 ccache 的精神续作------官方把自己定义为「a ccache-like compiler caching tool」,即「类 ccache 的编译器缓存工具」。
名字的构成也就说清了它和前辈的关系:ccache 是经典的本地 C/C++ 编译缓存,sccache 在前面加了个 s(Shared) ,核心增量就是「共享」------缓存不再只躺在本机磁盘,还能放到 S3、GCS、Redis 这类云后端,让多台机器、多个 CI runner 共享同一份缓存,并支持分布式编译。这正是它诞生的场景:Mozilla 为加速 Firefox 这类超大型项目的 CI 而造,共享缓存是大规模持续集成最实际的诉求。
需要说明:官方只给出「Shared Compilation Cache」这个展开和「ccache-like」的定位,并没有逐字母拆解每个字符的官方对照表。上面把 s 对应 Shared、cache 承接 ccache,是基于这层关系的合理解读。
缓存原理:把「编译输入」哈希成 key
sccache 的工作方式是编译器包装器(compiler wrapper)加本地 client-server 缓存。整个链路是这样的:
拆开看几个关键点:
一、拦截编译调用。 通过 RUSTC_WRAPPER 环境变量,cargo 把原本要交给 rustc 的每次编译调用,先交给 sccache。sccache 决定是走缓存还是真编译。
二、对输入做哈希得到 key。 sccache 把一次编译的全部「输入」哈希成一个缓存 key,包括源文件内容、编译参数、编译器版本、相关依赖等。key 命中就直接返回之前缓存的产物(.o / .rlib),完全跳过 rustc;未命中才真正编译,并把结果写进缓存。
这里有个 Windows 上特别容易踩的细节:绝对路径必须一致才能命中 。sccache 把源文件的绝对路径也算进哈希,如果每次 CI checkout 到不同目录(比如带 build id 的临时路径),缓存会大面积 miss。解决办法是设 SCCACHE_BASEDIRS 做路径归一化,把公共前缀在哈希前剥掉------注意它只接受绝对路径,填相对路径会导致 server 起不来。
三、client-server 常驻。 sccache 的 server 在本机监听(默认 127.0.0.1:4226),把统计和状态放在内存里,避免每次冷启动。它按需自动拉起,闲置约 10 分钟后自动退出。
四、多级缓存后端。 默认存本地磁盘;也可以配 S3 / GCS / Redis 等共享后端,支持多级层次缓存与自动回填。共享后端正是它比 ccache 强的地方,也是短命 CI runner 场景的价值所在------机器销毁了,缓存还在云上。
两条硬限制:决定了它对 Rust 到底能省多少
这是全文最该记住的部分。sccache 对 C/C++ 缓存效果拔群,但用在 Rust 上,有两条官方明确的限制会大幅缩小它的作用范围。
| 限制 | 后果 |
|---|---|
| 增量编译的 crate 不能缓存 | Cargo 对 debug profile 的 workspace 成员和 path 依赖默认开启增量编译,这些 crate 根本进不了缓存 |
| 调用系统链接器的 crate 不能缓存 | bin、dylib、cdylib、proc-macro 这几类 crate 缓存不了 |
第一条尤其致命。Cargo 默认对你自己的 workspace 成员和本地 path 依赖开增量编译,而增量编译的产物 sccache 存不了。也就是说,你天天改的那些业务 crate,在默认配置下用了 sccache 也等于没用。要让它们进缓存,必须显式关掉增量编译:
bash
export CARGO_INCREMENTAL=0
第二条则意味着,最终产物那个 bin(可执行文件)、以及 cdylib、proc-macro 宏 crate,都无法缓存。
两条叠加,结论就清晰了:sccache 对 Rust 的真正价值,是缓存那棵庞大的第三方依赖树 。这些依赖绝大多数是普通 lib crate,不增量、不直接链接,正好落在可缓存区间;而它们往往又是一次干净构建里耗时最长的部分。所以别指望「全量缓存」,要把预期放在「依赖层提速」上------对依赖树巨大的项目,这一块的收益依然相当可观。
Windows / MSVC runner 上的正确接入
原理和限制讲完,落到 Windows 就是几个具体动作。顺序很重要。
第一步,关掉增量编译。 否则如上所述,sccache 对本地 crate 完全失效:
text
CARGO_INCREMENTAL=0
第二步,挂上 wrapper。 CI 环境推荐用环境变量,而不是写死进仓库里的 .cargo/config.toml------这样不污染仓库、也不影响其他人的本地开发行为:
text
RUSTC_WRAPPER=sccache
也可以用绝对路径 RUSTC_WRAPPER=C:\path\to\sccache.exe。如果确实要走 cargo config,正确的键名是 [build] 段下的 rustc-wrapper(需要 cargo 1.40 或更新版本),别拼错成 rustc-wraper。
第三步,处理 MSVC 的调试信息格式。 MSVC 默认用 /Zi 生成独立的 PDB 文件,而 sccache 需要内嵌调试信息才能可靠缓存。纯 Rust 项目一般不用操心;但如果项目里有走 cmake 构建的 C/C++ 依赖,就要按 cmake 版本处理:
- cmake 3.25 及以上:
-DCMAKE_MSVC_DEBUG_INFORMATION_FORMAT=Embedded -DCMAKE_POLICY_CMP0141=NEW - cmake 3.24 及以下:改用
/Z7(或/Zi配合每个目标文件独立的/Fd)
第四步,别让缓存故障拖垮构建。 缓存后端偶发 I/O 错误时,默认行为可能让整个 CI 失败。加上这个开关,让它在出错时优雅回退到直接调用编译器:
text
SCCACHE_IGNORE_SERVER_IO_ERROR=1
第五步,验证命中率。 构建结束后跑一下统计,别凭感觉判断有没有生效:
bash
sccache --show-stats
如果要排查问题,可以开日志:SCCACHE_LOG=debug 配合 SCCACHE_ERROR_LOG=<file>。
macOS 与 Windows 保持一套配置
sccache 本身是跨平台一致的,上面这套环境变量在 macOS 和 Windows runner 上通用,差异只在缓存目录或共享后端的配置。所以如果你的项目要同时覆盖两个平台,建议在 CI job 的环境层统一设 RUSTC_WRAPPER、CARGO_INCREMENTAL=0、SCCACHE_IGNORE_SERVER_IO_ERROR=1,而不是在仓库里写平台分支。共享后端如果接 S3,凭据走 CI 的 secret 变量,别提交进仓库。
一个诚实的预期收敛:因为顶层可执行文件、proc-macro 宏 crate 和增量的 workspace crate 都缓存不了,sccache 帮你省的主要是第三方依赖的编译。这在多数 Rust CI 里仍是占比最大的一块,但它不是银弹------理解它省在哪、不省在哪,比盲目挂上去更重要。
小结
- sccache = Shared Compilation Cache,是 ccache 的「共享版」,靠对编译输入哈希成 key 来命中缓存、跳过重复编译。
- 用在 Rust 上有两条硬限制:增量编译的 crate 不能缓存、链接类(bin/dylib/cdylib/proc-macro)crate 不能缓存 。所以它主要给第三方依赖层提速,不要期待全量缓存。
- 正确接入的关键动作:
CARGO_INCREMENTAL=0关增量、RUSTC_WRAPPER=sccache挂 wrapper、MSVC 用内嵌调试信息、SCCACHE_IGNORE_SERVER_IO_ERROR=1兜底、sccache --show-stats验证。 - Windows 上格外注意绝对路径一致性 (必要时用
SCCACHE_BASEDIRS归一化),macOS 与 Windows 用同一套环境变量即可。
参考与数据源
- Mozilla sccache 官方仓库与文档:github.com/mozilla/scc...
- ccache 官方站(sccache 的设计原型):ccache.dev/