Rust 也支持基准测试,它可以测试代码的性能。让我们把src/lib.rs修改成这样(省略注释):
src/lib.rs
#![feature(test)] extern crate test; pub fn add_two(a: i32) -> i32 { a + 2 } #[cfg(test)] mod tests { use super::*; use test::Bencher; #[test] fn it_works() { assert_eq!(4, add_two(2)); } #[bench] fn bench_add_two(b: &mut Bencher) { b.iter(|| add_two(2)); } }
注意test功能 gate,它启用了这个不稳定功能。
test
我们导入了testcrate,它包含了对基准测试的支持。我们也定义了一个新函数,带有bench属性。与一般的不带参数的测试不同,基准测试有一个&mut Bencher参数。Bencher提供了一个iter方法,它接收一个闭包。这个闭包包含我们想要测试的代码。
bench
&mut Bencher
Bencher
iter
我们可以用cargo bench来运行基准测试:
cargo bench
$ cargo bench Compiling adder v0.0.1 (file:///home/steve/tmp/adder) Running target/release/adder-91b3e234d4ed382a running 2 tests test tests::it_works ... ignored test tests::bench_add_two ... bench: 1 ns/iter (+/- 0) test result: ok. 0 passed; 0 failed; 1 ignored; 1 measured
我们的非基准测试将被忽略。你也许会发现cargo bench比cargo test花费的时间更长。这是因为Rust会多次运行我们的基准测试,然后取得平均值。因为我们的函数只做了非常少的操作,我们耗费了1 ns/iter (+/- 0),不过运行时间更长的测试就会有出现偏差。
cargo test
1 ns/iter (+/- 0)
编写基准测试的建议:
写基准测试有另一些比较微妙的地方:开启了优化编译的基准测试可能被优化器戏剧性的修改导致它不再是我们期望的基准测试了。举例来说,编译器可能认为一些计算并无外部影响并且整个移除它们。
#![feature(test)] extern crate test; use test::Bencher; #[bench] fn bench_xor_1000_ints(b: &mut Bencher) { b.iter(|| { (0..1000).fold(0, |old, new| old ^ new); }); }
得到如下结果:
running 1 test test bench_xor_1000_ints ... bench: 0 ns/iter (+/- 0) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
基准测试运行器提供两种方法来避免这个问题:要么传递给iter的闭包可以返回一个随机的值这样强制优化器认为结果有用并确保它不会移除整个计算部分。这可以通过修改上面例子中的b.iter调用:
b.iter
# struct X; # impl X { fn iter<T, F>(&self, _: F) where F: FnMut() -> T {} } let b = X; b.iter(|| { // note lack of `;` (could also use an explicit `return`). (0..1000).fold(0, |old, new| old ^ new) });
要么,另一个选择是调用通用的test::black_box函数,它会传递给优化器一个不透明的“黑盒”这样强制它考虑任何它接收到的参数。
test::black_box
#![feature(test)] extern crate test; # fn main() { # struct X; # impl X { fn iter<T, F>(&self, _: F) where F: FnMut() -> T {} } let b = X; b.iter(|| { let n = test::black_box(1000); (0..n).fold(0, |a, b| a ^ b) }) # }
上述两种方法均未读取或修改值,并且对于小的值来说非常廉价。对于大的只可以通过间接传递来减小额外开销(例如:black_box(&huge_struct))。
black_box(&huge_struct)
执行上面任何一种修改可以获得如下基准测试结果:
running 1 test test bench_xor_1000_ints ... bench: 131 ns/iter (+/- 3) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
然而,即使使用了上述方法优化器还是可能在不合适的情况下修改测试用例。
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8