インテル® C++ コンパイラー XE 13.1 ユーザー・リファレンス・ガイド

ユーザー指示または SIMD ベクトル化

ユーザー指示または SIMD ベクトル化は、OpenMP* 並列化が自動並列化を補足するように、自動ベクトル化を補足します。下記の図でこの関係を示します。ユーザー指示によるベクトル化は SIMD (Single-Instruction, Multiple-Data) 機能として実装され、SIMD ベクトル化と呼ばれます。

SIMD ベクトル化機能は、インテル製マイクロプロセッサーおよび互換マイクロプロセッサーの両方で利用可能です。ベクトル化により呼び出されるライブラリー・ルーチンは、互換マイクロプロセッサーよりもインテル製マイクロプロセッサーにおいてより優れたパフォーマンスが得られる可能性があります。また、ベクトル化は、/arch/Qx (Windows*) または -m-x (Linux* および OS X*) などの特定のオプションによる影響を受けます。

次の図は、ハードウェアのベクトル化機能を活用するベクトル化コードを生成するためのさまざまなアプローチの中で、SIMD ベクトル化がどこに位置付けされているかを示しています。SIMD ベクトル化を用いて記述されたプログラムは、自動ベクトル化ヒントを使用して記述されたものと似ています。SIMD ベクトル化を使用すると、ベクトル化コードの取得に必要なコードの変更を最小限にすることができます。

SIMD ベクトル化は #pragma simd プラグマを使用してループをベクトル化します。 ループにプラグマを追加して、ループがベクトル化されるように再コンパイルしなければなりません (Windows* の場合は –Qsimd オプションが、Linux* の場合は –simd オプションがデフォルトで有効になります)。

関数 add_floats() が不明なポインターを多く使用しているため、コンパイラーの自動ランタイム独立性チェックにより最適化が行われる C++ の例ついて考えてみます。 #pragma ivdep の自動ベクトル化ヒントを使ってデータ依存性のアサーションを追加し、自動ベクトル化の最適化をループに適用するかどうかコンパイラーに判断させることができます。 または、#pragma simd を使用して、このループのベクトル化を実行することができます。

例: #pragma simd なし

[D:/simd] cat example1.c
void add_floats(float *a, float *b, float *c, float *d, float *e, int n){
 int i;
 for (i=0; i<n; i++){
  a[i] = a[i] + b[i] + c[i] + d[i] + e[i];
 }
}

コマンドライン入力と出力結果

[D:/simd] icl example1.c –nologo -Qvec-report2 
example1.c
D:\simd\example1.c(3): (列 3) リマーク: ループはベクトル化されませんでした: ベクトル依存関係が存在しています。

例: #pragma simd あり

[D:/simd] cat example1.c
void add_floats(float *a, float *b, float *c, float *d, float *e, int n){
 int i;
 #pragma simd
 for (i=0; i<n; i++){
  a[i] = a[i] + b[i] + c[i] + d[i] + e[i];
 }
}

コマンドライン入力と出力結果

[D:/simd] icl example1.c -nologo -Qvec-report2
example1.c
D:\simd\example1.c(4): (列 3) リマーク: "simd" ループがベクトル化されました。

SIMD プラグマと自動ベクトル化ヒントの主な違いは、SIMD プラグマでは、コンパイラーはループをベクトル化できない場合に警告を発行します。 自動ベクトル化ヒントでは、#pragma vector always ヒントを使用した場合でも、実際のベクトル化はコンパイラーの判断にまかせられます。

SIMD プラグマにはオプション節があり、コンパイラーにベクトル化の方法を指示できます。 コンパイラーが正しいベクトル化コードを生成するための十分な情報を得られるように、これらの節を適切に使用してください。節についての詳細は、#pragma simd の説明を参照してください。

追加のセマンティクス

#pragma simd プラグマの使用に関して、次の点に注意してください。

一部の自動ベクトル化が可能なループでは、SIMD プラグマ を使用してベクトル・セマンティクスを表現するのは難しいかもしれません。 例えば、C には MIN/MAX 演算子がないため、C で MIN/MAX リダクションを表現するのは困難です。

vector 宣言の使用

算術関数 sinf() が含まれるループを持つ次の C++ の例ついて考えてみます。 このセクションで示すコード例はすべて Windows* オペレーティング・システムのみが対象です。

例: 算術関数を持つループが自動ベクトル化するケース

[D:/simd] cat example2.c
void vsin(float *restrict a, float *restrict b, int n){
int i;
for (i=0; i<n; i++) {
  a[i] = sinf(b[i]);
  }
}

コマンドライン入力と出力結果

[D:/simd] icl example2.c –nologo –O3 -Qvec-report2 -Qrestrict
example2.c
D:\simd\example2.c(3): (列 3) リマーク: ループがベクトル化されました。

上記のコードをコンパイルすると、sinf() 関数を持つループは適切な SVML ライブラリー関数 (インテル® コンパイラーにより提供される) を使用して自動ベクトル化されます。 自動ベクトライザーはエントリーポイントを識別し、スカラー算術ライブラリー関数に対応する SVML 関数を探し、起動します。

このループ内に sinf() と同じプロトタイプを持つユーザー定義関数 foo() への呼び出しがある場合、この呼び出し位置に foo() がインライン展開されていない限り、自動ベクトライザーはこの関数が何をするか分からないため、ループのベクトル化に失敗します。

例: ユーザー定義関数を持つループが自動ベクトル化しないケース

[D:/simd] cat example2.c
float foo(float);
void vfoo(float *restrict a, float *restrict b, int n){
 int i;
 for (i=0; i<n; i++){
  a[i] = foo(b[i]);
 }
}

コマンドライン入力と出力結果

[D:/simd] icl example2.c -nologo -O3 -Qvec-report2 -Qrestrict
example2.c
D:\simd\example2.c(4): (列 3) リマーク: ループはベクトル化されませんでした: ベクトル依存関係が存在しています。

このような場合、__attribute__((vector)) 宣言を使用してループをベクトル化できます。 vector 宣言を関数宣言に加えて、呼び出し元と呼び出し先の両コードを再コンパイルするだけです。 これで、ループと関数はベクトル化されます。

例: vector 宣言のあるユーザー定義関数を持つループがベクトル化するケース

[D:/simd] cat example3.c
// foo() と vfoo() は 同じ declspec を見ることができれば
// 同じコンパイル単位に存在する必要はありません
__declspec(vector)
float foo(float);
void vfoo(float *restrict a, float *restrict b, int n){
int i;
for (i=0; i<n; i++) {
  a[i] = foo(b[i]);
  }
}
float foo(float x) {
...
}

コマンドライン入力と出力結果

[D:/simd] icl example3.c -nologo -O3 -Qvec-report3 –Qrestrict 
example3.c
D:\simd\example3.c(9): (列 3) リマーク: ループがベクトル化されました。
D:\simd\example3.c(14): (列 3) リマーク: 関数がベクトル化されました。

vector 宣言の使用に関する制約

ベクトル化は、ハードウェアとソースコードのスタイルという 2 つの主な要因により制約されます。vector 宣言を使用する場合、使用できない機能は次のとおりです。

仮パラメーターは次のデータ型でなければなりません。

関連情報


このヘルプトピックについてのフィードバックを送信