ここでは、コードのベクトル化に役立つ言語機能について説明します。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) | 配列 a が n バイト境界にアライメントされていると見なすようコンパイラに指示します。アライメント情報が取得できなかった場合に使用します。 |
#pragma ivdep | ベクトル依存性が存在していると推定されてもそれを無視するようコンパイラに命令します。 |
#pragma vector{aligned|unaligned|always} | ループをベクトル化するときの方法を指定し、効率ヒューリスティックについては無視したほうがよいことを示します。 |
#pragma novector | ループをベクトル化しないよう指定します。 |
マルチバージョン・コードは、不明な値のポインタにより、データの依存性の分析がループの独立性を証明できなかった場合に備えて、コンパイラによって生成されます。この機能は、動的依存性のテストと呼ばれます。
これらのプラグマは、プログラム中の後に続くループのみのベクトル化を制御しますが、コンパイラはネストされたループには適用しません。各ネストされたループは、その前にプラグマが適用されるように、固有のプラグマが必要です。プラグマ は、loop 文の前にのみ配置してください。
構文: #pragma vector always
定義: このプラグマは、ベクトル化するかどうかを決めるとき、効率ヒューリスティックを無効にするようコンパイラに命令します。#pragma vector always は、1 外のストライドまたはほとんどアライメントの合っていないメモリアクセスをベクトル化します。
例:
for(i = 0; i <= N; i++) { a[32*i] = b[99*i]; } |
構文: #pragma ivdep
定義: このプラグマは、ベクトル依存性が存在していると推定されてもそれを無視するようコンパイラに命令します。正しいコードにするため、コンパイラは、想定される依存性を証明された依存性として扱います。これは、ベクトル化を行わないようにします。このプラグマは、その決定を無視します。推定されたループの依存性が安全で、無視できる場合にのみ使用してください。
この例のループは、k の値が不明なため、ivdep プラグマではベクトル化を行いません (ベクトル化は k<0 の場合、不正です)。
例:
#pragma ivdep for (i = 0; i < m; i++) { a[i] = a[i + k] * c; } |
構文: #pragma vector{aligned | unaligned}
定義: vector ループプラグマは、ループのベクトル化が有効である場合、通常のヒューリスティックな採算性についての判断を無視して、ベクトル化を行います。アライメントされた (またはアライメントされていない) 指示子がこのプラグマで使用されると、ループは、アライメントされた (またはアライメントされていない) 演算を使用して、ベクトル化されます。アライメントされた指示子かアライメントされていない指示子のいずれかを指定します。
警告
アライメントされているものを引数に指定する場合、ループはこの命令を使用してベクトル化可能であることが確実でなければなりません。それ以外の場合、コンパイラは誤ったコードを生成します。
次の例に示したループは、アライメントされた指示子を使用して、アライメントされた命令でループがベクトル化されるように要求を出します。コンパイラは通常そうすることが安全であると証明できないように、配列が宣言されているためです。
例:
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
定義: 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 は、Pentium
例:
#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]; } } |