Intel AVX を使用して SIMD 演算を試してみる - その2 -

2012-03-03 - kawa0810の日記 の続きです.
今回は AVX 命令の組み込み関数の説明等です.

データ型

 AVX 命令で使用できる型は主に整数 (int),単精度 (float),倍精度 (double) です.また,AVX 命令は SSE 命令の拡張であるため 256bit 単位の演算だけでなく 128bit 単位の演算も可能です.各精度に対応する AVX の型は以下の通りです.

__m128i//ビット整数値等で 128bit 演算を行う場合に使用
__m256i//ビット整数値等で 256bit 演算を行う場合に使用

__m128//単精度で 128bit 演算を行う場合に使用
__m256//単精度で 256bit 演算を行う場合に使用

__m128d//倍精度で 128bit 演算を行う場合に使用
__m256d//倍精度で 256bit 演算を行う場合に使用

 一般的に int 型と float 型のデータサイズは 4byte,double 型のデータサイズは 8byte かと思います.つまり,AVX 命令を用いて SIMD 演算を行う場合は

  • 128bit 単位で演算を行う場合,int 型と float 型は4つずつ,double 型は2つずつ計算を行うことが可能
  • 256bit 単位で演算を行う場合,int 型と float 型は8つずつ,double 型は4つずつ計算を行うことが可能

となります.今回は SIMD 演算を行う際に用意したデータを __m256 型にキャストすることで変換を行いますが,必ずメモリアライメントを32の倍数に揃える必要があります.メモリアライメントを揃える方法はいろいろありますが今回は以下の方法で揃えています.

  • 動的に確保する場合
_mm_malloc(size, byte) //size は確保したいバイト数,byte は揃えたい境界

ただし,_mm_malloc() でメモリを確保した場合,_mm_free() でメモリを解放する必要があります.

_mm_free(T *) //T はデータ型
  • 静的に確保する場合
T __attribute__((aligned(byte))) tmp[n];//T はデータ型,byte は揃えたい境界

ただし,__attribute__((aligned(32))) は gcc拡張機能であるため,gcc 以外のコンパイラを使用する場合は適宜適切な方法を使用してください.

また,キャスト方法を以下に示します.

  • float 型の場合
__m256 *vx = (__m256 *)x;
  • double 型の場合
__m256d *vx = (__m256d *)x;

主に使用すると思われる単精度と倍精度の AVX 命令

 主に使用すると思われる単精度と倍精度の AVX 命令の基本的な命令を以下に示します.

四則演算
  • 加算:  \bf{x} + \bf{y}
_mm_add_ps(__mm128, __mm128)//128bit の単精度の加算
_mm_add_pd(__mm128, __mm128)//128bit の倍精度の加算
_mm256_add_ps(__mm256, __mm256)//256bit の単精度の加算
_mm256_add_pd(__mm256, __mm256)//256bit の倍精度の加算
  • 減算:  \bf{x} - \bf{y}
_mm_sub_ps(__mm128, __mm128)//128bit の単精度の減算
_mm_sub_pd(__mm128, __mm128)//128bit の倍精度の減算
_mm256_sub_ps(__mm256, __mm256)//256bit の単精度の減算
_mm256_sub_pd(__mm256, __mm256)//256bit の倍精度の減算
  • 乗算:  \bf{x} \times \bf{y}
_mm_mul_ps(__mm128, __mm128)//128bit の単精度の乗算
_mm_mul_pd(__mm128, __mm128)//128bit の倍精度の乗算
_mm256_mul_ps(__mm256, __mm256)//256bit の単精度の乗算
_mm256_mul_pd(__mm256, __mm256)//256bit の倍精度の乗算
  • 除算:  \bf{x} \div \bf{y}
_mm_div_ps(__mm128, __mm128)//128bit の単精度の除算
_mm_div_pd(__mm128, __mm128)//128bit の倍精度の除算
_mm256_div_ps(__mm256, __mm256)//256bit の単精度の除算
_mm256_div_pd(__mm256, __mm256)//256bit の倍精度の除算
初期化
_mm_setzero_ps()//128bit の単精度の初期化
_mm_setzero_pd()//128bit の倍精度の初期化
_mm256_setzero_ps()//256bit の単精度の初期化
_mm256_setzero_pd()//256bit の倍精度の初期化
  • サンプルコード1
...
__m256 sum = _mm256_setzero_ps();//sum = 0.0
...
  • サンプルコード2
...
__m256d* vx = (__m256 *)x;
vx[0] = _mm256_setzero_pd();
//x[0] == 0.0
//x[1] == 0.0
//x[2] == 0.0
//x[3] == 0.0
...
__mm128, __mm256 型にデータをセットする
_mm_set_ps(float, float, float, float)//128bit の単精度
_mm_set_pd(double, double)//128bit の倍精度
_mm256_set_ps(float, float, float, float, float, float, float, float)//256bit の単精度
_mm256_set_pd(double, double, double, double)//256bit の倍精度
__mm128, __mm256 型にデータをロードする
_mm_load_ps(float* )//128bit の単精度
_mm_load_pd(double* )//128bit の倍精度
_mm256_load_ps(float* )//256bit の単精度
_mm256_load_pd(double* )//256bit の倍精度
  • サンプルコード
...
double *x = (double *)_mm_malloc(sizeof(double) * n, 32);
for(int i=0; i<n; i+=4)
    __m256 vx = _mm256_load_pd(x+i);
...

私のブログだと基本的にキャストで __mm256 型などに変換していますが,Open Multi-Processing (OpenMP) の並列化方法と SIMD 演算を組み合わせる際に明示的にデータをロードしたい場合などで使用するかと思います.

Fused Multiply Add (FMA) 命令

FMA 命令の説明は今後すると思いますが,現在のところ Sandy Bridge には FMA 命令は未実装のようです.
Why no FMA in AVX in Sandy Bridge?
次期 Intel CPU である Haswell が速く登場すると良いですね


Intel AVX を使用して SIMD 演算を試してみる