如何比较两组 bench 结果?

解读

在国内 Rust 岗位面试中,面试官提出“比较两组 bench 结果”并不是让你背诵命令,而是考察能否用工程化手段把性能差异量化、可复现、可审计。候选人常犯三类错误:

  1. 只报“快 15 %”却不给置信区间;
  2. 忽略环境扰动(CPU 绑核、频率、NUMA、后台进程);
  3. 不会把结论固化到 CI,导致回归无人知晓。
    因此,回答必须体现统计严谨性 + Rust 工具链熟练度 + 工程闭环

知识点

  1. 统计显著性:中心极限定理、Welch’s t-test、置信区间 95 %。
  2. Rust 工具链:criterion 的 cargo benchcargo criterion --message-format=json 输出;critcmp 一键比较;iai 用于指令级精确计数。
  3. 环境控制cpufreq-set -g performancetaskset -c 2-3echo 1 > /proc/sys/kernel/nmi_watchdog、关闭超线程、禁用 ASLR。
  4. 可复现性Cargo.lock、toolchain 固定到 stable-1.78.0rust-toolchain.toml、Docker --cpus=2 --cpuset-cpus=2,3
  5. CI 集成:GitHub Actions 缓存 target/criterion、上传 criterion.zip 为产物,PR 评论自动贴性能差异报告。
  6. 业务解释:差异来源是算法复杂度降低、分支预测改善,还是内存布局优化(如 #[repr(C)]#[repr(transparent)])。

答案

第一步固化环境:同一台裸金属或同一容器镜像,CPU 频率锁到最高,关闭所有节能与超线程,禁用 Swap。
第二步两次 bench 都使用相同编译器版本与 flags,推荐 RUSTFLAGS="-C target-cpu=native -C opt-level=3",并在 Cargo.toml 里给待测 crate 打开 lto = "thin"
第三步用 criterion 跑至少 100 次采样,命令:

cargo criterion --message-format=json > baseline.json
git checkout feature
cargo criterion --message-format=json > feature.json

第四步统计比较

critcmp baseline.json feature.json

输出示例:

time::parse  -12.3%  (-15.4% … -9.1%)  (p=0.002)

若 p-value < 0.05 且置信区间不包含 0,即可认为差异统计显著
第五步业务验证:用 perf stat -e cache-misses,instructions,cycles 采样,确认性能提升来自指令数减少或缓存命中率提高,而非偶然。
第六步归档:把 target/criterion 整个目录打 tar,连同 critcmp 文本输出一起上传到内部对象存储,供后续回归对比。
第七步CI 门禁:在 .github/workflows/perf.yml 中增加一步,若 critcmp 发现任何接口回归超过 5 % 且 p<0.05,则 PR 自动失败,并 @ 代码所有者。

拓展思考

  1. 微架构敏感场景:若代码将在多代服务器混布,需加跑 cargo bench --features=portable 并在 RUSTFLAGS 里去掉 target-cpu=native,比较“最低公分母”性能,防止线上劣化。
  2. 内存分配差异:当 bench 涉及大量短生命周期对象时,换用 mimallocjemalloc 会导致 10 % 以上波动,应在报告中显式注明全局分配器版本
  3. 异步代码 benchcriterion 默认测量同步闭包,对 async fn 需用 futures::executor::block_ontokio::runtime::Builder::current_thread,并确认无交叉任务干扰;更先进做法是用 bencher::AsyncBenchertokio-rs/tokio-uring 提供的 bench 宏。
  4. 持续基准平台:国内公司可基于自研开源方案 volo-rs/perf-book 搭建内部 PerfHub,把每次 master merge 的 bench 结果写进 ClickHouse,自动生成趋势图,实现“性能资产可审计”。