如何比较两组 bench 结果?
解读
在国内 Rust 岗位面试中,面试官提出“比较两组 bench 结果”并不是让你背诵命令,而是考察能否用工程化手段把性能差异量化、可复现、可审计。候选人常犯三类错误:
- 只报“快 15 %”却不给置信区间;
- 忽略环境扰动(CPU 绑核、频率、NUMA、后台进程);
- 不会把结论固化到 CI,导致回归无人知晓。
因此,回答必须体现统计严谨性 + Rust 工具链熟练度 + 工程闭环。
知识点
- 统计显著性:中心极限定理、Welch’s t-test、置信区间 95 %。
- Rust 工具链:criterion 的
cargo bench与cargo criterion --message-format=json输出;critcmp一键比较;iai用于指令级精确计数。 - 环境控制:
cpufreq-set -g performance、taskset -c 2-3、echo 1 > /proc/sys/kernel/nmi_watchdog、关闭超线程、禁用 ASLR。 - 可复现性:
Cargo.lock、toolchain 固定到stable-1.78.0、rust-toolchain.toml、Docker--cpus=2 --cpuset-cpus=2,3。 - CI 集成:GitHub Actions 缓存
target/criterion、上传criterion.zip为产物,PR 评论自动贴性能差异报告。 - 业务解释:差异来源是算法复杂度降低、分支预测改善,还是内存布局优化(如
#[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 自动失败,并 @ 代码所有者。
拓展思考
- 微架构敏感场景:若代码将在多代服务器混布,需加跑
cargo bench --features=portable并在RUSTFLAGS里去掉target-cpu=native,比较“最低公分母”性能,防止线上劣化。 - 内存分配差异:当 bench 涉及大量短生命周期对象时,换用
mimalloc或jemalloc会导致 10 % 以上波动,应在报告中显式注明全局分配器版本。 - 异步代码 bench:
criterion默认测量同步闭包,对async fn需用futures::executor::block_on或tokio::runtime::Builder::current_thread,并确认无交叉任务干扰;更先进做法是用bencher::AsyncBencher或tokio-rs/tokio-uring提供的bench宏。 - 持续基准平台:国内公司可基于自研开源方案
volo-rs/perf-book搭建内部 PerfHub,把每次 master merge 的 bench 结果写进 ClickHouse,自动生成趋势图,实现“性能资产可审计”。