1. 参考資料
openMP_demo
OpenMP 入門
OpenMP チュートリアル (1) OpenMP 削減句の詳細な分析
2. OpenMP の概要
1. OpenMP の概要
OpenMP (Open Multi-Processing) は、C/C++ をサポートする共有メモリ並列システム用のマルチスレッド プログラム ソリューションです。OpenMP は、並列アルゴリズムの高レベルの抽象的な記述を提供します。これにより、複数のプロセッサ コアで並列コンピューティングを実装し、プログラムの実行効率を向上させることができます。プログラムに追加されたプラグマ命令に従って、コンパイラがプログラムを自動的に並列処理するため、OpenMP を使用することで並列プログラミングの難しさと複雑さが軽減されます。コンパイラが OpenMP をサポートしていない場合、プログラムは通常の (シリアル) プログラムに縮退し、プログラム内の既存の OpenMP 命令はプログラムの通常のコンパイルと動作に影響を与えません。
主流のコンパイル環境の多くには OpenMP が組み込まれており、Visual Studio では OpenMP の起動が非常に簡単です。プロジェクトを右クリック→「プロパティ」→「構成プロパティ」→「C/C++」→「言語」→「OpenMP サポート」を選択し、「はい」を選択します。
2. 共有メモリモデル
OpenMP は、マルチプロセッサおよびマルチコア共有メモリ マシン向けに設計されています。OpenMP の並列処理は処理ユニット (CPU コア) の数によって決まります。。
3. ハイブリッド並列モデル
OpenMP は単一ノードの並列処理に適しており、MPI を OpenMP と組み合わせて分散メモリ並列処理を実現します。これは、ハイブリッド並列処理モデルと呼ばれることがよくあります。
- OpenMP は、各ノード (1 台のコンピューター) での計算集約的な作業に使用されます。
- MPI は、ノード間の通信とデータ共有を実装するために使用されます。
4.モデルFork-Join
_
OpenMP は並列実行Fork-Join
モデルを使用します。
- フォーク: メインスレッドは一連の並列スレッドを作成します。
- 結合: チーム スレッドは並列領域で別々に計算を実行し、メイン スレッドのみを残して同期されて終了します。
上図ではparallel region
並列ドメインとなっており、並列ドメイン内ではマルチスレッドが並行しており、並列ドメイン間ではメインスレッド(マスター)による線形実行が行われます。
5.barrier
同期機構
barrier
これは、並列ドメインでのコードのスレッド同期に使用されます。スレッドの実行が に達すると、停止して待機する必要があります。barrier
すべてのスレッドが実行されるまでbarrier
実行は続行されず、それによってスレッドの同期が実現されます。
#include <stdio.h>
int main(void)
{
int th_id, nthreads;
#pragma omp parallel private(th_id)
{
th_id = __builtin_omp_get_thread_num();
printf("Hello World from thread %d\n", th_id);
#pragma omp barrier
if (th_id == 0) {
nthreads = __builtin_omp_get_num_threads();
printf("There are %d threads\n", nthreads);
}
}
return 0;
}
yoyo@yoyo:~/PATH/TO$ gcc -fopenmp demo.c -o demo
yoyo@yoyo:~/PATH/TO$ ./demo
Hello World from thread 10
Hello World from thread 3
Hello World from thread 2
Hello World from thread 6
Hello World from thread 4
Hello World from thread 7
Hello World from thread 0
Hello World from thread 5
Hello World from thread 11
Hello World from thread 8
Hello World from thread 1
Hello World from thread 9
There are 12 threads
3. 共通操作
1. よく使用される命令
# 安装OpenMP
sudo apt-get install libomp-dev
# 使用gcc编译OpenMP程序
gcc -fopenmp demo.c -o demo
# 使用g++编译OpenMP程序
g++ -fopenmp demo.cpp -o demo
2. 重要な操作
(1) 並列領域:#pragma omp parallel
命令を使用して、並列領域を定義する。
(2) スレッド番号:使用omp_get_thread_num()
関数現在のスレッドの番号を取得します。
(3) 総スレッド数:使用omp_get_num_threads()
関数スレッドの総数を取得する。
(4) データ共有: や などのキーワードを使用して、private
変数shared
の共有状態を宣言できます。
(5) 同期メカニズム:#pragma omp barrier
命令を使用して、スレッド同期を実装する。
3. OpenMPがサポートされているかどうかを確認します
#include <stdio.h>
int main()
{
#if _OPENMP
printf("support openmp\n");
#else
printf("not support openmp\n");
#endif
return 0;
}
yoyo@yoyo:~/PATH/TO$ gcc -fopenmp demo.c -o demo
yoyo@yoyo:~/PATH/TO$ ./demo-1
support openmp
4.Hello World
#include <stdio.h>
int main(void)
{
#pragma omp parallel
{
printf("Hello, world. \n");
}
return 0;
}
yoyo@yoyo:~/PATH/TO$ gcc -fopenmp demo.c -o demo
yoyo@yoyo:~/PATH/TO$ ./demo
Hello, world.
Hello, world.
Hello, world.
Hello, world.
Hello, world.
Hello, world.
Hello, world.
Hello, world.
Hello, world.
Hello, world.
Hello, world.
Hello, world.
スレッド数が指定されていないため、デフォルトの数値は CPU コアの数になります。
#include <stdio.h>
int main(void)
{
// 指定线程数量
#pragma omp parallel num_threads(6)
{
printf("Hello, world. \n");
}
return 0;
}
5.#pragma omp parallel for
omp_get_thread_num
: 現在のスレッド ID を取得します。
#include <stdio.h>
#include <omp.h>
#include <stdlib.h>
int main(void) {
#pragma omp parallel for
for (int i=0; i<12; i++) {
printf("OpenMP Test, th_id: %d\n", omp_get_thread_num());
}
return 0;
}
yoyo@yoyo:~/PATH/TO$ gcc -fopenmp demo.c -o demo
yoyo@yoyo:~/PATH/TO$ ./demo
OpenMP Test, th_id: 8
OpenMP Test, th_id: 3
OpenMP Test, th_id: 1
OpenMP Test, th_id: 9
OpenMP Test, th_id: 5
OpenMP Test, th_id: 0
OpenMP Test, th_id: 6
OpenMP Test, th_id: 11
OpenMP Test, th_id: 2
OpenMP Test, th_id: 7
OpenMP Test, th_id: 4
OpenMP Test, th_id: 10
6.プロトコルのreduction
動作
6.1 はじめに
#include <stdio.h>
#include <omp.h>
#include <stdlib.h>
int main(void) {
int sum = 0;
#pragma omp parallel for
for (int i=1; i<=100; i++) {
sum += i;
}
printf("%d", sum);
return 0;
}
yoyo@yoyo:~/PATH/TO$ gcc -fopenmp demo.c -o demo
yoyo@yoyo:~/PATH/TO$ ./demo
1173yoyo@yoyo:~/PATH/TO$ ./demo
2521yoyo@yoyo:~/PATH/TO$ ./demo
3529yoyo@yoyo:~/PATH/TO$ ./demo
2174yoyo@yoyo:~/PATH/TO$ ./demo
1332yoyo@yoyo:~/PATH/TO$ ./demo
1673yoyo@yoyo:~/PATH/TO$ ./demo
1183yoyo@yoyo:~/PATH/TO$
複数回実行すると、スレッドが同じリソースをめぐって競合するため、結果は毎回異なります。この行についてはsum += i;
、次のように書き換えることができますsum = sum + i
。複数のスレッドがsum
同時に並行して書き込みを行うため、競合が発生します。この問題を解決するには、 を使用できますreduction
。
6.2reduction
はじめに
reduction(操作符:变量)
sum
例として sum 関数を取り上げます。
#include <stdio.h>
#include <omp.h>
#include <stdlib.h>
int main(void) {
int sum = 0;
#pragma omp parallel for reduction(+:sum)
for (int i=1; i<=100; i++) {
sum += i;
}
printf("%d", sum);
return 0;
}
yoyo@yoyo:~/PATH/TO$ gcc -fopenmp demo.c -o demo
yoyo@yoyo:~/PATH/TO$ ./demo
5050yoyo@yoyo:~/PATH/TO$ ./demo
5050yoyo@yoyo:~/PATH/TO$ ./demo
5050yoyo@yoyo:~/PATH/TO$ ./demo
5050yoyo@yoyo:~/PATH/TO$ ./demo
5050yoyo@yoyo:~/PATH/TO$ ./demo
5050yoyo@yoyo:~/PATH/TO$
上記のコードでは、reduction(+:sum)
変数 sum を各スレッドでコピーし、コピーした変数をスレッド内で使用することを意味しますが、スレッドごとに使用する sum データが異なるため、データ競合の問題は発生しません。続行方法を示すreduction
プラス記号も表示されます。+
プロトコル操作。いわゆるリダクション操作とは、単純に複数のデータを徐々に操作し、最終的にはリダクションできないデータが得られることを意味します。
たとえば、上記のプログラムでは、仕様演算は である+
ため、スレッド 1 とスレッド 2 のデータを演算する必要があります+
。つまり、スレッド 1 の合計値とスレッド 2 の合計値を加算し、その結果を求めます。がグローバル変数 sum に割り当てられます。他のスレッドもこの手順に従い、最終的なグローバル変数 sum は正しい結果になります。
4 つのスレッドがある場合、4 つのスレッドローカル合計があり、各スレッドは合計のコピーをコピーします。この場合、リダクション演算の結果は次のようになります:
( ( ( sum 1 + sum 2 ) + sum 3 ) + sum 4 ) (((sum_1 + sum2) + sum_3) + sum_4)( ( ( _1+スム2 ) _ _+うーん_ _3)+うーん_ _4)
ここで、sum_i
は i 番目のスレッドによって取得された合計を表します。