OpenMPとはマルチコアCPUによるメモリ共有型の並列化を簡単に実装できる並列化技術です。
開発環境
Visual Studio 2015 c++
Windows 10 Home
Intel Core i7-6700K
初めてのOpenMP
まず最初に新規プロジェクトで空のアプリケーションを作って、以下のソースを実行してみましょう。
この例では、OpenMPの実行時ライブラリを使用しないので、インクルードする必要はありませんが、後々使うので一応インクルードしています。
#include <omp.h> #include <stdio.h> int main() { #pragma omp parallel printf("Hello OpenMP!\n"); return 0; }
でも、このままじゃ並列化されていません。
以下の2点の作業を行って初めて並列化されます。
これで実行すると…
コア数分だけ「printf("Hello OpenMP!\n");」の部分が表示されるはずです。私のCPUではコア数が8個ですので、8個に並列化されています。
OpenMPでは「#pragma」を入れるだけで並列化できるので非常に簡単ですね!
並列化数を指定
任意の数だけ並列化させたい場合は、「#pragma omp parallel」を「#pragma omp parallel num_threads('任意の数')」に変更するだけです。
#include <omp.h> #include <stdio.h> int main() { #pragma omp parallel num_threads(5) printf("Hello OpenMP!\n"); return 0; }
実行結果を示します。
指定した数(=5)だけ並列されていますね。
forループを並列化
OpenMPに任せると、適当にループを並列化してくれます。
forループ並列化の例を載せます。
#include <omp.h> #include <stdio.h> int main() { int a[10] = { 1,2,3,4,5,6,7,8,9,10 }; int b[10] = { 0 }; int i; #pragma omp parallel num_threads(2) #pragma omp for for (i = 0; i < 10; i++) { b[i] = a[i] * a[i]; printf(" i=%d, thread_ID=%d, thread_Num=%d\n", i, omp_get_thread_num(), omp_get_num_threads()); } for (i = 0; i < 10; i++) printf(" b[%d] = %d\n", i, b[i]); return 0; }
この例では、forループが”i=0~4”と”i=5~10”の2つのループに分割されて計算されます。
実行すると以下のような結果が表示されます。
スレッド0とスレッド1の動作順序は保証されるわけではありませんので、動作環境によっては順序が入れ替わることもあります。また、配列の代入順序とコマンドプロンプトへの表示順序が同じである保証もありません。
セクションで並列化
OpenMPではループの並列化によく使われますが、プログラムをブロック(セクション)分けして並列化することもにも使えます。
それがsectionsのコマンドです。以下に、各ブロックを並列化した例を示します。
#include <omp.h> #include <stdio.h> int main() { #pragma omp parallel #pragma omp sections { #pragma omp section printf("section0: thread_ID=%d, thread_Num=%d\n", omp_get_thread_num(), omp_get_num_threads()); #pragma omp section printf("section1: thread_ID=%d, thread_Num=%d\n", omp_get_thread_num(), omp_get_num_threads()); #pragma omp section printf("section2: thread_ID=%d, thread_Num=%d\n", omp_get_thread_num(), omp_get_num_threads()); } return 0; }
実行すると以下のような結果が表示されます。
このようにして、単純なループだけでなく異なる処理を並列化することが簡単にできます。上記の実行例では、8スレッドの内0,1,3スレッドで並列化されていることが分かります。
OpenMPとマルチスレッドの記述比較
OpenMPとスレッドを比較するため、"Hello OpenMP!"と10回表示させるだけの簡単なプログラムを両方の方法で記述していきます。マルチスレッドプログラムの書き方はいろいろありますが、今回はWindows APIを使用してみます。
↓OpenMPでのソース
#include <omp.h> #include <stdio.h> int main() { #pragma omp parallel num_threads(10) printf("Hello OpenMP!\n"); return 0; }
↓マルチスレッドでのソース
#include <Windows.h> #include <stdio.h> void threadProc(void) { printf("Hello OpenMP!\n"); } int main() { HANDLE hThread[10]; for (int i = 0; i < 10; i++) { hThread[i] = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)threadProc, NULL, 0, NULL); } WaitForMultipleObjects(10, hThread, TRUE, INFINITE); return 0; }
使い方によりますが、圧倒的にOpenMPの方がすっきりしていて使いやすいですね!マルチスレッドプログラミングは結構めんどくさいのです…