外观
第 8 节:测量与基准——先测,再优化
症状
"我觉得这里慢"——然后花一下午改代码,结果程序没变快。性能优化最大的浪费,是不测量就动手。
诊断
性能工作的正确顺序(全篇总纲):
- 测:找到真正的热点(90% 的时间花在哪)
- 改:优化热点
- 再测:确认真的快了(并且测试还绿)
不测量就优化,等于蒙着眼睛开枪——你可能在优化一个只占 1% 时间的函数,而真正的热点在旁边睡觉。
解药一:最轻量的计时——Instant ⭐
内置 API 篇第 7 节的 Instant,做"粗基准"完全够用:
rust
use std::time::Instant;
let start = Instant::now();
// ……要测的代码……
let elapsed = start.elapsed();
println!("耗时:{:?}", elapsed);注意:一次运行受系统干扰(其他程序抢 CPU),多跑几次、看趋势;想稳定,循环多次取最短/平均。
解药二:黑盒计时——release + 外部工具 ⭐
程序级测量,用系统工具(比 println 更真实):
bash
cargo build --release
# Windows
Measure-Command { .\target\release\perf_test.exe }
# Linux/Mac
time ./target/release/perf_test永远用 release 测(第 4 节:dev 差 20 倍,测出来全是假象)。
解药三:认真的基准——criterion ✨
要"权威"的基准报告,用 criterion:
bash
cargo add --dev criterionrust
use criterion::{criterion_group, criterion_main, Criterion};
fn bench_filter(c: &mut Criterion) {
let numbers: Vec<u32> = (0..1_000_000).collect();
c.bench_function("filter_sum", |b| {
b.iter(|| {
let sum: u64 = numbers.iter()
.filter(|n| *n % 2 == 0)
.map(|&n| n as u64)
.sum();
std::hint::black_box(sum); // 防止编译器把结果优化掉
});
});
}
criterion_group!(benches, bench_filter);
criterion_main!(benches);cargo bench 会给出置信区间、对比上一轮的变化——比手写 Instant 严谨得多(还自动多跑几轮)。
预防
- 三个"先":先测、先测、先测——热点没找到之前,优化都是猜
- 改一处,测一处:一次只改一个变量,才知道哪个改动生效
- 回归测试兜底:优化完跑
cargo test,快的前提是"结果还对"(性能篇总纲第 3 条) - 95/95 法则:真实项目 95% 的代码是"冷路径",优化它们毫无意义;热路径通常只有 5%——找到那 5%
- 性能 vs 可读性:可读代码通常够快;只有实测证明热点,才为性能牺牲可读性(并加注释说明为什么)