doridoridoriand’s diary

主に技術的なことを書いていく予定(たぶん)

CPUで並列処理

今回はGPU使ってないのでこの画像は適切じゃないんだけど、ちょっと適当なの探すのが面倒だったのでw

C++でThread並列を実装してみました。といっても大層なことをしたわけではなく、OpenMPを利用しただけです。 そもそもOPenMPってなんぞやってかんじですよね。すごい雑にいうと、マルチスレッド対応の実行ファイルを生成するのを補助してくれる業界規格です。この辺の詳しい事に関しては、以下の資料を読むとわかりやすかったです

利用方法はとても簡単で、omp.hヘッダファイルをインクルードした上で、#pragma`ではじまるディレクティブをforやwhileの前に書いてあげるだけです

といってもコードを書かないと何言ってるんだってなると思われるので、まずシングルスレッド版(並列化していないバージョン)を示します

#include <iostream>
#include <string>
#include <stdlib.h>

const int REPEAT_TIMES = 10000;
unsigned int num_a[REPEAT_TIMES][REPEAT_TIMES], num_b[REPEAT_TIMES][REPEAT_TIMES], num_c[REPEAT_TIMES][REPEAT_TIMES];

void initialize_params() {
  for (int counter = 0; counter < REPEAT_TIMES; counter++) {
    for (int counter2 = 0; counter2 < REPEAT_TIMES; counter2++) {
      num_a[counter][counter2] = 1 * counter2;
      num_b[counter][counter2] = 2 * counter;
    }
  }
}

int calc_vector() {
  for (int counter = 0; counter < REPEAT_TIMES; counter++) {
    for (int counter2 = 0; counter2 < REPEAT_TIMES; counter2++) {
      num_c[counter][counter2] = num_a[counter][counter2] + num_b[counter][counter2];
    }
  }
}

int main (int argc, char** argv) {
  double measure_start, measure_stop;

  measure_start = clock();
  for (int counter = 0; counter <= 100; counter++) {
    initialize_params();
    calc_vector();
  }
  measure_stop = clock();

  std::cout << "処理時間" << (double)(measure_stop - measure_start) / CLOCKS_PER_SEC << "[s]" << std::endl;

  return 0;
}

まあ至って単純。ただの足し算をひたすらしているだけです。これを並列化するためには #include <omp.h>を新たに付け足し、適切な箇所でディレクティブを追記してあげればOK。 すると次のようになるかと思われます

#include <iostream>
#include <string>
#include <stdlib.h>
#include <omp.h>

//OpenMPを用いたCPU並列化バージョン

using namespace std;

const int REPEAT_TIMES = 10000;
unsigned int num_a[REPEAT_TIMES][REPEAT_TIMES], num_b[REPEAT_TIMES][REPEAT_TIMES], num_c[REPEAT_TIMES][REPEAT_TIMES];

void initialize_params() {
  #pragma omp parallel for
  for (int counter = 0; counter < REPEAT_TIMES; counter++) {
    #pragma omp parallel for
    for (int counter2 = 0; counter2 < REPEAT_TIMES; counter2++) {
      num_a[counter][counter2] = 1 * counter2;
      num_b[counter][counter2] = 2 * counter;
    }
  }
}

int calc_vector() {
  #pragma omp parallel for
  for (int counter = 0; counter < REPEAT_TIMES; counter++) {
    #pragma omp parallel for
    for (int counter2 = 0; counter2 < REPEAT_TIMES; counter2++) {
      num_c[counter][counter2] = num_a[counter][counter2] + num_b[counter][counter2];
    }
  }
}

int main (int argc, char** argv) {
  time_t start_time, stop_time;

  time(&start_time);
  #pragma omp parallel for
  for (int counter = 0; counter <= 100; counter++) {
    initialize_params();
    calc_vector();
  }
  time(&stop_time);

  cout << "処理時間" << (double)(stop_time - start_time) << "[s]" << endl;

  return 0;
}

今回はCentOS上で実行するので、コンパイルオプションに-fopenmpをつけてコンパイルしました

$ g++ ファイル名.cpp -o 出力したい名前 -fopenmp

これでコンパイルし、MacBookPro上に立てたVagrantで実行しました。
結果は以下の表の通りになりました

シングルスレッド: 53.9202[s]
マルチスレッド(4スレッド): 16.0[s]

おー。大体3.3倍くらい早くなっていますね。4倍とはいかないものの、効果は絶大です

すべての処理を並列化出来るわけではないですが(時間発展するやつとかはそこまで早くならない)、積極的にマルチスレッドを活用していきたいと思いました