OpenMP 入門 須田礼仁 2009/10/30 初版
OpenMP 共有メモリ並列処理の標準化 API http://openmp.org/ 最新版は 30 3.0 バージョンによる違いはあまり大きくない サポートしているバージョンはともかく csp で動きます gcc も対応しています
やっぱり SPMD Single Program Multiple Data プログラム #pragma omp parallel printf( I am %d of %d n, omp_get_thread_num(), omp_get_num_threads()); 画面出力 I am 0 of 4 I am 3 of 4 I am 2 of 4 I am 1 of 4 実行全員同じことをする thread 0 thread 2 printf( I am %d of %d n, 0, 4); printf( I am %d of %d n, 2, 4); thread 1 thread 3 printf( I am %d of %d n, 1, 4); printf( I am %d of %d n, 3, 4);
OpenMP ディレクティブ #pragma omp ほにゃらら OpenMP プログラムとしてコンパイルしないと きは無視される OpenMP 関数はダミーにすり替えられる うまくプログラムを書けば 同一のプログラムが逐次用にコンパイル 実行できる ややこしいところは 並列用にコンパイルするときに定義されるマクロ _OPENMP を使ってなんとかする
OpenMP の並列化 OpenMP は自動並列化ではない 正しく動作するかどうかは プログラマの責任 逐次コンパイルと同じ演算をするかどうかも プログラマの責任
並列実行 #pragma omp parallel 直後の文 ( 複文でもよい ) が並列 ( 重複 ) 実行される 入れ子になっていてもよい が スレッドを作りすぎるとオーバーヘッドが大
基本 #include <omp.h> main() { pre(); pre(); #pragma omp parallel comp(); post(); } comp(); comp(); comp(); comp(); >> /usr/vac/bin/xlc qsmp=omp test.c >> setenv OMP_NUM_THREADS 4 post(); >> a.out
何プロセスで実行するか 3 通りの設定方法がある 環境変数 OMP_NUM_THREADS 関数 void omp_set_num_threads(int num_threads); #pragma omp parallel num_threads( スレッド数 ) 複数指定したら 下の方が優先される ( 多分 )
変数はどうなるか 私有 (private) と共有 (shared) がある 私有 : そのスレッドからしか参照できない 共有 : 他のスレッドから参照 変更できる デフォルト parallel の内側で宣言されれば 私有 parallel の外側で宣言されていれば 共有れば parallel 節で逆の指定をすることもできる private( 変数リスト ) とか shared( 変数リスト )
変数はどうなるか int x; #pragma omp parallel { int y; } めもり int x; { int y; { int y; { int y; { int y; } } } } ひとつだけのスレッド 0 用のスレッド 1 用のスレッド 2 用のスレッド 3 用の x y y y y
メモリモデル メモリアクセスは プログラムの通りの順序で行われるとは限らない volatile int x=0, y; スレッド 0 は 共有変数 y に produce() の結果を書き それから x を 1 にする #pragma omp parallel if (omp_get_thread_num() == 0) { スレッド 1 は x が 1 になったのを確認して y = produce(); y を読み込んで consume() の引数に渡す x = 1; } else { という具合には動作してくれない! while (x == 0); ( するかもしれないが しないかもしれない ) consume(y); () } こういうプログラムは よしておこう
flush 特殊な命令 (flush) の前後で メモリアクセス の順序を保証する 右のプログラム片の場合 part A に含まれるメモリ書きこみやメモリ読み出しが完了するまで part B に含まれるメモリ読み出しやメモリ書きこみは行われない キャッシュをフラッシュしてしまうかもね ほかのスレッドについては何も言えない part A #pragma omp flush part B
barrier #pragma omp barrier 同期する flush を含む #pragma omp parallel { pre(); #pragma omp barrier post(); } pre(); pre(); pre(); pre(); barrier post(); post(); post(); post(); barrier の前にあるスレッドがメモリに書きこんだものは barrier の後ではどのスレッドからでも読みだせる
parallel l の中で役立つもの #pragma omp single 直後の文を どれかひとつのスレッドで実行する #pragma omp master 直後の文は スレッド 0 番で実行される #pragma omp critical ( なまえ ) クリティカルセクション ( 名前はなくてもよい ) #pragma omp atomic 不可分演算 (x += a とか x ++ とかのみ )
for 文の並列処理 #pragma omp parallel for 直後に for 文を書く for (i = 0; i < 100; i++) のタイプのやつ ループがスレッドに分割されて実行される for (i=0; i<25; i++) for (i=25; i< 50; i++) for (i=50; i<75; i++) for (i=75; i<100; i++) # 図はイメージです
ループの分割はいろいろ static 一定のサイズのチャンクが ラウンドロビンでスレッドに割り当てられる dynamic 一定サイズのチャンクで 計算を終えたスレッドが 計算されていないチャンクを取りに行く guided 似るがクズがだだ dynamic に似ているが チャンクサイズがだんだん小さくなる
reduction スレッドをまたぐ総和のような演算を 勝手にやってくれる #pragma omp parallel for reduction(+; a) for (i=0; i< 100; i++) a += b[i]; 自分でプログラムするのはちょっと面倒 いくつかやり方がある ; 考えてみてね
性能について 共有メモリ並列処理は メモリアクセス競合が性能のボトルネックになりやすい データをキャッシュに乗せれば 主記憶アクセスの競合はない しかし キャッシュが共有の場合もある 単純な同期しか準備されていない 遅延隠蔽などが難しい コア数と同数のスレッド数というのが想定
むかしのウェブページについて 10 年近く前に 須田が名古屋大学の講義の ために作ったウェブページがあるたウ 当時は OpenMP の日本語資料は少なかった いまでも OpenMP で検索すると上位に来てしまう しかし 時代遅れになってしまっている 当時は 4 CPU マシンは数百万円もした メモリ性能 キャッシュサイズも全然違う といいつつ 今回の資料はあまりそれと違わない
並列処理言語について スーパーコンでは MPI と OpenMP というのが 標準的なプログラミング いずれもある時間スロットでマシンを占有的に利用する前提で作られている ( バッチ処理 ) コア数と OpenMP スレッド数 ノード数とド数と MPI プロセス数を同じにする ( ハイブリッド並列化 ) 十万コアを超える現在 百万コアを超える近い将来 こんなプログラムでよいはずはない