Intel AVX を使用して SIMD 演算を試してみる - その4 -
2012-03-05 - kawa0810の日記 の続きです.
今回はその2 (2012-03-04 - kawa0810の日記) で紹介した AVX 命令を試します.
計算する問題
今回は SIMD 演算を用いてベクトルのスカラ倍 を計算する方法を考えます.ただし,
はベクトルの要素数
= 2.0
とします.また,端数は逐次計算で処理を行います.
普通にプログラミングすると以下のようになると思われます.ただし,今回はプログラミング言語を C++ にし,テンプレートを使ったり関数をオーバーロードさせたりして単精度と倍精度のソースが1つに収まるようにしています.
/*-------------------- scal.cpp --------------------*/ #include <iostream> #include <immintrin.h> template<class T> void scal(const std::size_t n, const T alpha, T* x){ for(std::size_t i=0; i<n; ++i) x[i] = alpha * x[i]; } int main(void){ const std::size_t n = 12; const double alpha = 2.0; double *x = static_cast<double *>( _mm_malloc(sizeof(double) * n, 32) ); for(std::size_t i=0; i<n; ++i) x[i] = i; scal(n, alpha, x); for(std::size_t i=0; i<n; ++i) std::cout << x[i] << "\n"; _mm_free(x); return 0; }
C++ の場合でもコンパイル方法はほぼ同様です.Mac でコンパイルする場合を以下に記載しておきます.
$ g++-mp-4.6 -S main.cpp -std=c++0x -mavx $ clang++-mp-3.1 main.s -mavx
SIMD 演算を行うにあたり気をつけること
AVX 命令は必ず 128bit (16byte) 単位もしくは 256bit (32byte) 単位で計算を行います.しかしながら, はスカラであり,単精度ならば 4byte 倍精度ならば 8byte であるため計算を行う際に少し工夫が必要です.今回は _mm256_set_ps() もしくは _mm256_set_pd を使用して __m256 型に複数
を格納して計算を行うことにします.つまり,
- 単精度で計算を行う場合
__m256 valpha = _mm256_set_ps(alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha); //valpha[0] == alpha //valpha[1] == alpha //valpha[2] == alpha //valpha[3] == alpha //valpha[4] == alpha //valpha[5] == alpha //valpha[6] == alpha //valpha[7] == alpha
- 倍精度で計算を行う場合
__m256d valpha = _mm256_set_pd(alpha, alpha, alpha, alpha); //valpha[0] == alpha //valpha[1] == alpha //valpha[2] == alpha //valpha[3] == alpha
となります.
SIMD 演算を用いて高速化
上述の点に気をつけてコードを書くと以下のようになるかと思います.
/*-------------------- scal_simd.cpp --------------------*/ #include <iostream> #include <immintrin.h> void scal(const std::size_t n, const float alpha, float* x){ static const std::size_t para = 8; __m256 *vx = reinterpret_cast<__m256*>( x ); const __m256 valpha = _mm256_set_ps(alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha); const std::size_t end = n / para; const std::size_t beg = end * para; for(std::size_t i=0; i<end; ++i) vx[i] = _mm256_mul_ps(valpha, vx[i]); for(std::size_t i=beg; i<n; ++i) x[i] = alpha * x[i]; } void scal(const std::size_t n, const double alpha, double* x){ static const std::size_t para = 4; __m256d *vx = reinterpret_cast<__m256d*>( x ); const __m256d valpha = _mm256_set_pd(alpha, alpha, alpha, alpha); const std::size_t end = n / para; const std::size_t beg = end * para; for(std::size_t i=0; i<end; ++i) vx[i] = _mm256_mul_pd(valpha, vx[i]); for(std::size_t i=beg; i<n; ++i) x[i] = alpha * x[i]; } int main(void){ const std::size_t n = 12; const double alpha = 2.0; double *x = static_cast<double *>( _mm_malloc(sizeof(double) * n, 32) ); for(std::size_t i=0; i<n; ++i) x[i] = i; scal(n, alpha, x); for(std::size_t i=0; i<n; ++i) std::cout << x[i] << "\n"; _mm_free(x); return 0; }
Intel AVX を使用して SIMD 演算を試してみる
- AVX その1: 2012-03-03 - kawa0810の日記
- AVX その2: 2012-03-04 - kawa0810の日記
- AVX その3: 2012-03-05 - kawa0810の日記
- AVX その4 (今回) : 2012-03-06 - kawa0810の日記
- AVX その5 (最後): 2012-03-07 - kawa0810の日記