並列処理が簡単に出来るOpemMPを今更試してみた。
手元の環境はWindowsXP + GCC4.5.0(g++)
GCCなら4.2.0以降であれば、特に何もしないでも-fopenmpをつけるだけでOpemMPが使えるらしい。
openmp_example.cpp
#include <iostream> #include <ctime> int main(){ const unsigned int loop = 4000000000U; clock_t start,end; start = clock(); #ifdef _OPENMP #pragma omp parallel #endif { #ifdef _OPENMP #pragma omp for #endif for(unsigned int i=0; i<loop; ++i){ ; } } end = clock(); std::cout << (end-start) << "clock" << std::endl; return 0; }
結果
g++ -Wall -o openmp_example.exe openmp_example.cpp
12234clock
ファイルサイズ 25,899 Byte
メモリ使用量1,364KB, スレッド数:1
g++ -Wall -fopenmp -o openmp_example.exe openmp_example.cpp
3015clock
ファイルサイズ 27,320Byte
メモリ使用量1,848KB, スレッド数:4
libgomp-1.dllが必要
4倍近く早くなっているので、並列化が有効になってるのがわかる。
並列処理の弊害
複数のスレッドが一つの変数にアクセスし、値を更新する場合、注意が必要。
#include <iostream> #include <ctime> int main(){ const unsigned int loop = 100000000U; unsigned int count = 0; clock_t start,end; start = clock(); #ifdef _OPENMP #pragma omp parallel #endif { #ifdef _OPENMP #pragma omp for #endif for(unsigned int i=0; i<loop; ++i){ count++; } } end = clock(); std::cout << (end-start) << "clock" << std::endl; std::cout << "count:" << count << std::endl; return 0; }
640clock
count:52728384
countの値がloopと等しくなるはずだが、並列処理によりcountの値が正しく更新できていない。
並列処理中にアトミックな処理が必要な場合、omp atomicプラグマを指定する。
#ifdef _OPENMP #pragma omp atomic #endif count++;
7422clock
count:100000000
なぜかブロックで括ることができないので、複数行にわたって書く場合、いちいち#pragma omp atomicを書く必要がある。
#pragma omp criticalもあるが、atomicとは違うので注意。
いちいち#ifdefで確認しなくても、サポートしていないプラグマならだいたい読み飛ばされるだけなので、面倒な人は書かなくてもいいかもしれない。
GCCの場合、-Wallでunknownなpragmaに対して警告が出るようになっていても、
"-Wno-unknown-pragmas"を加えるとこの警告だけが無効になる。