なアセンブラ命令を常に原子的に実行?
-
11-09-2019 - |
質問
今日かこの質問:
いてコード
static int counter = 0;
void worker() {
for (int i = 1; i <= 10; i++)
counter++;
}
の場合 worker
いう二つの異なるスレッド、どのような価値を持ち得るの counter
てやっていたが完了した?
知っている際でも、それは何かに使えます。私内にはガッツから、 counter++
がされるのはシングルアセンブラ命令の場合は両方のスレッドの実行と同じコア counter
20.
だがそのスレッドは異なるコアプロセッサがあり、レースの条件をmicrocode?はアセンブラ命令が常に見ることとして原子間に働きかけているのか?
解決
具体的にはためのx86、に関する例: counter++
, 多くの方法で取りまとめを行いました。最も些細な例です:
inc counter
これは、以下のマイクロ操作
- 負荷
counter
非登録のCPU - 増加の登録
- 店舗の更新登録
counter
これは本質的に同一として:
mov eax, counter
inc eax
mov counter, eax
った場合、その他の剤更新 counter
との間に負荷さんには反映され counter
後のお店です。このエージェントが別のスレッドと同じコア、その一方で、もうひとつのコアのCPU、別のCPUを同じシステム、外部剤を使用するDMA(Directメモリアクセス)などです。
したい場合に保証するこ inc
は原子を使用 lock
接頭辞:
lock inc counter
lock
保証することもできる更新 counter
の負荷のお店です。
に関する複雑な指示は、通常ではないと彼らの実行を原子的な支援を lock
接頭辞です。
他のヒント
答えは次のとおりです!それが依存
ここでは、アセンブラ命令が何であるか、周りにいくつかの混乱です。通常、1つのアセンブラ命令は、1つのマシン命令に変換されます。あなたがマクロを使用する場合excemptionがある - しかし、あなたはそれを認識しておく必要があります。
。問題はつまるところ、言ったことは、原子1つの機械命令で?
古き良き時代には、それがでした。しかし、今日、複雑なCPUを、実行時間の長い命令、ハイパースレッディングで、...そうではありません。 をいくつかのCPU保証いくつかののインクリメント/デクリメント命令はアトミックです。その理由は、彼らは非常に単純なsyncronizingのためのきちんとしていること、である。
また、いくつかのCPUコマンドはそれほど問題ではありません。あなたがフェッチ(プロセッサが一体に取り出すことができ、データの一片の)シンプルを持っている場合 - すべてで分割することは何もないので、それ自体がアトミックもちろんですフェッチ。あなたが整列していないデータを持っている場合しかし、それは再び複雑になります。
答えは:それは依存しています。慎重ベンダーの機械の取扱説明書をお読みください。疑いで、そうではありません!
編集: ああ、私はそれが今、あなたはまた、カウンタ++をお願いしました。 「翻訳される可能性が最も高い」文はまったく信頼することはできません。これは主にコースのコンパイラにも依存します!コンパイラは異なる最適化を行っているとき、それはより困難なります。
の常にのない - 他人にそれはそうではないいくつかのアーキテクチャ上の1つのアセンブリ命令は、1つの機械コード命令に変換される。
。の他にの - あなたはを決してを使用しているプログラム言語が1つのアセンブリ命令へのコードの一見単純なラインをコンパイルしていると仮定することができます。また、いくつかのアーキテクチャ上で、あなたがアトミックに実行される1つの機械コードを負いかねます。
あなたがコーディングされている言語に依存して、代わりに適切な同期化技術を使用しています。
- 増分/減分)の運用の32ビット以下の整数変数を一つの32bitプロセッサーの無いハイパースレッディング技術は原子.
- 上のプロセッサのハイパースレッディング技術やマルチプロセッサシステムの増分/減分の業務を保証するものではありませんで実行されatomicaly.
ネイサンさんのコメントによって無効化: <ストライキ>私が正しく私のインテルのx86アセンブラを覚えていれば、INC命令は専用レジスタのために働くと直接メモリ位置のために動作しません。ストライキ>
<ストライキ> ストライキ><ストライキ>だから、カウンタは、++(ちょうどポストインクリメント部分を無視して)アセンブラで単一の命令ではないでしょう。ロードカウンタ変数バックカウンターに登録し、登録する登録インクリメント、ロードする:少なくとも3つの命令になります。そして、それは、x86アーキテクチャのためだけである。ストライキ>
要するに、それは言語仕様で指定し、使用しているコンパイラが仕様をサポートしていることをされていない限り、それは原子であることに依存しないでください。
はありませんあなたはこれを仮定することはできません。それは明らかにコンパイラの仕様書に記載がない限り。しかも誰もが確かに、原子その1つのアセンブラ命令を保証することはできません。 uop - 実際には、各アセンブラ命令は、マイクロコードオペレーションの数に変換されます。
また、競合状態の問題はしっかりとメモリモデルと結合される(シーケンシャルコヒーレンス、コヒーレンスおよびなどを解放する)、それぞれに対しての答えと結果が異なる可能性があります。
もう一つの問題は、揮発性として変数を宣言しない場合、生成されたコードは、おそらく唯一のメモリが更新されるループの最後で、すべてのループの繰り返しでメモリを更新しないだろうということです。
あなたはcounter++
が本当にアトミックマルチスレッドになりたい場合は、System.Threading.Interlocked.Increment(counter)
を使用することができます。
/ counter++
がアトミックであることができなかったか、なぜ多くの異なる方法での実際の情報のための他の回答を参照してください。 ; - )
ほとんどの場合には、いいえ。実際には、x86の上で、あなたは命令を実行することができます。
push [address]
これは、Cで、のようになります:
*stack-- = *address;
これは、1つの命令するでの二つのメモリ転送を行う。
これは、少なくとも、1つのクロックサイクルで行うことは基本的に不可能ではありませんので、の1 メモリ転送1サイクルでも可能ではありません!
は、他の多くのプロセッサでは、メモリシステムとプロセッサとの間の別離が大きいです。 (多くの場合、これらのプロセッサは、ARMおよびPowerPCなどのメモリ・システムに応じほとんど、またはビッグエンディアン可能)メモリシステムの読み取りおよび書き込みを並べ替えることができれば、これはまた、原子の挙動にも影響があります。
この目的のために、メモリバリアがある( http://en.wikipedia.org/wiki/ Memory_barrierする)
要するにので、アトミック命令が(関連するロック接頭辞)インテルに十分であるが、より多くのメモリI / Oが同じ順序でないかもしれないため、非インテルで行う必要があります。
これは、他のアーキテクチャにインテルから「ロックフリー」ソリューションを移植する既知の問題があります。
(x86でシステムはまた、少なくとも64ビット・モードでは、メモリバリアを必要とするように見える)はマルチプロセッサ(マルチコアないことに注意してください。
私はあなたがアクセスの競合状態を得るだろうと考えてます。
あなたはカウンタをインクリメントでアトミック操作を確保したい場合、あなたは++カウンタを使用する必要があると思います。