OpenMPのアトミックとクリティカルの違いは何ですか?
私がすることができます
#pragma omp atomic
g_qCount++;
これは同じではありません
#pragma omp critical
g_qCount++;
?
OpenMPのアトミックとクリティカルの違いは何ですか?
私がすることができます
#pragma omp atomic
g_qCount++;
これは同じではありません
#pragma omp critical
g_qCount++;
?
回答:
g_qCountへの影響は同じですが、実行される処理が異なります。
OpenMPクリティカルセクションは完全に一般的です。コードの任意のブロックを囲むことができます。ただし、スレッドがクリティカルセクションに出入りするたびに(シリアライゼーションの固有のコストに加えて)大きなオーバーヘッドが発生することで、その一般性の代償を払うことになります。
(さらに、OpenMPでは、名前のないすべてのクリティカルセクションは同一と見なされます(必要に応じて、名前のないすべてのクリティカルセクションに対してロックは1つだけです)。そのため、上記のように1つの[名前のない]クリティカルセクションに1つのスレッドがある場合、どのスレッドも入ることができません。 [名前なし]クリティカルセクション。ご想像のとおり、名前付きクリティカルセクションを使用することでこれを回避できます。
アトミック操作はオーバーヘッドがはるかに低くなります。利用可能な場合は、ハードウェアを利用して(たとえば)アトミック増分操作を提供します。その場合、コード行の開始/終了時にロック/ロック解除は必要ありません。ハードウェアが干渉できないことを通知するアトミックな増分を実行するだけです。
利点は、オーバーヘッドがはるかに低く、アトミック操作にある1つのスレッドが、発生しようとしている(異なる)アトミック操作をブロックしないことです。欠点は、アトミックがサポートする操作の制限されたセットです。
もちろん、どちらの場合でも、シリアル化のコストが発生します。
OpenMPでは、名前のないすべてのクリティカルセクションは相互に排他的です。
クリティカルとアトミックの最も重要な違いは、アトミックは単一の割り当てのみを保護でき、特定の演算子で使用できることです。
重要なセクション:
「name」タグを適切に使用して、ブロックのグループをシリアル化するように拡張できます。
もっとゆっくり!
原子操作:
はるかに速いです!
特定の操作のシリアル化のみを保証します。
最速の方法は重要でもアトミックでもありません。クリティカルセクションを使用した追加は、単純な追加よりも約200倍高価であり、アトミック追加は単純な追加より25倍高価です。
最速のオプション(常に適用できるとは限りません)は、各スレッドに独自のカウンターを与え、合計が必要なときに削減操作を行うことです。
の制限atomic
は重要です。それらはOpenMPの仕様で詳しく説明されているはずです。MSDNはクイックチートシートを提供しています。これが変更されなくても驚かないでしょう。(Visual Studio 2012には、2002年3月からOpenMPが実装されています。)MSDNを引用するには:
式ステートメントは、以下のいずれかの形式でなければなりません。
x
binop =expr
x++
++x
x--
--x
上記の式で
x
は、lvalue
はスカラー型の式です。expr
はスカラー型の式であり、で指定されたオブジェクトを参照しませんx
。binopはオーバーロードオペレータではなく、のいずれかである+
、*
、-
、/
、&
、^
、|
、<<
、または>>
。
atomic
できる場合は使用し、そうでない場合はクリティカルセクションに名前を付けることをお勧めします。それらに名前を付けることは重要です。このように頭痛の種をデバッグすることは避けます。
ここですでに素晴らしい説明。ただし、もう少し深く潜ることができます。OpenMPのアトミックセクションとクリティカルセクションの概念の主な違いを理解するには、まずロックの概念を理解する必要があります。ロックを使用する必要がある理由を確認してみましょう。
並列プログラムが複数のスレッドによって実行されています。これらのスレッド間で同期を実行した場合にのみ、確定的な結果が発生します。もちろん、スレッド間の同期は必ずしも必要ではありません。同期が必要なケースについて言及します。
マルチスレッドプログラムのスレッドを同期するために、lockを使用します。一度に1つのスレッドだけでアクセスを制限する必要がある場合は、ロックが機能します。ロックコンセプトの実装では、プロセッサからプロセッサに異なる場合があります。アルゴリズムの観点から、単純なロックがどのように機能するかを見てみましょう。
1. Define a variable called lock.
2. For each thread:
2.1. Read the lock.
2.2. If lock == 0, lock = 1 and goto 3 // Try to grab the lock
Else goto 2.1 // Wait until the lock is released
3. Do something...
4. lock = 0 // Release the lock
与えられたアルゴリズムは、次のようにハードウェア言語で実装できます。単一のプロセッサーを想定して、その中のロックの動作を分析します。このプラクティスでは、MIPS、Alpha、ARM、Powerのいずれかのプロセッサを想定します。
try: LW R1, lock
BNEZ R1, try
ADDI R1, R1, #1
SW R1, lock
このプログラムは問題ないようですが、問題があります。上記のコードは以前の問題に悩まされています。同期。問題を見つけましょう。ロックの初期値をゼロと仮定します。2つのスレッドがこのコードを実行する場合、一方がSW R1に到達し、もう一方がロック変数を読み取る前にロックする可能性があります。したがって、どちらもロックは解放されていると考えます。この問題を解決するために、単純なLWおよびSWではなく、別の指示が提供されています。これは、読み取り-変更-書き込み命令と呼ばれます。これは複雑な命令(サブ命令で構成される)であり、ロックの取得手順が1つだけで実行されることを保証します。一度にスレッドです。読み取り-変更-書き込みの違い単純な読み取りおよび書き込み命令と比較すると、ロードとストアの方法が異なります。LL(ロードリンク)を使用してロック変数をロードし、SC(ストア条件付き)を使用してロック変数に書き込みます。追加のリンクレジスタを使用して、ロック取得の手順が単一のスレッドで実行されるようにします。アルゴリズムを以下に示します。
1. Define a variable called lock.
2. For each thread:
2.1. Read the lock and put the address of lock variable inside the Link Register.
2.2. If (lock == 0) and (&lock == Link Register), lock = 1 and reset the Link Register then goto 3 // Try to grab the lock
Else goto 2.1 // Wait until the lock is released
3. Do something...
4. lock = 0 // Release the lock
リンクレジスタがリセットされたときに、別のスレッドがロックが解放されていると想定している場合、インクリメントされた値をロックに再度書き込むことはできません。したがって、ロックへのアクセスの並行性変数が獲得されます。
クリティカルとアトミックの主な違いは、次のような考えにあります。
実際の変数(操作を実行している)をロック変数として使用できるのに、なぜロック(新しい変数)を使用するのですか?
ロックに新しい変数を使用するとクリティカルセクションになり、実際の変数をロックとして使用するとアトミックコンセプトになります。クリティカルセクションは、実際の変数に対して多数の計算(複数行)を実行する場合に役立ちます。これは、これらの計算の結果を実際の変数に書き込めない場合は、手順全体を繰り返して結果を計算する必要があるためです。これは、高度に計算された領域に入る前にロックが解放されるのを待つのと比較して、パフォーマンスが低下する可能性があります。したがって、単一の計算(x ++、x-、++ x、-xなど)を実行する場合は常に、atomicディレクティブを使用して、より複雑な領域が集中セクションによって実行されている場合の重要なディレクティブ。
アトミックは単一のステートメントのクリティカルセクションです。つまり、1つのステートメントの実行に対してロックします。
クリティカルセクションはコードブロックのロックです
優れたコンパイラは、最初のコードと同じ方法で2番目のコードを変換します
++
と*=
)と、彼らはハードウェアでサポートされていない場合、彼らはによって置き換えられるかもしれないことcritical
セクション。