OpenMPに関して OpenMPとは†OpenMPは簡単に従来のプログラムを並列化することができるライブラリで以下のような特徴を持っています.
Visual Studioでの設定†Visual C++ 2005でOpenMPを用いるためには,コンパイラオプションに "/openmp" をセットします. プロジェクトのプロパティから, 構成プロパティ → C/C++ → 言語 の OpenMPサポート を「はい」にします. OpenMP使用時にはomp.hをインクルードする. #include <omp.h> OpenMP†有効/無効の確認†_OPENMPが定義されていれば,OpenMPが有効にしてコンパイルされている. #ifdef _OPENMP ... #endif スレッド数の確認†omp_get_max_threads()で最大スレッド数を確認できる. #ifdef _OPENMP cout << "OpenMP : On, threads =" << omp_get_max_threads() << endl; #endif スレッド番号の取得†int omp_get_thread_num(void); スレッド数の変更†
forの並列化†#pragma omp parallel for for(int i = 0; i < n; ++i){ // 処理 } 連続するfor文なら #pragma omp parallel { #pragma omp for for(int i = 0; i < n; ++i){ // 処理 } #pragma omp for for(int i = 0; i < n; ++i){ // 処理 } #pragma omp for for(int i = 0; i < n; ++i){ // 処理 } } parallelでスレッドの生成が行われるので, parallel指示は少ない方がよい. セクションでの並列化†#pragma omp parallel sections num_threads(2) { #pragma omp section { printf("thread %d\n", omp_get_thread_num()); } #pragma omp section { printf("thread %d\n", omp_get_thread_num()); } } reduction†合計値を求めるなどのreduction演算の場合は, #pragma omp for reduction(+:s) for(int i = 0; i < n; ++i){ s += i; } 変数のスコープ†OpenMPのブロック内の変数はデフォルトでは全てのスレッドで共有されます. しかし,例えばループ内で用いる一時変数などが共有されると,別のスレッドがその値を書き換えてしまうことがあります. そのため,スレッドごとに独立な変数とするためにprivateを用います. double x; #pragma omp parallel for private(x) for(long i = 0; i < n; ++i){ // xを使った処理 } 変数が複数あるときは,private(x,y,z)のように","で区切って指定します. 変数のスコープを変更するための命令としては,
同期†共有メモリでスレッド間でデータをやり取りする場合,他のスレッドの処理を待つ必要があります. これにはバリア同期を用いることができます.同期したい位置に以下を指定します. #pragma omp barrier 全スレッドがバリアに到達するまで,スレッドは待機します. また,スレッド間のメモリアクセスの一貫性をとるために, #pragma omp flush があります.ただし,barrierやcriticalなどの前後では明示的に指定しなくても 暗黙にflushされます. 共有メモリの資源を占有させるためのロックや,それを解放するためのアンロックもサポートされています. ロックを用いる場合は,omp_lock_t型のロック変数を用います. omp_lock_t lock0; omp_init_lock(&lock0); #pragma omp parallel num_threads(4) { int tid = omp_get_thread_num(); omp_set_lock(&lock0); for(int i = 0; i < 3; ++i){ cout << "Thread " << tid << endl; } omp_unset_lock(&lock0); } omp_destroy_lock(&lock0); 上記コードの実行結果は, Thread 0 Thread 0 Thread 0 Thread 1 Thread 1 Thread 1 Thread 2 Thread 2 Thread 2 Thread 3 Thread 3 Thread 3 計算例†円周率の計算 #include <stdio.h> #include <windows.h> #include <omp.h> volatile DWORD g_dwStart; double CalPI(long n) { double x, pi, sum = 0.0, step; step = 1.0/(double)n; #pragma omp parallel for reduction(+:sum) private(x) for(long i = 1; i <= n; ++i){ x = (i-0.5)*step; sum = sum+4.0/(1.0+x*x); } pi = step*sum; return pi; } int main(void) { #ifdef _OPENMP printf("OpenMP : On, threads = %d\n", omp_get_max_threads()); #endif long n = 100000000; for(int i = 0; i < 3; ++i){ g_dwStart = GetTickCount(); double pi = CalPI(n); printf("pi = %.15f, %d [msec]\n", pi, GetTickCount()-g_dwStart); } return 0; } リンク† |