ユーザー指示または SIMD ベクトル化は、OpenMP* 並列化が自動並列化を補足するように、自動ベクトル化を補足します。下記の図でこの関係を示します。ユーザー指示によるベクトル化は SIMD (Single-Instruction, Multiple-Data) 機能として実装され、SIMD ベクトル化と呼ばれます。
SIMD ベクトル化機能は、インテル製マイクロプロセッサーおよび互換マイクロプロセッサーの両方で利用可能です。ベクトル化により呼び出されるライブラリー・ルーチンは、互換マイクロプロセッサーよりもインテル製マイクロプロセッサーにおいてより優れたパフォーマンスが得られる可能性があります。また、ベクトル化は、/arch や /Qx (Windows*) または -m や -x (Linux* および Mac OS* X) などの特定のオプションによる影響を受けます。
次の図は、ハードウェアのベクトル化機能を活用するベクトル化コードを生成するためのさまざまなアプローチの中で、SIMD ベクトル化がどこに位置付けされているかを示しています。SIMD ベクトル化を用いて記述されたプログラムは、自動ベクトル化ヒントを使用して記述されたものと似ています。SIMD ベクトル化を使用すると、ベクトル化コードの取得に必要なコードの変更を最小限にすることができます。
SIMD ベクトル化は !DIR$ SIMD 宣言子を使用してループをベクトル化します。ループに宣言子を追加して、ループがベクトル化されるように再コンパイルしなければなりません (Windows* OS の場合は -Qsimd オプションが、Linux* OS の場合は -simd オプションがデフォルトで有効になります)。
不明なデータ依存性の距離 "X" のために、コンパイラーが自動でループをベクトル化しない Fortran の例ついて考えてみます。自動ベクトル化ヒント !DIR$ IVDEP でデータ依存性のアサーションを追加してループをベクトル化するかどうかをコンパイラーに判断させるか、または !DIR$ SIMD を使ってループのベクトル化を強制実行することができます。
例: !DIR$ SIMD なし |
---|
[D:/simd] cat example1.f subroutine add(A, N, X) integer N, X real A(N) DO I=X+1, N A(I) = A(I) + A(I-X) ENDDO end |
コマンドライン入力と出力結果 |
[D:/simd] ifort example1.f -nologo -Qvec-report2 D:\simd\example1.f(6): (例. 9) リマーク: ループはベクトル化されませんでした: ベクトル依存関係が存在しています。 |
例: !DIR$ SIMD あり |
[D:/simd] cat example1.f subroutine add(A, N, X) integer N, X real A(N) !DIR$ SIMD DO I=X+1, N A(I) = A(I) + A(I-X) ENDDO end |
コマンドライン入力と出力結果 |
[D:/simd] ifort example1.f -nologo -Qvec-report2 -Qsimd D:\simd\example1.f(7): (例. 9) リマーク: ループがベクトル化されました。 |
SIMD 宣言子と自動ベクトル化ヒントの主な違いは、SIMD 宣言子では、コンパイラーはループをベクトル化できない場合に警告を発行します。自動ベクトル化ヒントでは、!DIR$ VECTOR ALWAYS ヒントを使用した場合でも、実際のベクトル化はコンパイラーの判断にまかせられます。
SIMD 宣言子にはオプション節があり、コンパイラーにベクトル化の方法を指示できます。コンパイラーが正しいベクトル化コードを生成するための十分な情報を得られるように、これらの節を適切に使用してください。節についての詳細は、!DIR$ SIMD の説明を参照してください。
!DIR$ SIMD 宣言子の使用に関して、次の点に注意してください。
算術関数 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) リマーク: ループはベクトル化されませんでした: ベクトル依存関係が存在しています。 |
このような場合、!DIR$ attributes vector::function-name-list 宣言を使用してループをベクトル化できます。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) リマーク: 関数がベクトル化されました。 |
ベクトル化は、ハードウェアとソースコードのスタイルという 2 つの主な要因により制約されます。vector 宣言を使用する場合、使用できない機能は次のとおりです。
_Cilk_spawn、_Cilk_for、OpenMP* parallel/for/sections/task、および明示的なスレッド API 呼び出しによるスレッドの生成と join
setjmp、longjmp、EH、SEH の使用
インライン・アセンブリー・コードと VML
非ベクトル関数の呼び出し (すべての SVML 関数はベクトル関数と見なされる)
ロック、バリア、atomic 構文、クリティカル・セクション
GOTO 文
組み込み関数 (例: SVML 組み込み関数)
関数ポインターと仮想関数による関数呼び出し
ループ/配列表記構造
構造体アクセス
計算型 GOTO 文
© 1996-2011 Intel Corporation. 無断での引用、転載を禁じます。