マルチスレッドプログラミングで総和計算 - その4 OpenMP 編-

kawa0810 のブログ の続きです.
最後は OpenMP について取り扱います.

OpenMP

Open Multi-Processing (OpenMP) はプログラム中の並列化したい箇所に OpenMP の規約に伴う指示文(ディレクティブ)を記述することで,指定した部分のプログラムをマルチスレッドで動作するようにしてくれます.OpenMP は共有メモリ型計算機で用いられることが多く,分散メモリ型計算機環境では以前紹介した MPI が用いられます.また,gccOpenMP を使用する場合は gcc4.2 以上のコンパイラを使用する必要があります.

OpenMP を用いた総和計算

OpenMP を用いた総和計算の例を以下に記載します.

/**--------------------
   openmp_sum.cpp
   --------------------*/

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <numeric>
#ifdef _OPENMP
#include <omp.h>//-fopenmp
#endif

std::size_t const CACHE_LINE = 32;
std::size_t const NUM_THREAD = 4;

template<class T>
T vec_sum(std::size_t const n, T const* x){
  T sum = 0;

#ifdef _OPENMP
#pragma omp parallel for reduction(+:sum) num_threads(NUM_THREAD)
#endif
  for(std::size_t i=0; i<n; ++i){
    sum += x[i];
  }

  return sum;
}

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

  posix_memalign(reinterpret_cast<void**>(&x), CACHE_LINE, n * sizeof(float));
  posix_memalign(reinterpret_cast<void**>(&y), CACHE_LINE, n * sizeof(double));

  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" << std::endl;
  std::cout << "- Accumulate: " << std::accumulate(x, x+n, 0.0) << std::endl;
  std::cout << "- vec_add: " << vec_sum(n, x) << std::endl;

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

  free(x);
  free(y);

  return 0;
}

OpenMP の使い方

・インクルードするファイル

#include <omp.h>

また,gccコンパイルする際は -fopenmp が必要となります.

$ g++-4.2 openmp.cpp -fopenmp

・#pragma omp parallel for
並列化したい for 文の前に 「#pragma omp parallel for」を指定してあげることで OpenMP 自動的に並列化してくれます.Pthread や std::async,_beginthreadex と比べると以下の利点があります.
・スレッドで動作させたい部位を別の関数にする必要がない
・返り値の指定する必要がない
・引数の制限がない
しかしながら,代わりにすべて自動で並列化してしまうため,細かい制御をすることができません.

「#pragma omp parallel for」の後ろにある reduction と num_threads はそれぞれ以下の意味となっています.
・reduction: 各スレッドが終了する際に指定した変数に対して演算を行う.Fortran の場合,比較も可能ですが,C/C++ の場合は四則演算のみとなっています.
・num_threads: 指定した数のスレッドを生成して実行する.

マルチスレッドプログラミングで総和計算のまとめ

今回はマルチスレッドプログラミング方法ということで Pthread,std::async,_beginthreadex,OpenMP で総和計算を例に記述方法の違いについて書いてみました.各手法の違いをまとめると以下のような感じかと思います(個人的な感想を含む).

・Pthread: POSIX 準拠なので多くの環境で動くが,返り値や引数の制限が厳しい.
・std::async: C++11 から登場なので開発環境の制限があるが,今後に期待.
・_beginthreadex: Windows 環境なら安定.使いやすさは(ry
OpenMP: 簡単に書くことが可能だが,細かい制御はできない.

今回,紹介できなった機能も多くあるので興味がある方は調べてみてください.(本当は実行速度の比較などもやりたかったんですが主に時間的理由で断念しました...)

マルチスレッドプログラミングで総和計算