AVX で整数を SIMD 演算するには Intel AVX2 からのようですね

整数を SIMD 演算する方法

今まで浮動小数点型がメインだったのでたまには整数型の話です.ただし,Intel AVX 命令を使って整数型を SIMD 演算するには Intel AVX2 が必要であり,Intel AVX2 は次世代 CPU である Haswell に搭載のようなのでまだ使用することができません.また,SSE 命令で整数型を計算する場合は SSE2 を使用する必要があります.

SSE2 を使用する場合は以下の整数演算が可能なようです

  • 8bit: 主に char 型.__m128i 型で計算可能.命令のサッフィクスは epi8 等
  • 16bit: 主に short 型.__m128i 型で計算可能.命令のサッフィクスは epi16 等
  • 32bit: 主に int 型.__m128i 型で計算可能.命令のサッフィクスは epi32 等
  • 64bit: 主に long int 型.__m64 型で計算できる模様(未確認).命令のサッフィクスは si64 等

ちなみに...

  • unsigned 型の場合は epu*
  • signed 型の場合は epi*

を使用します.

int 型のサンプルコード

int 型でベクトル同士の加算演算を行うサンプルコードを以下に記載します.

/*--------------------
  sse_int.cpp
  --------------------*/

#include <iostream>
#include <immintrin.h>

void vec_add(std::size_t const n, int* z, int const* x, int const* y){
  std::size_t static const para = 4;
  std::size_t const end = n / para;
  std::size_t const beg = end * para;

  auto vx = reinterpret_cast<__m128i const*>( x );
  auto vy = reinterpret_cast<__m128i const*>( y );
  auto vz = reinterpret_cast<__m128i*>( z );

  for(std::size_t i=0; i<end; ++i)
    vz[i] = _mm_add_epi32(vx[i], vy[i]);
  for(std::size_t i=beg; i<n; ++i)
    z[i] = x[i] + y[i];
}

int main(void){
  std::size_t const n = 30;
  int* x = static_cast<int*>( _mm_malloc(sizeof(int) * n, 32) );
  int* y = static_cast<int*>( _mm_malloc(sizeof(int) * n, 32) );
  int* z = static_cast<int*>( _mm_malloc(sizeof(int) * n, 32) );

  for(std::size_t i=0; i<n; ++i) x[i] = i;
  for(std::size_t i=0; i<n; ++i) y[i] = i;
  for(std::size_t i=0; i<n; ++i) z[i] = 0;
  
  vec_add(n, z, x, y);

  for(std::size_t i=0; i<n; ++i)
    std::cout << z[i] << std::endl;

  _mm_free(x);
  _mm_free(y);
  _mm_free(z);

  return 0;
}

Intel AVX2 が使えるようになれば...

Intel AVX2 が使えるようになればこんな感じにプログラムが書けるようになると思います...タブン(現在のところ機能が未実装なので確認できません^^;)

/*--------------------
  avx_int.cpp
  --------------------*/

#include <iostream>
#include <immintrin.h>//immintrin.h は AVX なので AVX2 を include する

void vec_add(std::size_t const n, int* z, int const* x, int const* y){
  std::size_t static const para = 8;
  std::size_t const end = n / para;
  std::size_t const beg = end * para;

  auto vx = reinterpret_cast<__m256i const*>( x );
  auto vy = reinterpret_cast<__m256i const*>( y );
  auto vz = reinterpret_cast<__m256i*>( z );

  for(std::size_t i=0; i<end; ++i)
    vz[i] = _mm256_add_epi32(vx[i], vy[i]);
  for(std::size_t i=beg; i<n; ++i)
    z[i] = x[i] + y[i];
}

int main(void){
  std::size_t const n = 30;
  int* x = static_cast<int*>( _mm_malloc(sizeof(int) * n, 32) );
  int* y = static_cast<int*>( _mm_malloc(sizeof(int) * n, 32) );
  int* z = static_cast<int*>( _mm_malloc(sizeof(int) * n, 32) );

  for(std::size_t i=0; i<n; ++i) x[i] = i;
  for(std::size_t i=0; i<n; ++i) y[i] = i;
  for(std::size_t i=0; i<n; ++i) z[i] = 0;
  
  vec_add(n, z, x, y);

  for(std::size_t i=0; i<n; ++i)
    std::cout << z[i] << std::endl;

  _mm_free(x);
  _mm_free(y);
  _mm_free(z);

  return 0;
}

Haswell で使えるようになる新機能

Haswell で使えるようになる新機能で個人的に期待しているものをまとめてきます.

  • Intel AVX2: 整数型のサポート
  • FMA 命令の対応
  • ビット操作命令系の追加
  • Hardware Transactional Memory: Transactional Synchronization Extensions (TSX) とか.詳細は Intel の Programming Reference の Chapter8 を参照.