基準(zhǔn)測(cè)試

2018-08-12 22:03 更新

基準(zhǔn)測(cè)試

Rust支持基準(zhǔn)測(cè)試來(lái)測(cè)試用戶代碼的性能。我們來(lái)看一下 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 的功能聲明,這表示這并不是一個(gè)穩(wěn)定的功能。

用戶需要引入 test 的封裝,來(lái)使得基準(zhǔn)測(cè)試得以支持。通過(guò) bench 屬性的使用,我們可以實(shí)現(xiàn)一個(gè)新的函數(shù)。與不能有參數(shù)的常規(guī)測(cè)試不同,這里的基準(zhǔn)測(cè)試使用 &mut Bencher 來(lái)改善這個(gè)情況。這里的 Bencher 提供了一個(gè)閉包的 iter 方法。這個(gè)閉包就包含了需要進(jìn)行基準(zhǔn)測(cè)試的代碼。

用戶通過(guò) cargo bench 指令來(lái)執(zhí)行基準(zhǔn)測(cè)試:

$ 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

我們的非基準(zhǔn)測(cè)試可以被忽略掉。用戶也許會(huì)注意到 cargo benchcargo test 長(zhǎng)一個(gè)比特。這是因?yàn)?Rust 會(huì)多次執(zhí)行基準(zhǔn)測(cè)試,然后會(huì)取一個(gè)平均值。因?yàn)樵谶@個(gè)例子中,只是實(shí)現(xiàn)了一些簡(jiǎn)單的功能,所以我們有一個(gè) ns/iter (+/- 0),但是這會(huì)展示出結(jié)果的方差。

如下是編寫基準(zhǔn)測(cè)試的建議:

  • 將安裝代碼移到 iter 循環(huán)之外,只是將用戶希望進(jìn)行測(cè)試的代碼放到 iter 循環(huán)內(nèi)。
  • 將實(shí)現(xiàn)同一功能的代碼放到迭代體內(nèi),不要將結(jié)果進(jìn)行累計(jì),也不要改變狀態(tài)。
  • 將外部函數(shù)進(jìn)行冪等化,基準(zhǔn)測(cè)試程序會(huì)將它測(cè)試多次。
  • 盡量使 iter 循環(huán)體內(nèi)容的代碼更精簡(jiǎn),然后使得基準(zhǔn)測(cè)試運(yùn)行起來(lái)更快捷,使得校準(zhǔn)器可以以更高精度的來(lái)校準(zhǔn)。
  • 使 iter 循環(huán)體內(nèi)的代碼更簡(jiǎn)單,以幫助查明性能改進(jìn)的地方(或回歸)。

疑難雜癥:優(yōu)化

在基準(zhǔn)測(cè)試程序的編寫方面還存在一些辣手的問(wèn)題:優(yōu)化編譯后的基準(zhǔn)測(cè)試代碼可能會(huì)被優(yōu)化器篡改,這樣使得基準(zhǔn)測(cè)試可能就不再是用戶希望的測(cè)試對(duì)象了。比如,編譯器可能會(huì)重新組織一些代碼,因?yàn)檫@些代碼并沒(méi)有什么作用,甚至?xí)⑺縿h除。

#![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);
});
}

上述代碼會(huì)出現(xiàn)下述結(jié)果:

running 1 test
test bench_xor_1000_ints ... bench: 0 ns/iter (+/- 0)

test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured

為了避免上述問(wèn)題,基準(zhǔn)測(cè)試的執(zhí)行者會(huì)采用兩個(gè)方法。iter 方法可以獲得一個(gè)閉包,它可以返回一個(gè)任意值來(lái)迫使優(yōu)化器考慮這個(gè)結(jié)果,來(lái)保證優(yōu)化器不會(huì)更改基準(zhǔn)測(cè)試代碼。下面的例子就是來(lái)展示如何校準(zhǔn) b.iter.

`b.iter(|| {  
// note lack of `;` (could also use an explicit `return`).
(0..1000).fold(0, |old, new| old ^ new)
});`

另一種方法是通用的 test::black_box 函數(shù),它對(duì)優(yōu)化器就是一個(gè)“黑盒”,可以迫使優(yōu)化器考慮更多的參數(shù)。

#![feature(test)]

extern crate test;

b.iter(|| {
let n = test::black_box(1000);

(0..n).fold(0, |a, b| a ^ b)
})

這些并不會(huì)讀取和更改這個(gè)值。較大的值可以直接降低開銷。

`black_box(&huge_struct)`

執(zhí)行上述任何一種變化提供了以下基準(zhǔn)測(cè)試結(jié)果。

running 1 test
test bench_xor_1000_ints ... bench:   131 ns/iter (+/- 3)

test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured

即使使用上述方式,優(yōu)化器仍然會(huì)以不可想象的方式來(lái)修改測(cè)試用例。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)