OpenMPのアトミックとクリティカルの違いは何ですか?


111

OpenMPのアトミックとクリティカルの違いは何ですか?

私がすることができます

#pragma omp atomic
g_qCount++;

これは同じではありません

#pragma omp critical
g_qCount++;

回答:


173

g_qCountへの影響は同じですが、実行される処理が異なります。

OpenMPクリティカルセクションは完全に一般的です。コードの任意のブロックを囲むことができます。ただし、スレッドがクリティカルセクションに出入りするたびに(シリアライゼーションの固有のコストに加えて)大きなオーバーヘッドが発生することで、その一般性の代償を払うことになります。

(さらに、OpenMPでは、名前のないすべてのクリティカルセクションは同一と見なされます(必要に応じて、名前のないすべてのクリティカルセクションに対してロックは1つだけです)。そのため、上記のように1つの[名前のない]クリティカルセクションに1つのスレッドがある場合、どのスレッドも入ることができません。 [名前なし]クリティカルセクション。ご想像のとおり、名前付きクリティカルセクションを使用することでこれを回避できます。

アトミック操作はオーバーヘッドがはるかに低くなります。利用可能な場合は、ハードウェアを利用して(たとえば)アトミック増分操作を提供します。その場合、コード行の開始/終了時にロック/ロック解除は必要ありません。ハードウェアが干渉できないことを通知するアトミックな増分を実行するだけです。

利点は、オーバーヘッドがはるかに低く、アトミック操作にある1つのスレッドが、発生しようとしている(異なる)アトミック操作をブロックしないことです。欠点は、アトミックがサポートする操作の制限されたセットです。

もちろん、どちらの場合でも、シリアル化のコストが発生します。


5
「移植性が失われる可能性があります」-これが本当かどうかはわかりません。標準(バージョン2.0)アトミック操作が許可されている指定します(基本的にはのようなもの++*=)と、彼らはハードウェアでサポートされていない場合、彼らはによって置き換えられるかもしれないことcriticalセクション。
Dan R

@DanRoche:はい、その通りです。その発言がこれまで正しいとは思わなかったので、今すぐ修正します。
Jonathan Dursi

数日前、OpenMPチュートリアルを実行しましたが、私が理解している限り、2つの異なるコードに違いがあります。つまり、クリティカルセクションによって命令がスレッドによって一度に実行されることが保証されるため、結果が異なる可能性があります。ただし、次のような命令である可能性があります。g_qCount = g_qCount + 1; スレッド1の場合、g_qCountの結果はRAMメモリーではなく書き込みバッファーにのみ格納され、スレッド2が値g_qCountをフェッチするときは、書き込みバッファーではなく、RAMの値を読み取るだけです。アトミック命令は、命令がデータをメモリに確実にフラッシュしたことを保証します
Giox79

30

OpenMPでは、名前のないすべてのクリティカルセクションは相互に排他的です。

クリティカルとアトミックの最も重要な違いは、アトミックは単一の割り当てのみを保護でき、特定の演算子で使用できることです。


13
これは、以前の回答のコメント(または編集)である方がよいでしょう。
kynan 2015年

20

重要なセクション:

  • コードブロックのシリアル化を保証します。
  • 「name」タグを適切に使用して、ブロックのグループをシリアル化するように拡張できます。

  • もっとゆっくり!

原子操作:

  • はるかに速いです!

  • 特定の操作のシリアル化のみを保証します。


9
しかし、この答えは非常に読みやすいですし、最初の答えの偉大合計アップだろう
のMichałMiszczyszyn

7

最速の方法は重要でもアトミックでもありません。クリティカルセクションを使用した追加は、単純な追加よりも約200倍高価であり、アトミック追加は単純な追加より25倍高価です。

最速のオプション(常に適用できるとは限りません)は、各スレッドに独自のカウンターを与え、合計が必要なときに削減操作を行うことです。


2
説明であなたが言及するすべての数字に同意しません。x86_64を想定すると、アトミック操作では、おおよそ1サイクルのコストで数サイクルのオーバーヘッド(キャッシュラインの同期)が発生します。そうでなければ「真の共有」コストが発生する場合、オーバーヘッドはゼロになります。クリティカルセクションでは、ロックのコストが発生します。ロックがすでに取得されているかどうかに応じて、オーバーヘッドは約2つのアトミック命令または2つのスケジューラの実行とスリープ時間です。これは通常、200倍を大幅に超えます。
Klaas van Gend 2016

6

の制限atomicは重要です。それらはOpenMPの仕様で詳しく説明されているはずです。MSDNはクイックチートシートを提供しています。これが変更されなくても驚かないでしょう。(Visual Studio 2012には、2002年3月からOpenMPが実装されています。)MSDNを引用するには:

式ステートメントは、以下のいずれかの形式でなければなりません。

xbinop =expr

x++

++x

x--

--x

上記の式でxは、lvalueはスカラー型の式です。exprはスカラー型の式であり、で指定されたオブジェクトを参照しませんxbinopはオーバーロードオペレータではなく、のいずれかである+*-/&^|<<、または>>

atomicできる場合は使用し、そうでない場合クリティカルセクションに名前を付けることをお勧めします。それらに名前を付けることは重要です。このように頭痛の種をデバッグすることは避けます。


1
それはいくつかの他の有益なステートメントを持つために私たちを可能にするように#pragma ompのaromic更新(または読み取り、upate、書き込み、キャプチャ):これはすべて、私たちのような他の先進原子のディレクティブを持っていない
pooria

1

ここですでに素晴らしい説明。ただし、もう少し深く潜ることができます。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

与えられたアルゴリズムは、次のようにハードウェア言語で実装できます。単一のプロセッサーを想定して、その中のロックの動作を分析します。このプラクティスでは、MIPSAlphaARMPowerのいずれかのプロセッサを想定します。

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ディレクティブを使用して、より複雑な領域が集中セクションによって実行されている場合の重要なディレクティブ。


-5

アトミックは、単一の命令のみに対して相互排除を有効にする必要がある場合に比較的パフォーマンスが効率的です。ompクリティカルについては同様ではありません。


13
これは、説明なしの受け入れられた回答の不適切な言い回しにすぎません。
高性能マーク

-5

アトミックは単一のステートメントのクリティカルセクションです。つまり、1つのステートメントの実行に対してロックします。

クリティカルセクションはコードブロックのロックです

優れたコンパイラは、最初のコードと同じ方法で2番目のコードを変換します


それは間違っています。わからないことは話さないでください。
jcsahnwaldt Reinstate Monica
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.