鍍金池/ 教程/ Java/ 基準(zhǔn)測試
哲學(xué)家就餐問題
鏈接進(jìn)階
名詞中英文對照
測試
引用和借用
泛型
方法語法
函數(shù)
不安全代碼
并發(fā)
裝箱語法和模式
注釋
棧和堆
運算符與重載
語法索引
文檔
固有功能
所有權(quán)
循環(huán)
通用函數(shù)調(diào)用語法
不定長類型
<code>const</code> 和 <code>static</code>
迭代器
其他語言中的 Rust
枚舉
詞匯表
If語句
猜猜看
錯誤處理
生命周期
編譯器插件
發(fā)布途徑
閉包
trait 對象
不使用標(biāo)準(zhǔn)庫
關(guān)聯(lián)常量
外部函數(shù)接口(FFI)
類型轉(zhuǎn)換
原生類型
匹配
參考文獻(xiàn)
Rust 編程語言
內(nèi)聯(lián)匯編
條件編譯
選擇你的保證
學(xué)習(xí) Rust
`type`別名
自定義內(nèi)存分配器
屬性
if let
高效 Rust
可變性
語法和語義
模式
基準(zhǔn)測試
結(jié)構(gòu)體
變量綁定
語言項
切片模式
<code>Deref</code> 強制多態(tài)
關(guān)聯(lián)類型
裸指針
<code>Borrow</code> 和 <code>AsRef</code>
準(zhǔn)備
Rust 開發(fā)版
字符串

基準(zhǔn)測試

benchmark-tests.md
commit 024aa9a345e92aa1926517c4d9b16bd83e74c10d

Rust 也支持基準(zhǔn)測試,它可以測試代碼的性能。讓我們把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,它啟用了這個不穩(wěn)定功能。

我們導(dǎo)入了testcrate,它包含了對基準(zhǔn)測試的支持。我們也定義了一個新函數(shù),帶有bench屬性。與一般的不帶參數(shù)的測試不同,基準(zhǔn)測試有一個&mut Bencher參數(shù)。Bencher提供了一個iter方法,它接收一個閉包。這個閉包包含我們想要測試的代碼。

我們可以用cargo bench來運行基準(zhǔn)測試:

$ 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)測試將被忽略。你也許會發(fā)現(xiàn)cargo benchcargo test花費的時間更長。這是因為Rust會多次運行我們的基準(zhǔn)測試,然后取得平均值。因為我們的函數(shù)只做了非常少的操作,我們耗費了1 ns/iter (+/- 0),不過運行時間更長的測試就會有出現(xiàn)偏差。

編寫基準(zhǔn)測試的建議:

  • 把初始代碼放于iter循環(huán)之外,只把你想要測試的部分放入它
  • 確保每次循環(huán)都做了“同樣的事情”,不要累加或者改變狀態(tài)
  • 確保外邊的函數(shù)也是冪等的(idempotent),基準(zhǔn)測試runner可能會多次運行它
  • 確保iter循環(huán)內(nèi)簡短而快速,這樣基準(zhǔn)測試會運行的很快同時校準(zhǔn)器可以在合適的分辨率上調(diào)整運轉(zhuǎn)周期
  • 確保iter循環(huán)執(zhí)行簡單的工作,這樣可以幫助我們準(zhǔn)確的定位性能優(yōu)化(或不足)

Gocha:優(yōu)化

寫基準(zhǔn)測試有另一些比較微妙的地方:開啟了優(yōu)化編譯的基準(zhǔn)測試可能被優(yōu)化器戲劇性的修改導(dǎo)致它不再是我們期望的基準(zhǔn)測試了。舉例來說,編譯器可能認(rèn)為一些計算并無外部影響并且整個移除它們。

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

得到如下結(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

基準(zhǔn)測試運行器提供兩種方法來避免這個問題:要么傳遞給iter的閉包可以返回一個隨機的值這樣強制優(yōu)化器認(rèn)為結(jié)果有用并確保它不會移除整個計算部分。這可以通過修改上面例子中的b.iter調(diào)用:

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

要么,另一個選擇是調(diào)用通用的test::black_box函數(shù),它會傳遞給優(yōu)化器一個不透明的“黑盒”這樣強制它考慮任何它接收到的參數(shù)。

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

執(zhí)行上面任何一種修改可以獲得如下基準(zhǔn)測試結(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)化器還是可能在不合適的情況下修改測試用例。

上一篇:條件編譯下一篇:裸指針