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

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

C/C++ マルチスレッド

Pthread とは

近年,マルチコアプロセッサの普及に伴い,複数のプロセッサを利用する並列・並行プログラムの開発が求められています.POSIX Thread (Pthread) は POSIX 標準のスレッドであり,Pthread を用いることでスレッドの生成・実行・制御が可能となります.

Pthread を用いた総和計算の高速化の例

/**--------------------
   pthread_sum.cpp
   --------------------*/

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <numeric>
#include <pthread.h>//-lpthread

template<class T>
struct Pthread_sum{
  std::size_t tid;
  std::size_t n;
  T const* x;
  T sum;
};

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

template<class T>
void* vec_sum_thread(void* args){
  Pthread_sum<T>* param = static_cast<Pthread_sum<T>*>(args);
  std::size_t len = (param->n + NUM_THREAD - 1) / NUM_THREAD;
  std::size_t const beg = param->tid * len;
  std::size_t const end = (param->tid == NUM_THREAD - 1 ? param->n : beg+len);

  param->sum = 0;
  for(std::size_t i=beg; i<end; ++i){
    param->sum += param->x[i];
  }
}

template<class T>
T vec_sum(std::size_t const n, T const* x){
  pthread_t thread[NUM_THREAD];
  Pthread_sum<T> param[NUM_THREAD];

  for(std::size_t i=0; i<NUM_THREAD; ++i){
    param[i].tid = i;
    param[i].n = n;
    param[i].x = x;
    pthread_create(&thread[i], NULL, vec_sum_thread<T>, &param[i]);
  }

  for(std::size_t i=0; i<NUM_THREAD; ++i)
    pthread_join(thread[i], NULL);

  for(std::size_t i=1; i<NUM_THREAD; ++i){
    param[0].sum += param[i].sum;
  }

  return param[0].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;
}

Pthread の使用方法

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

#include <pthread.h>

また,コンパイルオプションに -lpthread を指定する必要があります.

$ g++-mp-4.7 pthread_sum.cpp -lpthread

・pthread_create()
pthread_create() を使用することでスレッドを作成・実行します.

int pthread_create(pthread_t *restrict thread,
		   const pthread_attr_t *restrict attr,
		   void*(*start_routine)(void*),
		   void *restrict arg);
//thread: 作成・実行させたいスレッド
//attr: スレッドに付与する属性
//void*: スレッドで実行させたい関数
//arg: 関数の引数

スレッドで実行させたい部分は必ず関数にして pthread_create() に渡す必要があります.また,関数の返り値は必ず void* にしなければなりません.さらに,関数の引数に渡せるのも void* 1つだけとなっており,一般的に構造体に渡したい変数を設定して渡す必要があります.

・pthread_join()
実行中のスレッドの同期や終了を行います.

int pthread_join(pthread_t th, void** thread_return);
//th: th で指定したスレッドを待つ
//thread_return: th の返り値が thread_return で指定された領域に格納される

Pthread 以外のマルチスレッドの実現方法

Pthread は実行する関数の返り値と引数に必ず void* を使う必要があるなど多くの問題点を含んでいます.Pthread 以外の代表的なマルチスレッドの実現方法に以下のものが挙げられます.

  • std::thread, std::future, std::async
  • Win32 API の CreateThread, _beginthreadex
  • OpenMP
マルチスレッドプログラミングで総和計算