このセクションでは、ベクトル・プログラミングでよく起こる問題について簡単な例をいくつか挙げて説明します。
下の例に示したループはベクトルコピーを行うものですが、これはベクトル化されます。その理由は、コンパイラが dest[i] と src[i] との区別が付くのを証明できるからです。
void vec_copy(float *dest, float *src, int len) { int i; for(i=0; i<len; i++;) { dest[i]=src[i]; } } |
下の例に示した restrict キーワードは、各ポインタが別々のオブジェクトを参照しているのを示します。したがって、マルチ・バージョン・コードを生成しなくてもベクトル化できます。
void vec_copy(float *restrict dest, float *restrict src, int len) { int i; for(i=0; i<len; i++) { dest[i]=src[i]; } } |
16 バイト以上のデータ構造体または配列は、その基底アドレスが 16 の倍数になる方法で各構造体や配列要素の先頭をアライメントしてください。
下の図は、アライメントの合っていないデータが原因でデータ・キャッシュ・ユニット (DCU) を分割した場合の影響を示したものです。アライメントの合っていないデータをロードすると 16 バイト境界にまたがるため、メモリアクセスが 1 回余分に発生し、その結果、6〜12 サイクルのストールが発生します。データのアライメントの合っているのがわかっている場合や、アライメントされているものとして指定した場合は、このストールを避けられます。
例えば、要素 a[0] と b[0] が 16 バイト境界にアライメントされているのがわかっている場合は、アライメント・オプション (#pragma vector aligned) をオンにすると、次のループはベクトル化できます:
float *a, *b; int i;
for(int i=0; i<10; i++) { a[i]=b[i]; } |
ベクトル化すると、ループはこのように実行されます:
ベクトル反復処理である a[0:3] = b[0:3] と a[4:7] = b[4:7] は、要素 a[0] と b[0] (同様に a[4] と b[4]) が 16 バイトでアライメントされている場合には、アライメントの合った移動命令を使用して両方とも実装できます。
警告
不正なアライメント・オプションでベクトライザを指定すると、コンパイラの動作は予測がつかなくなります。特に、アライメントの合っていないデータを、アライメントの合っている移動命令で処理すると、不正命令例外が発生します。
下の例は、アライメントの合っていないメモリ命令でしかベクトル化しないループを示したものです。コンパイラはこのローカル配列をアライメントできますが、コンパイル時には lb の値がわからないため、 アライメントが正しいかどうかの判断はできません。
void f(int lb) { float z2[N], a2[N], y2[N], x2; for(i=lb; i<N; i++) { a2[i]=a2[i]*x2+y2[i]; } } |
lb が 4 の倍数であることがわかっているときは、下の例に示したように #pragma vector aligned を使用してループをアライメントできます。
void f(int lb) { float z2[N], a2[N], y2[N], x2; assert(lb%4==0);
#pragma vector aligned
for(i=lb; i<N; i++) { a2[i]=a2[i]*x2+y2[i]; } } |