28

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です。対象となる機能は次のとおりです。

  • get68行目
  • get_unchecked81行目
  • next551行目
  • mul796行目
  • matrix_mul(ベンチマーク)1038行目

型レベルの数値を使用して行列をパラメーター化していることに注意してください (ダミーのタグ付き型を介した動的サイズのオプションもあります)。そのため、ベンチマークは 2 つの 100x100 行列を乗算しています。

アップデート:

ベンチマークで直接使用されていないものを削除し、一般的なパラメーターを削除して、コードを大幅に簡素化しました。イテレータを使用しない乗算の実装も作成しましたが、そのバージョンは同じ効果を引き起こしません。このバージョンのコードについては、こちらを参照してください。minimal-performanceブランチを複製して実行cargo benchすると、乗算の 2 つの異なる実装のベンチマークが実行されます (そのブランチで開始するためにアサーションがコメント アウトされていることに注意してください)。

get*また、参照の代わりにデータのコピーを返すように関数を変更すると ( のf64代わりに)、効果がなくなることにも注意してください&f64(ただし、コードは全体的にわずかに遅くなります)。

4

1 に答える 1