コードをベクトル化するのは良い考えですか?いつそれを行うかという観点からのグッドプラクティスは何ですか?その下で何が起こりますか?
ベクトル化とは、コンパイラが、独立した命令を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
Intel C++/Fortranコンパイラなどの一部のコンパイラは、コードを自動ベクトル化できます。ループをベクトル化できなかった場合、インテル®コンパイラーはそれができなかった理由を報告することができます。レポートを使用して、ベクトル化できるようにコードを変更できます(可能な場合)
依存関係については、「最新のアーキテクチャ向けのコンパイラの最適化:依存関係ベースのアプローチ」という本で詳しく説明されています。
ベクトル化は、大きなデータを保持できる単一のレジスタに限定する必要はありません。 「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。ポインタアクセスはエイリアシングを作成する可能性があるため、防止する必要があります。