初めてOpenMPを使ってみて気づいたこと
最近、あるプログラムを並列化して速度を上げるためにOpenMPを使っています。ネット上で検索するとOpenMPの入門記事はあっても実際に使ってみたよ、というのは多くないようなのでそのときに気づいたことなどを書いてみました。
OpenMPと他の並列化技術との比較
並列化技術はOpenMPに限らず色々あります。C++なら最近は標準でstd::threadやstd::asyncがあります。また、並列化にはマルチスレッドによる並列化以外にもSIMDやGPGPUや分散コンピューティングによる並列化もあります。OpenMPはもともとマルチスレッドによる並列化技術でしたが、最近の規格ではSIMDやGPGPUによる並列化も定義していて、コンパイラは対応を進めている最中のようです。今回はOpenMPのマルチスレッドによる並列化を行いました。
速度面に関してはgccでベンチマーク(-O3 -march=native)を取ってみたところ、OpenMPとstd::threadやstd::asyncではほぼ変わりないようです。
#pragmaによる記述は好みが分かれそうなところですが、既存のコードへの変更が少なくて済むのが利点です。特に並列for構文では各スレッドへのタスクの割り当てと結果のreductionを自動化してくれるので、プログラマが部分タスクへの切り分けをしなくて良くなります。また、上手く書けばOpenMPを無効化しても意味を変えずにコンパイルできるのも特徴です。
仕様書が読みやすい
OpenMPの仕様書はすごく読みやすいので迷ったら当たってみることをお勧めします。最新の規格はOpenMP4.0ですが、一つ前のOpenMP3.0の仕様書は日本語版も公開されています。
http://openmp.org/wp/openmp-specifications/
初期値の設定
OpenMPでは多くのパラメータの初期値が実装定義となっているので、特定の値が欲しければ自分で設定する必要があります。並列化するスレッド数が実装定義とか言われても困るので
omp_set_num_threads(omp_get_num_procs());
などで設定しましょう。
スレッド数の動的管理の設定は重要そうですが私の環境では目に見える違いはありませんでした・・・
omp_set_dynamic(1);
scheduleの設定は重要
scheduleは並列for構文で並列化された処理をどのようにスレッドに割り当てるかを設定します。大まかに言えばループごとの処理量が一定ならschedule(static)が速く、そうでないならschedule(dynamic)などのほうが速いようです。場合によってはかなり差が出るので実際に試してみた方がいいところだと思います。ちなみに何も書かなかった場合の挙動は例によって実装定義です。