ベクトル化とループ

ここでは、ベクトル化と関連するループの並列化について説明します。

-parallel (Linux*) オプションまたは /Qparallel (Windows*) オプションを使用すると、インテル製マイクロプロセッサーおよび互換マイクロプロセッサーにおいて並列化が有効になります。実行ファイルでは、互換マイクロプロセッサーよりもインテル製マイクロプロセッサーにおいてより優れたパフォーマンスが得られる可能性があります。また、並列化は、/arch/Qx (Windows*) または -m-x (Linux* および Mac OS* X) などの特定のオプションによる影響を受けます。

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

ループの並列化との相互作用

-parallel-x (Linux* および Mac OS* X) または /Qparallel/Qx (Windows*) オプションを組み合わせると、同じコンパイルで、自動ループ並列化と自動ループベクトル化の両方が試みられます。

多くの場合、コンパイラーは、並列化には最外ループ、ベクトル化には最内ループを認識します。しかし、有効であると判断された場合、コンパイラーは、同じループに並列化とベクトル化を適用します。

「自動並列化のプログラミング」および「ベクトル化のプログラミングにおけるガイドライン」を参照してください。

まれに、ループ並列化 (自動または OpenMP* 宣言子のいずれかによって) が成功すると、コンパイラーにレポートされるベクトル化されなかったループのメッセージに影響することがあります。例えば、-vec-report2 (Linux* および Mac OS* X) または /Qvec-report2 (Windows*) オプションでは、ループのベクトル化が成功しなかったことが示されます。

ベクトル化されるループの種類

整数ループの場合、128 ビットのインテル® ストリーミング SIMD 拡張命令 (インテル® SSE) とインテル® アドバンスト・ベクトル・エクステンション (インテル® AVX) は 32 ビット、16 ビット、8 ビット、および限定対応の 64 ビットの整数データ型を使用するほとんどの算術演算子と論理演算子に対して SIMD 命令を提供します。

整数丸め演算の最終的な精度が保持される場合は、ベクトル化が可能です。例えば、最後に格納された値が 16 ビット整数である場合には、32 ビットの右シフト演算子は 16 ビット・モードではベクトル化されません。また、インテル® SSE およびインテル® AVX 命令セットは完全に直交型ではない (例えば、バイトオペランドのシフトはサポートされていない) ため、実際にはすべての整数演算をベクトル化ができないので注意してください。

32 ビット単精度および 64 ビット倍精度の浮動小数点数を操作するループの場合、インテル® SSE は算術演算子 (加算 (+)、減算 (-)、乗算 (*)、除算 (/)) に対して SIMD 命令を提供します。

また、ストリーミング SIMD 拡張命令は、MINMAX という二項演算子、および SQRT という単項演算子に SIMD 命令を提供しています。これ以外の複数の算術演算子の SIMD バージョン (三角関数 SINCOSTAN など) は、インテル® コンパイラーに添付されているベクトル数値ランタイム・ライブラリー内のソフトウェアでサポートしています。

ベクトル化が可能であるためには、ループは次の条件を満たしていなければなりません。

sin()log()fmax() などの算術組み込み関数は、コンパイラーのランタイム・ライブラリーにベクトル化されたバージョンが含まれているため、使用可能です。このような関数を次にリストします。多くは float と double の両方があります。

acos ceil fabs round
acosh cos floor sin
asin cosh fmax sinh
asinh erf fmin sqrt
atan erfc log tan
atan2 erfinv log10 tanh
atanh exp log2 trunc
cbrt exp2 pow

次の例のループは、sqrtf() がベクトル化可能で、func() がインライン展開されるため、ベクトル化できます。インライン展開は同じソースファイルにある関数のデフォルトの最適化で有効になっています。インライン展開レポートは /Qopt-report-phase ipo_inl (Windows*) または -opt-report-phase ipo_inl (Linux* および MacOS* X ) オプションを指定して取得できます。

float func(float x, float y, float xp, float yp) {
  float denom;
  denom = (x-xp)*(x-xp) + (y-yp)*(y-yp);
  denom = 1./sqrtf(denom);
  return denom;
}
float trap_int(float y, float x0, float xn, int nx, float xp, float yp) {
  float x, h, sumx;
  int i;
  h = (xn-x0) / nx;
  sumx = 0.5*( func(x0,y,xp,yp) + func(xn,y,xp,yp) );
  for (i=1;i<nx;i++) {
    x = x0 + i*h;
    sumx = sumx + func(x,y,xp,yp);
  }
  sumx = sumx * h;
  return sumx; 
}
// コマンドライン 
> icc -c -vec-report2 trap_integ.c
trap_int.c(16) (列 3): リマーク: ループがベクトル化されました。

ループ本体内の文

ベクトル化可能な演算は、浮動小数点データと整数データとで異なります。

整数配列の演算

ループ本体の文には、算術演算または論理演算 (これも、通常は配列) を使用できます。算術演算は、加算、減算、ABSMIN、および MAX に制限されます。論理演算には、ビット単位の ANDOR、および XOR の演算子を含んでいます。データ型は混在させられますが、効率性の低下につながる恐れがあります。

その他の演算

上記の浮動小数点演算と整数演算以外の文は使用できません。上記で説明された以外、ループの本体には、関数呼び出しは使用できません。

データ依存性

データ依存性とは、シリアルループに含まれている各演算の実行順序を制限する関係のことです。ベクトル化によって演算の実行順序が並び替えられるため、自動ベクトライザーでは任意のデータ依存性の解析を自由に使用できなければなりません。

データの依存関係によりベクトル化が妨げられる例を次に示します。この例に示す配列の各要素の値は、前の繰り返しで計算された前後の要素の値により決まります。

例 1: データ依存性を持つループ

subroutine dep(data, n)
  real :: data(n)
  integer :: i
 do i = 1, n-1
    data(i) = data(i-1)*0.25 + data(i)*0.5 + data(i+1)*0.25
  end do
end subroutine dep

上記の例に示すループは、ベクトル化できません。これは、現在の要素 DATA(I) への WRITE が直前の要素 DATA(I-1) の使用に依存しており、この要素が直前の反復時にすでに書き込まれ変更されているためです。このことは、次の例に示すように、配列のアクセスパターンの最初の 2 回の反復を見ればわかります。

例 1: データ依存性を持つループをベクトル化したもの

I=1: READ DATA(0)
READ DATA(1)
READ DATA(2)
WRITE DATA(1)
I=2: READ DATA(1)
READ DATA(2)
READ DATA(3)
WRITE DATA(2)

このループが示す通常のシーケンスでは、2 回目の反復時に読み込まれる DATA(1) の値は、最初の反復時に書き込まれます。ベクトル化を行うためには、元のループのセマンティクスを変えることなく、対象となるすべての反復を並列に実行しなければなりません。

例 2: データに依存しないループ
for(i=0; i<100; i++)
a[i]=b[i];
//which has the following access pattern
read b[0]
write a[0]
read b[1]
write a[1]

データ依存性の解析

データ依存性の解析とは、2 つのメモリーアクセスの重なり合う条件を見つけることです。その条件は、1 つのプログラムの中で参照を 2 回行うと仮定した場合は、次の 2 つの事項によって規定されます。

配列参照のデータ依存アナライザーは一連のテストとして構成され、時間とスペースコストに加えて性能においても段階的に強化していきます。

いずれかの次元で独立性が認められれば、それによって依存関係が排除できるため、最初は 1 次元ずつ単純なテストをいくつか実行します。宣言されている次元境界を超える恐れのある多次元配列参照は、テストを実施する前に、線形形式に変換できます。

簡単なテストとして、高速最大公約数 (GCD) テストや拡張限界テストなどを使用できます。GCD テストでは、ループ・インデックスの係数の GCD で定数項を均等に等分できない場合、データの独立性が証明されます。拡張限界テストでは、添字式において極値がオーバーラップする可能性があるかどうかをチェックします。

どの単純なテストでも独立性を証明できなかった場合は、最終的に Fourier-Motzkin 法の消去を用いた強力な階層型依存性解法を使用して、すべての次元におけるデータ依存性問題を解決します。

最適化に関する注意事項

インテル® コンパイラー、関連ライブラリーおよび関連開発ツールには、インテル製マイクロプロセッサーおよび互換マイクロプロセッサーで利用可能な命令セット (SIMD 命令セットなど) 向けの最適化オプションが含まれているか、あるいはオプションを利用している可能性がありますが、両者では結果が異なります。また、インテル® コンパイラー用の特定のコンパイラー・オプション (インテル® マイクロアーキテクチャーに非固有のオプションを含む) は、インテル製マイクロプロセッサー向けに予約されています。これらのコンパイラー・オプションと関連する命令セットおよび特定のマイクロプロセッサーの詳細は、『インテル® コンパイラー・ユーザー・リファレンス・ガイド』の「コンパイラー・オプション」を参照してください。インテル® コンパイラー製品のライブラリー・ルーチンの多くは、互換マイクロプロセッサーよりもインテル製マイクロプロセッサーでより高度に最適化されます。インテル® コンパイラー製品のコンパイラーとライブラリーは、選択されたオプション、コード、およびその他の要因に基づいてインテル製マイクロプロセッサーおよび互換マイクロプロセッサー向けに最適化されますが、インテル製マイクロプロセッサーにおいてより優れたパフォーマンスが得られる傾向にあります。

インテル® コンパイラー、関連ライブラリーおよび関連開発ツールは、互換マイクロプロセッサー向けには、インテル製マイクロプロセッサー向けと同等レベルの最適化が行われない可能性があります。これには、インテル® ストリーミング SIMD 拡張命令 2 (インテル® SSE2)、インテル® ストリーミング SIMD 拡張命令 3 (インテル® SSE3)、ストリーミング SIMD 拡張命令 3 補足命令 (SSSE3) 命令セットに関連する最適化およびその他の最適化が含まれます。インテルでは、インテル製ではないマイクロプロセッサーに対して、最適化の提供、機能、効果を保証していません。本製品のマイクロプロセッサー固有の最適化は、インテル製マイクロプロセッサーでの使用を目的としています。

インテルでは、インテル® コンパイラーおよびライブラリーがインテル製マイクロプロセッサーおよび互換マイクロプロセッサーにおいて、優れたパフォーマンスを引き出すのに役立つ選択肢であると信じておりますが、お客様の要件に最適なコンパイラーを選択いただくよう、他のコンパイラーの評価を行うことを推奨しています。インテルでは、あらゆるコンパイラーやライブラリーで優れたパフォーマンスが引き出され、お客様のビジネスの成功のお役に立ちたいと願っております。お気づきの点がございましたら、お知らせください。

改訂 #20110307


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

© 1996-2011 Intel Corporation. 無断での引用、転載を禁じます。