4

Windows では、メモリ バリアを実装するためのコンパイラ組み込み関数が 3 つあります。

1. _ReadBarrier;

2. _WriteBarrier;

3. _ReadWriteBarrier;

しかし、私は奇妙な問題を発見しました: _ReadBarrier は何もしないダミー関数のようです! 以下は、VC++ 2012 によって生成された私のアセンブリ コードです。

私の質問は次のとおりです。アセンブリ命令でメモリバリア機能を実装する方法は?

int main()
{   
013EEE10  push        ebp  
013EEE11  mov         ebp,esp  
013EEE13  sub         esp,0CCh  
013EEE19  push        ebx  
013EEE1A  push        esi  
013EEE1B  push        edi  
013EEE1C  lea         edi,[ebp-0CCh]  
013EEE22  mov         ecx,33h  
013EEE27  mov         eax,0CCCCCCCCh  
013EEE2C  rep stos    dword ptr es:[edi]  
    int n = 0;
013EEE2E  mov         dword ptr [n],0  
    n = n + 1;
013EEE35  mov         eax,dword ptr [n]  
013EEE38  add         eax,1  
013EEE3B  mov         dword ptr [n],eax  
    _ReadBarrier();
    n = n + 1;
013EEE3E  mov         eax,dword ptr [n]  
013EEE41  add         eax,1  
013EEE44  mov         dword ptr [n],eax 
}
013EEE56  xor         eax,eax  
013EEE58  pop         edi  
013EEE59  pop         esi  
013EEE5A  pop         ebx  
013EEE5B  add         esp,0CCh  
013EEE61  cmp         ebp,esp  
013EEE63  call        __RTC_CheckEsp (013EC3B0h)  
013EEE68  mov         esp,ebp  
013EEE6A  pop         ebp  
013EEE6B  ret 
4

3 に答える 3

9

_ReadBarrier_WriteBarrier、および_ReadWriteBarrier、コンパイラがコードを並べ替える方法に影響を与える組み込み関数です。これらは CPU メモリ バリアとはまったく関係がなく、特定の種類のメモリに対してのみ有効です (「影響を受けるメモリ」を参照)。

MemoryBarrier()は、CPU メモリ バリアを強制するために使用する組み込み関数です。ただし、Microsoft からの推奨事項は、std::atomic<T>今後 VC++ を使用することです。

于 2013-01-28T22:26:21.563 に答える
4

最新のプロセッサは、実際に命令を「完了する」場所よりもはるかに先に命令を実行することができるため、厳密な順序付けが必要な特定のタイプのメモリ操作に関しては、メモリ バリアを使用して、はるかに先に実行することを防ぎます。必須 - ほとんどの場合、変数 b の前に変数 a に書き込むか、a の前に b に書き込むかは実際には問題ではありません。しかし、時々そうです。

x86 命令セットにはlfence、 、sfenceおよびがありfence、これらはそれぞれ、ロード、ストア、およびすべてのメモリ操作を「フェンスイン」する命令です。「フェンス」または「バリア」命令に関するポイントは、バリア命令に先行するすべての命令が、バリアの後の次の命令が続行できるようになる前に、ロード、ストア、またはその両方を完了していることを確認することです。

たとえば、セマフォ、ミューテックス、または同様の命令を実装している場合、これは重要です。たとえば、他のデータの読み取りを続行する前に、「セマフォをロックしました」という値を格納することが重要だからです。そうしないと、問題が発生する可能性があります。

メモリ バリアを使って何をしているのかを本当に理解している場合を除き、メモリ バリアを使用しないことがおそらく最善であり、同じ問題を解決する既存のコードに依存することが、そのstd::atomicようなコードに資金を提供する 1 つの場所であることに注意してください。私はかなりの「トリッキーな」コードを書いてきましたが、コードにメモリ バリアが必要になったのは 1 度か 2 度だけです。

何度か、コンパイラがコードを広めないようにする必要がありました。これは、「no-op 関数」を使用して行うことができます。最近では、それを行うための特別な組み込み関数さえあるようです。

于 2013-01-28T23:25:58.073 に答える
0

考慮すべき重要な点がいくつかあります。おそらく 1 つ目は、バリアはマルチスレッド コードでのみ効果があり、ほとんどのコンパイラはマルチスレッド コードを生成するために特別なオプションを必要とすることです。また、次のようなもの_ReadBarrierはほぼ確実にコンパイラの組み込みであり、マルチスレッド コードのオプションを指定しない限り何もしないはずです。

2 つ目は、マルチスレッドのコンテキストであっても、ハードウェアに必要なものはさまざまであることです。私が (約 40 年以上) 扱ってきたほとんどのマシンでは、マシンは何も必要としませんでした。バリアは、マシンに洗練された読み取りおよび書き込みパイプラインがある場合にのみ関連します。(以前のほとんどのマシンには、フェンスやバリアの命令さえなかったので、生成されたコードは空でなければなりませんでした。)

于 2013-01-28T22:21:33.020 に答える