Rust 學習筆記:循環和迭代器的性能比較
- Rust 學習筆記:循環和迭代器的性能比較
- 示例 1
- 示例 2
- 總結
Rust 學習筆記:循環和迭代器的性能比較
示例 1
我們運行一個基準測試,將《福爾摩斯探案集》的全部內容加載到一個字符串中,并在內容中查找單詞 the。下面是使用 for 循環的搜索版本和使用迭代器的版本的基準測試結果:
test bench_search_for ... bench: 19,620,300 ns/iter (+/- 915,700)
test bench_search_iter ... bench: 19,234,900 ns/iter (+/- 657,200)
使用迭代器的版本略優于使用循環的版本。
迭代器雖然是高級抽象,但編譯后的代碼與自己編寫的底層代碼大致相同。
迭代器是 Rust 的零成本抽象之一,意思是使用抽象不會帶來額外的運行時開銷。
示例 2
以下代碼取自音頻解碼器。解碼算法使用線性預測數學運算來估計基于前一個樣本的線性函數的未來值。這段代碼使用一個迭代器鏈對作用域中的三個變量進行一些計算:一個數據的緩沖片、一個包含12個系數的數組,以及要在qlp_shift中移動數據的量。我們在這個例子中聲明了變量,但沒有給它們任何值;盡管這段代碼在其上下文之外沒有太多意義,但它仍然是Rust如何將高級思想轉換為低級代碼的一個簡明的真實示例。
let buffer: &mut [i32];
let coefficients: [i64; 12];
let qlp_shift: i16;for i in 12..buffer.len() {let prediction = coefficients.iter().zip(&buffer[i - 12..i]).map(|(&c, &s)| c * s as i64).sum::<i64>() >> qlp_shift;let delta = buffer[i];buffer[i] = prediction as i32 + delta;
}
為了計算預測值,這段代碼遍歷系數中的 12 個值,并使用 zip 方法將系數值與緩沖區中的前 12 個值配對。然后,對于每一對,我們將這些值相乘,對所有結果求和,并將總和 qlp_shift 位中的位向右移動。
音頻解碼器等應用程序中的計算通常最優先考慮性能。這里,我們使用兩個適配器創建一個迭代器,然后消費該值。這個 Rust 代碼編譯成什么匯編代碼?
沒有任何循環對應于對系數值的迭代:Rust 知道有 12 次迭代,所以它“展開”循環。展開是一種優化,它消除了循環控制代碼的開銷,而是為循環的每次迭代生成重復的代碼。
所有的系數都存儲在寄存器中,這意味著訪問值非常快。在運行時對數組訪問沒有邊界檢查。Rust 能夠應用的所有這些優化使生成的代碼非常高效。
現在知道了這一點,就可以毫無畏懼地使用迭代器和閉包了!它們使代碼看起來更高級,但不會因此造成運行時性能損失。
總結
閉包和迭代器是受函數式編程語言思想啟發的 Rust 特性。它們有助于 Rust 以低級性能清晰地表達高級思想的能力。
閉包和迭代器的實現不影響運行時性能。這是 Rust 努力提供零成本抽象目標的一部分。