OpenMPのアトミックとクリティカルの違いは何ですか?
私がすることができます
#pragma omp atomic
g_qCount++;
しかし、これと同じではありません
#pragma omp critical
g_qCount++;
?
G_qCountへの影響は同じですが、行われることは異なります。
OpenMPのクリティカルセクションは完全に一般的です-任意のコードブロックを囲むことができます。ただし、スレッドがクリティカルセクションに出入りするたびにかなりのオーバーヘッドが発生するため、(シリアル化に伴う固有のコストに加えて)その一般性に対応できます。
(さらに、OpenMPでは、名前のないクリティカルセクションはすべて同一と見なされます(必要に応じて、名前のないすべてのクリティカルセクションにロックが1つしかありません)。 [名前のない]クリティカルセクション:ご想像のとおり、名前の付いたクリティカルセクションを使用してこれを回避できます。
アトミック操作のオーバーヘッドははるかに低くなります。利用可能な場合は、アトミックなインクリメント操作を提供する(たとえば)ハードウェアを利用します。その場合、コード行の開始/終了時にロック/ロック解除は必要ありません。ハードウェアが干渉できないと通知するアトミックなインクリメントを行うだけです。
利点は、オーバーヘッドがはるかに低く、アトミック操作にある1つのスレッドが、これから発生する(異なる)アトミック操作をブロックしないことです。欠点は、アトミックがサポートする操作の制限されたセットです。
もちろん、どちらの場合でも、シリアル化のコストが発生します。
OpenMPでは、名前のないクリティカルセクションはすべて相互に排他的です。
Criticalとatomicの最も重要な違いは、atomicは1つの割り当てのみを保護でき、特定の演算子で使用できることです。
重要なセクション:
「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
を使用し、それ以外の場合はnamed重要セクションを使用することをお勧めします。それらに名前を付けることは重要です。この方法で頭痛をデバッグする必要はありません。
すでに素晴らしい説明がここにあります。ただし、もう少し詳しく説明します。 OpenMPのatomicとcritical sectionの概念の核となる違いを理解するには、最初にlockの概念を理解する必要があります。 locksを使用する必要がある理由を確認しましょう。
並列プログラムは複数のスレッドによって実行されています。これらのスレッド間でsynchronizationを実行した場合にのみ、確定的な結果が発生します。もちろん、スレッド間の同期は必ずしも必要ではありません。 同期が必要なケースを参照しています。
マルチスレッドプログラムのスレッドをsynchronizeにするには、lockを使用します。一度に1つのスレッドのみによってアクセスを制限する必要がある場合、lock sが作用します。 lockコンセプトの実装は、プロセッサごとに異なる場合があります。単純なロックがアルゴリズムの観点からどのように機能するかを調べてみましょう。
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つのスレッドがこのコードを実行する場合、1つがSW R1、lockに到達してから、他のスレッドがlock変数を読み取ります。したがって、どちらもlockは無料であると考えています。この問題を解決するために、単純なLWおよびSWではなく、別の命令が用意されています。 Read-Modify-Write命令と呼ばれます。 ロック取得プロシージャが一度に単一スレッドのみで実行されることを保証する複雑な命令(サブ命令で構成される)です。 Read-Modify-Writeと単純なReadおよびWrite命令との違いは、異なる方法でLoadingおよびStoring。 LL(Load Linked)を使用してロック変数をロードし、SC(Store Conditional)を使用してロック変数に書き込みます。追加のリンクレジスタを使用して、ロック取得の手順が単一のスレッドによって確実に行われるようにします。アルゴリズムを以下に示します。
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
リンクレジスタがリセットされたときに、別のスレッドがロックが解放されていると想定している場合、ロックにインクリメントされた値を再度書き込むことはできません。したがって、lock変数へのアクセスの並行性が取得されます。
criticalとatomicの核となる違いは、次の考え方に由来します。
実際の変数(操作を実行している)をロック変数として使用できるのに、なぜロック(新しい変数)を使用するのですか?
ロックにnew変数を使用するとクリティカルセクションになり、ロックとしてactual変数を使用するとto atomic concept。クリティカルセクションは、実際の変数に対して多くの計算(複数行)を実行する場合に役立ちます。これは、これらの計算の結果が実際の変数に書き込まれなかった場合、結果を計算するために手順全体を繰り返す必要があるためです。これは、ロックが解除されるのを待ってから計算領域が大きくなるのに比べて、パフォーマンスが低下する可能性があります。したがって、単一の計算(x ++、x-、++ x、-xなど)を実行する場合はatomicディレクティブを使用し、criticalを使用することをお勧めしますより計算的に複雑な領域が集中セクションによって行われている場合のディレクティブ。