小任务只省 0.3ms、大任务命中也要 33ms:构建缓存「免费」这条共识的实测复盘
2026/9/19 2:28:38 网站建设 项目流程

Turborepo 30K 星、Nx 29K 星、sccache 7.4K 星——2026 年内容寻址(content-addressed)构建缓存几乎成了 monorepo 的标配。行业文章和工具文档都在说同一件事:缓存命中让重建接近零成本,CI 时间砍掉 60%~80%。但翻遍这些材料,你会发现一个共同的空白:没有人测量过一次「命中」本身到底花了多少时间

命中不是魔法——它要走完「重新哈希全部输入 → 查找/还原产物」这条路径。如果这条路径本身不便宜,那对于某些任务规模,缓存可能只是「看起来快」,实际净收益微乎其微。本文用一次可控的 Node 实测,把冷构建和缓存命中的每一项成本拆开来看。

背景:为什么这件事值得写

内容寻址缓存的机制很简单:对任务的所有输入文件算一个哈希值作为缓存 key,如果 key 命中就直接从缓存读取产物,跳过整个构建步骤。Turborepo、Nx、sccache、Bazel Remote Cache 本质上都是这个模式——区别只在语言生态和分布式执行能力。

2026 年的共识数据很漂亮:Nx 声称大型开源项目比 Turborepo 快 7×,Turborepo 文档称远程缓存可减少 70% 构建时间,sccache 在 Rust 生态中能把编译时间压到三分之一。这些数字说的是有缓存 vs 无缓存的整体对比,但它们隐藏了一个关键问题:即使缓存 100% 命中,你仍然要为每次运行付出哈希输入 + 还原产物的开销。当任务本身很快时,这笔开销可能吃掉大部分甚至全部的加速收益。

解剖:一次缓存命中到底花在哪

我们把一次完整的构建运行拆成四个可独立计时的阶段:

阶段冷构建(未命中)缓存命中
hash 输入 → 生成 key✅ 必须必须重新计算(无法复用上次的 key)
build / transpile / compile✅ 全量执行❌ 跳过
写产物到缓存✅ 必须❌ 跳过
从缓存还原产物❌ 不需要必须执行

注意那个加粗项:缓存命中仍然要重新哈希所有输入。这是因为工具不知道自上次运行以来输入是否变了——它必须重算 key 才能确认是否命中。这意味着 hash 成本是命中路径上的「固定税」,随输入大小线性增长。

图1:100% 堆叠条形图对比。冷构建 801ms 中 build 占 94%(753ms),write 占 3.6%,hash 仅 2.4%;缓存命中 32.8ms 中 hash 占 58.5%(19.2ms),restore 占 40.8%(13.4ms)。命中省掉了 build+write 这两块大头,但 hash 与 restore 仍在。

实证:八个量级的实测

方法

Node v22.22.2 (Win32 x64)上,用zlib.deflateSync(input)作为 CPU 密集型构建代理(真实压缩变换,CPU 开销随输入大小增长),用crypto.createHash('sha256')计算内容寻址 key(与 Turborepo/Nx 一致),用fs.readFileSync从本地磁盘还原缓存产物。对4KB ~ 48MB 共 8 个输入量级分别测量:

  • 冷构建= hash(input) + deflateSync(input) + writeFileSync(artifact)
  • 缓存命中= re-hash(input) + readFileSync(artifact)

每个量级运行 5~9 轮(小量级 9 轮以稳定亚毫秒计时),取中位数。

# 复现命令 cd <项目目录> node bench_cache.js # 输出 bench_result.json

结果

输入量级冷构建 (ms)缓存命中 (ms)加速倍数命中开销占冷构建比
4KB0.330.065.3×19%
16KB0.380.075.5×18.1%
64KB1.160.0912.4×8.1%
256KB4.020.2317.3×5.8%
1MB16.00.6624.4×4.1%
4MB63.12.5025.2×4.0%
16MB252.711.322.4×4.5%
48MB800.832.824.4×4.1%

注:数值均为各轮次中位数,保留一位小数。

图2:加速比从 4KB 的 5.3× 逐步爬升到 ≥1MB 后的 22~25× 并趋于饱和。柱下标注了冷→热毫秒数。折线连接各量级加速比,清晰展示增速放缓的趋势。

三个值得关注的数字:

1.48MB 任务一次命中要 32.8ms。这不是零——其中 hash 花了 19.2ms,restore 花了 13.4ms。在 CI 环境中,32ms 单次看不大,但如果你的 monorepo 有 50 个包、每次 CI 都命中 30 个包,仅还原路径就消耗约 1 秒。如果是远程缓存(网络往返替代本地 readFileSync),restore 可能膨胀到 50~200ms,总命中成本轻松突破 70ms/包。

2.4KB 任务净省仅 0.27ms。冷构建本身就只有 0.33ms(build 0.08ms + write 0.23ms),命中后还要付 0.06ms 的 hash+restore。你确实快了 5 倍,但绝对收益连一帧(16.7ms)都不到。为一个 lint 一个 10 行文件的任务引入整套缓存编排,投入产出比极低。

3.加速比天花板约 25×。≥1MB 后加速比稳定在 22~25× 之间不再上升,因为 warm 路径的 hash+restore 自身也在随输入增大而变重。理论上如果构建是 O(n²) 而 hash 是 O(n),大任务加速比应该持续上升——但在我们的 O(n) 代理模型中,hash 和 restore 都是线性的,所以比值收敛。

图3:缓存命中耗时占冷构建的百分比。前三个量级(≤64KB)的开销占比高达 8%~19%(橙色区域),说明这些小任务的命中「税负」很重;≥1MB 后稳定在 ~4%(绿色),净省 15ms 以上,缓存才真正划算。

局限:哪些事没解决

本次测量有几个明确的边界,结论不应越界推广:

  • 单任务模型:我们测的是「一个构建任务」的冷/热对比,没有模拟 monorepo 中多包依赖图、拓扑排序、并行执行的复杂度。真实 Turborepo/Nx 运行时还有任务调度、依赖分析等固定开销。
  • 本地磁盘缓存:restore 用的是readFileSync(本地 SSD 页缓存)。真实场景中很多团队用远程缓存(Vercel Remote Cache / Nx Cloud / S3),网络延迟会把 restore 从 13ms 推到 50~200ms+,进一步缩小(但不反转)加速收益。
  • 构建代理是 O(n) 压缩:真实 tsc/esbuild/swc 的构建可能是超线性或含 I/O 瓶颈,加速比的绝对值会不同,但「hash+restore 是命中固定税」这一结构性结论不变。
  • 没有测 correctness / 共享 ROI:缓存的价值不仅是速度,还包括跨机器/跨分支共享构建结果。本文只量化了单机单次的 wall-clock 收益。
  • 我们没有发现「缓存比不缓存更慢」的情况:在所有 8 个量级中,warm 均严格快于 cold。本文的论点不是「缓存有害」,而是「缓存不是免费的——它的成本结构决定了小任务场景下收益微乎其微」。

结论与下一步

方法论一句话:缓存那些构建本身够重的任务(≥数百 KB 输入的 typecheck/bundle/transpile),跳过几 KB 级别的小任务缓存——你省下的绝对时间不够抵消编排开销。同时留意命中路径的成本:随着输入增大,hash 和 restore 会从「可忽略」变成「不可忽略」(48MB 已达 33ms),远程缓存环境下更要警惕。

开源地址

  • 矩阵门户:GitHub - wangzifan396-wzf/WB: nano-tools: 1200+ single-file, zero-dependency, local-first web utilities in one portal. Offline and private, nothing leaves your browser. Binary & protocol parsers, crypto, dev, audio, visualization, productivity. · GitHub
  • 单文件工具聚合器:GitHub - wangzifan396-wzf/nano-workbench: Single-file tabbed launcher for the nano-tools matrix - one tab, 382 curated tools (of 1228), instant switching. Zero-dep. Part of nano-tools. · GitHub
  • GitHub 组织主页:wangzifan396-wzf (WangZi) · GitHub

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询