読者です 読者をやめる 読者になる 読者になる

メモリアライメントを意識した動的確保を行う際は posix_memalign が良いぽい

posix_memalign() が良い理由

本ブログでは今まで _mm_malloc() を使用してメモリアライメントを揃えていましたが,posix_memalign() を使用したほうが良いぽいです.理由は _mm_malloc() が Intel CPU 環境でのみ動作するのに対し,posix_memalign() は標準化されているためメインストリームな環境であれば概ね動作するためです.また,posix_memalign() で確保したメモリは free() で解放することができ,_mm_free() が混在しなくなるためコードの保守性の向上が考えられます.

posix_memalign の使用方法

簡単な使用方法のみ掲載します.
・インクルードするファイル

#include <stdlib.h>

posix_memalign() の使用方法

int posix_memalign(void **memptr, size_t alignment, size_t size)
//memptr: メモリの先頭
//alignment: 揃えたいメモリアライメント
//size: 確保したいメモリのサイズ

・メモリの解放方法
free() でメモリ解放可能

posix_memalign() に直した総和計算 with SIMD

/*--------------------
  posix_memalign_sum.cpp
  --------------------*/

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <numeric>
#include <immintrin.h>

float vec_sum(std::size_t const n, float const* x){
  std::size_t static const para = 8;
  std::size_t const end = n / para;
  std::size_t const beg = end * para;

  auto vsum = _mm256_setzero_ps();
  auto sum = reinterpret_cast<float*>(&vsum);
  auto vx = reinterpret_cast<__m256 const*>( x );

  for(std::size_t i=0; i<end; ++i)
    vsum = _mm256_add_ps(vsum, vx[i]);
  for(std::size_t i=beg; i<n; ++i)
    sum[0] += x[i];
  sum[0] += sum[1] + sum[2] + sum[3] + sum[4] + sum[5] + sum[6] + sum[7];
  
  return sum[0];
}

double vec_sum(std::size_t const n, double const* x){
  std::size_t static const para = 4;
  std::size_t const end = n / para;
  std::size_t const beg = end * para;

  auto vsum = _mm256_setzero_pd();
  auto sum = reinterpret_cast<double*>(&vsum);
  auto vx = reinterpret_cast<__m256d const*>( x );

  for(std::size_t i=0; i<end; ++i)
    vsum = _mm256_add_pd(vsum, vx[i]);
  for(std::size_t i=beg; i<n; ++i)
    sum[0] += x[i];
  sum[0] += sum[1] + sum[2] + sum[3];
  
  return sum[0];
}

int main(void){
  std::size_t const n = 1024;
  float* x;
  double* y;

  //x = static_cast<float*>( _mm_malloc(sizeof(float) * n, 32) );
  //y = static_cast<double*>( _mm_malloc(sizeof(double) * n, 32) );
  posix_memalign(reinterpret_cast<void**>(&x), 32, sizeof(float) * n);
  posix_memalign(reinterpret_cast<void**>(&y), 32, sizeof(double) * n);

  srand( static_cast<unsigned>(time(NULL)) );
  for(std::size_t i=0; i<n; ++i) x[i] = static_cast<float>( rand() ) / RAND_MAX;
  for(std::size_t i=0; i<n; ++i) y[i] = static_cast<double>( rand() ) / RAND_MAX;

  std::cout << "Single Precision\n";
  std::cout << "-Accumulate: " << std::accumulate(x, x+n, 0.0) << std::endl;  
  std::cout << "-SIMD: " << vec_sum(n, x) << std::endl;

  std::cout << "\nDouble Precision\n";
  std::cout << "-Accumulate: " << std::accumulate(y, y+n, 0.0) << std::endl;  
  std::cout << "-SIMD: " << vec_sum(n, y) << std::endl;

  //_mm_free(x);
  //_mm_free(y);
  free(x);
  free(y);

  return 0;
}