5

プレーン ロードは x86 で取得セマンティクスを持ち、プレーン ストアはリリース セマンティクスを持ちますが、コンパイラは命令を並べ替えることができます。フェンスとロックされた命令 (ロックされた xchg、ロックされた cmpxchg) は、ハードウェアとコンパイラの両方の並べ替えを防ぎますが、コンパイラのバリアで保護するために、プレーンなロードとストアは依然として必要です。Visual C++ には、コンパイラの並べ替えを防ぐ _ReadWriterBarrier() 関数が用意されています。また、C++ には、同じ理由で volatile キーワードが用意されています。私は、すべてが正しいことを確認するためだけに、このすべての情報を書きます。上に書かれていることはすべて真実です。_ReadWriteBarrier() で保護された関数で使用される揮発性変数としてマークする理由はありますか?

例えば:

int load(int& var)
{
    _ReadWriteBarrier();
    T value = var;
    _ReadWriteBarrier();
    return value;
}

その変数を非揮発性にしても安全ですか? 私が理解している限り、関数は保護されており、内部のコンパイラーによって並べ替えを行うことができないためです。一方、Visual C++ は volatile 変数に特別な動作を提供し (標準が行うものとは異なります)、揮発性の読み取りと書き込みをアトミックなロードとストアにしますが、私のターゲットは x86 であり、単純なロードとストアは x86 でアトミックであると想定されています。とにかく、ですよね?

前もって感謝します。

4

2 に答える 2

2

Volatile キーワードは C でも使用できます。「揮発性」は組み込みシステムでよく使用されます。特に、変数の値がいつでも変更される可能性がある場合、コードによってアクションが実行されることはありません。3 つの一般的なシナリオには、メモリ マップされたペリフェラル レジスタまたはグローバル変数からの読み取りが含まれます。割り込みサービス ルーチンまたはマルチスレッド プログラム内のルーチン。

したがって、volatile が _ReadWriteBarrier に似ていると見なされる最後のシナリオです。

_ReadWriteBarrier は関数ではありません。_ReadWriteBarrier は追加の命令を挿入せず、CPU による読み取りと書き込みの再配置を妨げません。コンパイラによる再配置を防止するだけです。_ReadWriteBarrier は、コンパイラの並べ替えを防ぐためのものです。

MemoryBarrier は、CPU の並べ替えを防ぐためのものです!

コンパイラは通常、命令を再配置します... C++ にはマルチスレッド プログラムの組み込みサポートが含まれていないため、コンパイラはコードを並べ替えるときにコードがシングル スレッドであると想定します。MSVC では、コード内で _ReadWriteBarrier を使用して、コンパイラが読み取りと書き込みを移動しないようにします。

これらのトピックに関する詳細な議論については、このリンクを確認して ください http://msdn.microsoft.com/en-us/library/ee418650(v=vs.85).aspx

コード スニペットに関して - ReadWriteBarrier を SYNC プリミティブとして使用する必要はありません - _ReadWriteBarrier への最初の呼び出しは必要ありません。

ReadWriteBarrier を使用する場合、volatile を使用する必要はありません

あなたは「それはアトミックなロードとストアを揮発性の読み取りと書き込みを行います」と書きました-それを言っても大丈夫だとは思いません。原子性と揮発性は異なります。アトミック操作は分割できないと見なされます - ... http://www.yoda.arachsys.com/csharp/threads/volatility.shtml

于 2011-02-12T10:12:53.030 に答える
1

注:私はこのトピックの専門家ではありません。私の発言の一部 「インターネットで聞いたこと」ですが、それでもいくつかの誤解を解くことができると思います。

[編集]一般に、x86アトミック読み取りやOOOXの欠如などのプラットフォーム固有の機能は#ifdef、ターゲットプラットフォームのチェックによって保護され、理想的にはパス内のポータブルソリューションを伴う、分離されたローカル最適化にのみ依存し#elseます。

注意点

  • 読み取り/書き込み操作の原子性
  • コンパイラの最適化による並べ替え(これには、単純なレジスタキャッシングのために別のスレッドで見られる異なる順序が含まれます)
  • CPUでのアウトオブオーダー実行

考えられる誤解

1. 私が理解している限りでは、関数は保護されており、内部のコンパイラーによって並べ替えを行うことはできません。
[編集]明確にするために:は_ReadWriteBarrier命令の並べ替えに対する保護を提供しますが、関数の範囲を超えて調べる必要があります。_ReadWriteBarrierこれを行うためにVS2010で修正されましたが、以前のバージョンは壊れている可能性があります(実際に行う最適化によって異なります)。

最適化は関数に限定されません。関数やコンパイル単位にまたがる複数のメカニズム(自動インライン化、リンク時コード生成)があります(そして、スコープの小さいレジスターキャッシングよりもはるかに重要な最適化を提供できます)。

2. Visual C ++ [...]は、アトミックなロードとストアを揮発性の読み取りと書き込み
で作成 します。どこでそれを見つけましたか?MSDNによると、標準を超えると、読み取りと書き込みの周りにメモリバリアが配置され、アトミック読み取りは保証されません。

[編集] C#、Java、Delphiなどは異なるメモリmdoelsを持っており、異なる保証を行う可能性があることに注意してください。

3. とにかく、プレーンなロードとストアはx86ではアトミックであるはずですよね?
いいえそうではありません。アラインされていない読み取りはアトミックではありません。それらがうまく整列している場合、それらはたまたまアトミックです-それが分離されて簡単に交換されない限り、私は信頼しません。そうしないと、「simplificaitonfox86」がそのターゲットへのロックダウンになります。

[編集]整列されていない読み取りが発生します:

char * c = new char[sizeof(int)+1];
load(*(int *)c);      // allowed by standard to be unaligned
load(*(int *)(c+1));  // unaligned with most allocators

#pragma pack(push,1)
struct 
{
   char c;
   int  i;
} foo;
load(foo.i);         // caller said so
#pragma pack(pop)

もちろん、パラメータを調整する必要があり、すべてのコードを制御する必要があることを覚えていれば、これはすべてアカデミックです。私は過去の怠惰にしばしば噛まれてきたので、私はもうそのようなコードを書くことはありません。

4. プレーンロードにはx86での取得セマンティクスがあり、プレーンストアにはリリースセマンティクスが
あります。x86プロセッサはアウトオブオーダー実行を使用しません(つまり、目に見えるOOOXはありません-私は思います)が、これはオプティマイザの並べ替えを停止しません指示。

5. _ReadBarrier / _WriteBarrier / _ReadWriteBarrierは、すべての魔法を実行しません。それらは実行しません。オプティマイザーによる並べ替えを防ぐだけです。MSDNはついに、VS2010に対して大きな悪い警告を出しましたが、この情報は以前のバージョンにも当てはまるようです。


さて、あなたの質問に。

スニペットの目的は、任意の変数Nを渡し、それを(原子的に?)ロードすることだと思います。簡単な選択は、インターロック読み取りまたは(Visual C ++ 2005以降では)揮発性読み取りです。

そうしないと、読み取りの前にコンパイラとCPUの両方にバリアが必要になります。VC++パーラーでは、次のようになります。

int load(int& var)
{   
  // force Optimizer to complete all memory writes:
  // (Note that this had issues before VC++ 2010)
   _WriteBarrier();    

  // force CPU to settle all pending read/writes, and not to start new ones:
   MemoryBarrier();

   // now, read.
   int value = var;    
   return value;
}

MSDNで2番目の警告があります:* Visual C ++コンパイラの過去のバージョンでは、_ReadWriteBarrier関数と_WriteBarrier_WriteBarrier関数はローカルでのみ適用され、呼び出しツリーの上の関数には影響しませんでした。これらの機能は、コールツリーの最後まで適用されるようになりました。*


それが正しいことを願っています。stackoverflowers、間違っている場合は訂正してください。

于 2011-02-12T08:56:43.720 に答える