ここでは、コードのベクトル化に役立つ言語機能について説明します。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 |
ループをベクトル化する方法を指定し、効率ヒューリスティックについては無視したほうがよいことを示します。 |
#pragma novector |
ループをベクトル化しないよう指定します。 |
マルチバージョン・コードは、不明な値のポインターにより、データの依存性の解析がループの独立性を証明できなかった場合に備えて、コンパイラーによって生成されます。この機能は、動的依存性のテストと呼ばれます。
これらのプラグマは、プログラム中の後に続くループのみのベクトル化を制御しますが、コンパイラーは入れ子されたループには適用しません。各入れ子されたループは、その前にプラグマが適用されるように、固有のプラグマが必要です。プラグマは、loop 文の前にのみ配置してください。
構文 |
---|
#pragma vector always |
このプラグマは、ベクトル化するかどうかを決めるとき、効率ヒューリスティックを無効にするようにコンパイラーに指示します。#pragma vector always は、1 以外のストライドまたはほとんどアライメントの合っていないメモリーアクセスをベクトル化します。
例 |
---|
void vec_always(int *a, int *b, int m) { #pragma vector always for(int i = 0; i <= m; i++) a[32*i] = b[99*i]; } |
構文 |
---|
#pragma ivdep |
このプラグマは、ベクトル依存性が存在していると推定されてもそれを無視するようにコンパイラーに指示します。正しいコードにするため、コンパイラーは、想定される依存性を証明された依存性として扱い、ベクトル化を行わないようにします。このプラグマは、その決定を無視します。推定されたループの依存性が安全で、無視できる場合にのみ使用してください。
この例のループは、k の値が不明なため、ivdep プラグマではベクトル化を行いません (ベクトル化は k < 0 の場合、不正です)。
例 |
---|
void ignore_vec_dep(int *a, int k, int c, int m) { #pragma ivdep for (int i = 0; i < m; i++) a[i] = a[i + k] * c; } |
構文 |
---|
#pragma vector{aligned | unaligned} |
vector ループプラグマは、ループのベクトル化が可能である限り、ベクトル化によるメリットの存否についての通常のヒューリスティックな判断を無視してベクトル化を行います。aligned または unaligned 指示子が使用されると、ループは、aligned または unaligned 演算を使用して、ベクトル化されます。aligned または unaligned のいずれか 1 つを指定します。
引数として aligned を指定する場合、ループはこの命令を使用してベクトル化可能であることが確実でなければなりません。それ以外の場合、コンパイラーは誤ったコードを生成します。
次の例に示すループは、コンパイラーは通常それが安全であると証明できないように配列が宣言されているため、aligned 指示子を使用して、aligned 命令でループがベクトル化されるように要求を出します。
例 |
---|
void vec_aligned(float *a, int m, int c) { int i; // Instruct compiler to ignore assumed vector dependencies. #pragma vector aligned for (i = 0; i < m; i++) a[i] = a[i] * c; // Alignment unknown but compiler can still align. for (i = 0; i < 100; i++) a[i] = a[i] + 1.0f; } |
コンパイラーは、コンパイル時にデータ構造のアライメントが不明な場合に備えて、いくつかのアライメント手法を用意しています。簡単な例を次に示します。ループにおいて、アライメントが不明な場合、コンパイラーはプレリュードのループを生成し、ほとんどの場合に発生する配列参照がアライメントされたアドレスにヒットするまでループを反復します。これにより、アライメント・プロパティーが判明し、そのプロパティーに応じてベクトルループが最適化されます。
アライメント手法の例 |
---|
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 ループプラグマは、ループのベクトル化が有効な場合でもループをベクトル化しないことを示します。この例では、反復回数が低すぎて、ベクトル化が無駄になるとわかっています。ループがベクトル化可能であると認識されても、#pragma novector を使用して、コンパイラーにベクトル化しないように指示できます。
例 |
---|
void novec(int lb, int ub, float *a, float *b) { #pragma novector for (int j = lb; j < ub; j++) a[j] = a[j] + b[j]; } |
構文 |
---|
#pragma vector nontemporal |
#pragma vector nontemporal は、インテル(R) Pentium(R) 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 dependency for (i = L; i <= U; i++) { p[i] = q[i]; } else { for (i = L; i <= U; i++) { p[i] = q[i]; } } |