コードをベクトル化するのは良い考えですか? いつそれを行うべきかという点で、どのような良い習慣がありますか? 下はどうなるの?
5 に答える
ベクトル化とは、独立した命令を 1 つのSIMD命令として実行できることをコンパイラーが検出することを意味します。通常の例は、次のようなことをすると
for(i=0; i<N; i++){
a[i] = a[i] + b[i];
}
としてベクトル化されます (ベクトル表記を使用)
for (i=0; i<(N-N%VF); i+=VF){
a[i:i+VF] = a[i:i+VF] + b[i:i+VF];
}
基本的に、コンパイラは、配列の VF 要素に対して同時に実行できる 1 つの操作を選択し、単一の操作を N 回実行する代わりに、この N/VF 回実行します。
パフォーマンスは向上しますが、アーキテクチャの要件が増えます。
前述のように、ベクトル化は SIMD 命令を利用するために使用され、大きなレジスターにパックされた異なるデータに対して同一の操作を実行できます。
コンパイラーがループを自動ベクトル化できるようにするための一般的なガイドラインは、ループのさまざまな反復で、データ要素のフロー依存性と逆依存性がないようにすることです。
http://en.wikipedia.org/wiki/Data_dependency
インテル C++/Fortran コンパイラーなどの一部のコンパイラーは、コードを自動ベクトル化できます。ループをベクトル化できなかった場合、インテル® コンパイラーはベクトル化できなかった理由を報告できます。ベクトル化可能になるようにコードを変更するために使用できるレポートがあります (可能であると仮定します)。
依存関係については、書籍「Optimizing Compilers for Modern Architectures: A Dependence-based Approach」で詳しく説明されています。
ベクトル化は、大きなデータを保持できる単一のレジスタに限定する必要はありません。「128」ビットレジスタを使用して「4x32」ビットデータを保持するのと同じです。アーキテクチャの制限によって異なります。一部のアーキテクチャには、独自のレジスタを持つ異なる実行ユニットがあります。その場合、データの一部をその実行ユニットに供給し、その実行ユニットに対応するレジスタから結果を取得することができます。
たとえば、以下の場合を考えてみましょう。
for(i = 0; i <N; i ++)
{
a [i] = a [i] + b [i];
}
2つの実行ユニットを持つアーキテクチャで作業している場合、ベクトルサイズは2として定義されます。上記のループは次のようにリフレームされます
for(i = 0; i <(N / 2); i + = 2)
{
a [i] = a [i] + b [i];
a [i + 1] = a [i + 1] + b [i + 1];
}注:forステートメント内の2は、ベクトルサイズから導出されます。
私は2つの実行ユニットを持っているので、ループ内の2つのステートメントが2つの実行ユニットに送られます。合計は、実行ユニットに個別に累積されます。最後に、(2つの実行ユニットからの)累積値の合計が実行されます。
グッドプラクティスは
1です。ループをベクトル化する前に、依存関係(ループの異なる反復間の)などの制約をチェックする必要があります。
2.関数呼び出しを防ぐ必要があります。
3.ポインターアクセスはエイリアシングを作成する可能性があるため、防止する必要があります。
SSEコード生成です。
フロート マトリックス コードを含むループがあり、matrix1[i][j] + matrix2[i][j] があり、コンパイラは SSE コードを生成します。