Rust で線形代数ライブラリを作成しています。
特定の行と列で行列セルへの参照を取得する関数があります。この関数は、行と列が境界内にあるという 1 組のアサーションで始まります。
#[inline(always)]
pub fn get(&self, row: usize, col: usize) -> &T {
assert!(col < self.num_cols.as_nat());
assert!(row < self.num_rows.as_nat());
unsafe {
self.get_unchecked(row, col)
}
}
タイトなループでは、境界チェックをスキップする方が速いかもしれないと考えたので、get_unchecked
方法を提供します。
#[inline(always)]
pub unsafe fn get_unchecked(&self, row: usize, col: usize) -> &T {
self.data.get_unchecked(self.row_col_index(row, col))
}
奇妙なことに、これらのメソッドを使用して (行と列の反復子を介して) 行列の乗算を実装すると、境界をチェックすると、実際には約 33% 高速になることがベンチマークで示されます。なぜこうなった?
Linux と OSX を実行している 2 つの異なるコンピューターでこれを試しましたが、どちらも効果を示しています。
完全なコードはgithub にあります。関連ファイルはlib.rsです。対象となる機能は次のとおりです。
get
68行目get_unchecked
81行目next
551行目mul
796行目matrix_mul
(ベンチマーク)1038行目
型レベルの数値を使用して行列をパラメーター化していることに注意してください (ダミーのタグ付き型を介した動的サイズのオプションもあります)。そのため、ベンチマークは 2 つの 100x100 行列を乗算しています。
アップデート:
ベンチマークで直接使用されていないものを削除し、一般的なパラメーターを削除して、コードを大幅に簡素化しました。イテレータを使用しない乗算の実装も作成しましたが、そのバージョンは同じ効果を引き起こしません。このバージョンのコードについては、こちらを参照してください。minimal-performance
ブランチを複製して実行cargo bench
すると、乗算の 2 つの異なる実装のベンチマークが実行されます (そのブランチで開始するためにアサーションがコメント アウトされていることに注意してください)。
get*
また、参照の代わりにデータのコピーを返すように関数を変更すると ( のf64
代わりに)、効果がなくなることにも注意してください&f64
(ただし、コードは全体的にわずかに遅くなります)。