概要
c++ で開発をしているときに、簡単にマルチプロセスをできる方法はないかと検索したときに、OpenMPが出てきたので色々試した結果、少しハマった部分があったので共有。
注意: 筆者はc++初心者なので、低レベルなハマりです。
簡単なコード
ググったらopenmpのc++サンプルコードは結構転がってます。例えば、
# include <stdio.h>
# include <omp.h>
int main() {
printf("使用可能な最大スレッド数:%d\n", omp_get_max_threads());
# pragma omp parallel for
for (int i = 0; i < 10; i++) {
// (何かの処理)
// 検証用の表示
printf("thread = %d, i = %2d\n", omp_get_thread_num(), i);
}
return 0;
}
と書いて、linuxであれば
g++ main.cpp -fopenmp
MacOSであれば
g++ main.cpp -Xpreprocessor -fopenmp -lomp
とすれば、
使用可能な最大スレッド数:4
thread = 3, i = 8
thread = 2, i = 6
thread = 3, i = 9
thread = 1, i = 3
thread = 0, i = 0
thread = 2, i = 7
thread = 1, i = 4
thread = 0, i = 1
thread = 1, i = 5
thread = 0, i = 2
となって、それぞれのイテレーションで、確かに複数スレッドが並列して走る。
for文の前に一行書くだけで並列処理、すごい!!となるわけです。
ハマりポイント()
for 文回す時って、だいたいの場合それぞれのイテレーションでなにか処理をして、結果を配列に格納する時が多いのではないでしょうか?
例として、
# include <stdio.h>
# include <omp.h>
# include <vector>
# include <iostream>
# include <stdio.h>
using namespace std;
//単純に2乗を計算して返す関数
double test_square(double x){
return x*x;
}
int main() {
printf("使用可能な最大スレッド数:%d\n", omp_get_max_threads());
double test;
//最後に結果を格納する配列
vector<double> box;
box.resize(10);
//各for文で呼ばれる配列
vector<double> chocolates;
for(int i=0; i<10; i++){
chocolates.push_back(i);
}
//for文だよ〜
//chocolateの各要素を2乗してboxの配列に格納するよ〜
#pragma omp parallel for
for (int i = 0; i < 10; i++) {
test = test_square(chocolates[i]);
box[i] = test;
printf("thread = %d, i = %2d\n", omp_get_thread_num(), i);
}
//最後に結果をプリントするよ〜
for(int i=0; i<10; i++){
cout << box[i] << endl;
}
return 0;
}
のような時です。
まあ、これはきちんと動いてくれました。
このとき、各イテレーションでtestという変数に、test_square()で2乗された値を入れています。
これが各プロセスで並列化して行われるわけですが、アクセス競合みたいなのが気になるわけです。。
今回は上手く動きましたが、自分の場合は似たようなロジックのコードを書いていて、アクセス競合が起き、結果がうまく受け取れない自体が発生していました。
そこで、以下のコードに書き換えることで上手くいきました。
# pragma omp parallel for private(test)
なんてことはないのですが、private(test) と書き、
各イテレーションでそれぞれ代入されるべきtestという変数を
private(各プロセスに関して独立している)と明記することで、競合をなくすことができました。
まとめ
たぶんわかっている人には常識すぎることだったかもしれませんが、初心者にはつまづきやすいところだと思ったので、もし誰かの助けになれば幸いです。
慣れてくればc++楽しい!!
これからも勉強がんばりましょ
おわり