言語サポートとディレクティブ

ここでは、コードのベクトル化に役立つ言語機能について説明します。declspec(align(n)) 宣言は、ハードウェアのアライメント境界を克服します。restrict 指示子とプラグマは、語彙スコープ、データの依存性、両義性の解決のために書きかたに関する問題を処理します。

言語サポート

特徴 説明
__declspec(align(n)) 変数を n バイト境界にアライメントするようコンパイラに命令します。変数のアドレスは address mod n=0 です。
__declspec(align(n,off)) n バイト境界内でオフセットだけ離して変数を n バイト境界にアライメントするようコンパイラに命令します。変数のアドレスは address mod n=off です。
restrict エイリアス仮定を行うとき一義化機構に自由度を持たせます。これによってベクトル化の度合いがより高くなります。
__assume_aligned(a,n) 配列 an バイト境界にアライメントされていると見なすようコンパイラに指示します。アライメント情報が取得できなかった場合に使用します。
#pragma ivdep ベクトル依存性が存在していると推定されてもそれを無視するようコンパイラに命令します。
#pragma vector{aligned|unaligned|always} ループをベクトル化するときの方法を指定し、効率ヒューリスティックについては無視したほうがよいことを示します。
#pragma novector ループをベクトル化しないよう指定します。

マルチバージョン・コード

マルチバージョン・コードは、不明な値のポインタにより、データの依存性の分析がループの独立性を証明できなかった場合に備えて、コンパイラによって生成されます。この機能は、動的依存性のテストと呼ばれます。

プラグマ・スコープ

これらのプラグマは、プログラム中の後に続くループのみのベクトル化を制御しますが、コンパイラはネストされたループには適用しません。各ネストされたループは、その前にプラグマが適用されるように、固有のプラグマが必要です。プラグマ は、loop 文の前にのみ配置してください。

#pragma vector always

構文: #pragma vector always

定義: このプラグマは、ベクトル化するかどうかを決めるとき、効率ヒューリスティックを無効にするようコンパイラに命令します。#pragma vector always は、1 外のストライドまたはほとんどアライメントの合っていないメモリアクセスをベクトル化します。

例:

for(i = 0; i <= N; i++)

{

   a[32*i] = b[99*i];

}

#pragma ivdep

構文: #pragma ivdep

定義: このプラグマは、ベクトル依存性が存在していると推定されてもそれを無視するようコンパイラに命令します。正しいコードにするため、コンパイラは、想定される依存性を証明された依存性として扱います。これは、ベクトル化を行わないようにします。このプラグマは、その決定を無視します。推定されたループの依存性が安全で、無視できる場合にのみ使用してください。

この例のループは、k の値が不明なため、ivdep プラグマではベクトル化を行いません (ベクトル化は k<0 の場合、不正です)。

例:

#pragma ivdep

for (i = 0; i < m; i++)

{

   a[i] = a[i + k] * c;

}

#pragma vector

構文: #pragma vector{aligned | unaligned}

定義: vector ループプラグマは、ループのベクトル化が有効である場合、通常のヒューリスティックな採算性についての判断を無視して、ベクトル化を行います。アライメントされた (またはアライメントされていない) 指示子がこのプラグマで使用されると、ループは、アライメントされた (またはアライメントされていない) 演算を使用して、ベクトル化されます。アライメントされた指示子かアライメントされていない指示子のいずれかを指定します。

警告

引数として aligned を指定する場合、ループはこの命令を使用してベクトル化可能であることが確実でなければなりません。それ以外の場合、コンパイラは誤ったコードを生成します。

次の例に示すループは、コンパイラは通常それが安全であると証明できないように配列が宣言されているため、aligned 指示子を使用して、aligned 命令でループがベクトル化されるように要求を出します。

例:

void foo (float *a)

{

   #pragma vector aligned

   for (i = 0; i < m; i++)

   {

      a[i] = a[i] * c;

   }

}

コンパイラは、コンパイル時にデータ構造のアライメントがわからない場合に備えて、いくつかのアライメント手法を用意しています。以下に簡単な例を次に示します (但し、他の方法もサポートされています)。ループにおいて、a のアライメントが不明な場合、コンパイラはプレリュードのループを生成し、ほとんどの場合に発生する配列参照がアライメントされたアドレスにヒットするまでループを繰返します。これにより、a のアライメント・プロパティが判明し、そのプロパティに応じてベクトルループが最適化されます。

アライメント手法の例

float *a;

// alignment unknown

for (i = 0; i < 100; i++)

{

   a[i] = a[i] + 1.0f;

}

 

// dynamic loop peeling

p = a & 0x0f;

if (p != 0)

{

   p = (16 - p) / 4;

   for (i = 0; i < p; i++)

   {

      a[i] = a[i] + 1.0f;

   }

}

 

// loop with a aligned (will be vectorized accordingly)

for (i = p; i < 100; i++)

{

   a[i] = a[i] + 1.0f;

}

#pragma novector

構文: #pragma novector

定義: novector ループプラグマは、ループのベクトル化が有効な場合でもループをベクトル化しないことを示します。この例では、反復回数 (ub - lb) が低すぎて、ベクトル化が無駄になるとわかっています。ループがベクトル化可能であると認識されても、#pragma novector を使用して、コンパイラにベクトル化しないように指示できます。

例:

void foo (int lb, int ub)

{

   #pragma novector

   for (j = lb; j < ub; j++)

   {

      a[j] = a[j] + b[j];

   }

}

#pragma vector nontemporal

構文: #pragma vector nontemporal

定義: #pragma vector nontemporal は、Pentium® 4 プロセッサ・ベースのシステムのストリーミング・ストアをもたらします。生成されたアセンブリとのループ (float type) の例は、次のとおりです。N が大きくなると、Pentium 4 プロセッサ・システムでの非ストリーミング組込み関数のパフォーマンスが大幅に向上します。

例:

#pragma vector nontemporal

for (i = 0; i < N; i++)

   a[i] = 1;

   .B1.2:

movntps XMMWORD PTR _a[eax], xmm0

movntps XMMWORD PTR _a[eax+16], xmm0

add eax, 32

cmp eax, 4096

jl .B1.2

動的依存性のテスト例

float *p, *q;

for (i = L; I <= U; i++)

{

   p[i] = q[i];

}

...

pL = p * 4*L;

pH = p + 4*U;

qL = q + 4*L;

qH = q + 4*U;

if (pH < qL || pL > qH)

{

   // loop without data dependence

   for (i = L; i <= U; i++)

   {

      p[i] = q[i];

   } else {

   for (i = L; i <= U; i++)

   {

      p[i] = q[i];

   }

}