ベクトル化の具体例

このセクションでは、ベクトル・プログラミングでよく起こる問題について簡単な例をいくつか挙げて説明します。

引数のエイリアシング: ベクトルコピー

下の例に示したループはベクトル・コピーを行うものですが、これはベクトル化されます。その理由は、コンパイラが 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 キーワードは、各ポインタが別々のオブジェクトを参照しているのを示します。したがって、マルチ・バージョン・コードを生成しなくてもベクトル化できます。

ベクトル化できるだけの違いがあることを証明するために 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 サイクルのストールが発生します。データのアライメントの合っているのがわかっている場合や、アライメントされているものとして指定した場合は、このストールを避けられます。

16 バイト境界にまたがる間違ってアライメントされたデータ

例えば、要素 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 を使用してループをアライメントできます。

変数の値が 4 の倍数であるという前提でアライメントを行う

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];

   }

}