编写基准测试#
基准是一种衡量代码性能的方法。它们可以用于比较不同的实现质量,或跟踪随时间变化的性能。
使用测试块进行基准测试#
对一个函数进行基准测试的最简单的方法是使用带有 @bench.T
参数的测试块。它有一个方法 @bench.T::bench
,该方法接受一个类型为 () -> Unit
的函数,并自动确定合适的迭代次数然后运行该函数多次。测量和统计分析将在 moon
中进行,并在控制台输出中显示。
fn fib(n : Int) -> Int {
if n < 2 {
return n
}
return fib(n - 1) + fib(n - 2)
}
test (b : @bench.T) {
b.bench(fn() { b.keep(fib(20)) })
}
输出如下所示(仅供参考):
time (mean ± σ) range (min … max)
21.67 µs ± 0.54 µs 21.28 µs … 23.14 µs in 10 × 4619 runs
该函数执行了 10 × 4619
次。第二个数字是由基准工具自动确定的。它会增加迭代次数,直到测量时间足够长,以获得准确的计时。第一个数字可以通过向 @bench.T::bench
传递命名参数 count
来调整。
test (b : @bench.T) {
b.bench(fn() { b.keep(fib(20)) }, count=20)
}
@bench.T::keep
是一个重要的辅助函数,它可以防止你的计算被优化掉并完全跳过。如果你正在基准测试一个纯函数,请确保使用这个函数以避免潜在的优化。然而,编译器仍然有可能提前计算并将计算替换为常量。
批量基准测试#
基准测试的一个常见场景是比较同一函数的两个或多个实现。在这种情况下,你可能想要在一个块中批量测试它们,以便于比较。 @bench.T::bench
方法的 name
参数可以用来标识测试块内的基准测试。
fn fast_fib(n : Int) -> Int {
if n < 2 {
return n
} else {
let mut a = 0
let mut b = 1
for i = 2; i <= n; i = i + 1 {
let t = a + b
a = b
b = t
}
b
}
}
test (b : @bench.T) {
b.bench(name="naive_fib", fn() { b.keep(fib(20)) })
b.bench(name="fast_fib", fn() { b.keep(fast_fib(20)) })
}
现在你可以通过查看输出,来评估哪个实现更快:
name time (mean ± σ) range (min … max)
naive_fib 21.01 µs ± 0.21 µs 20.76 µs … 21.32 µs in 10 × 4632 runs
fast_fib 0.02 µs ± 0.00 µs 0.02 µs … 0.02 µs in 10 × 100000 runs
基准统计的原始数据#
有时用户可能希望获取原始基准统计数据以进行进一步的分析。有一个函数 @bench.single_bench
返回一个抽象的 Summary
类型,用户可以将其序列化为 JSON 格式。我们不保证 Summary
类型内部字段的稳定性。
在使用 @bench.single_bench
时,用户必须确保计算没有被优化掉。此时没有独立的 keep
函数可用;之前介绍过的 keep
是 @bench.T
的一个方法。
fn collect_bench() -> Unit {
let mut saved = 0
let summary : @bench.Summary = @bench.single_bench(name="fib", fn() {
saved = fib(20)
})
println(saved)
println(summary.to_json().stringify())
}
输出可能如下所示:
6765
{
"name": "fib",
"sum": 217.22039973878972,
"min": 21.62009230518067,
"max": 21.87286402916848,
"mean": 21.72203997387897,
"median": 21.70412048323901,
"var": 0.007197724461032505,
"std_dev": 0.08483940394081341,
"std_dev_pct": 0.39056830777787843,
"median_abs_dev": 0.08189815918589166,
"median_abs_dev_pct": 0.3773392211360855,
"quartiles": [
21.669052078798433,
21.70412048323901,
21.76141434479756
],
"iqr": 0.09236226599912811,
"batch_size": 4594,
"runs": 10
}
时间单位均为微秒。