注:私はこのトピックの専門家ではありません。私の発言の一部は 「インターネットで聞いたこと」ですが、それでもいくつかの誤解を解くことができると思います。
[編集]一般に、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、間違っている場合は訂正してください。