前回のvolatileの変数を同期プリミティブに、な話の第2回です。
Microsoftの文章では、AcquireバリアはRead-Acquireバリア、ReleaseバリアはWrite-Releaseバリアと表現されています。確かにメモリバリアが使用される状況を考えると、アトミック変数代入時にはすなわち書き込みなので、Write-Releaseバリアという表現であってると思います。
Interlocked関数群をWindows環境において使用している状況では、これらの関数群がRead-Acquireバリア/Write-Releaseバリアを備えていると考えてよいとのことです。一方組み込み向けのこれらの関数ではバリアを備えない記述がみられます.
バリアが必要なのは、コンパイラやCPUが結果が変わらない範囲において、命令実行順序を移動するからという点にあります。これがマルチコア(マルチスレッド)が必須となっている昨今では大きな問題になっています。
具体的に x86/x64のアーキテクチャでは、この実行順序の移動について調べてみると、アウトオブオーダーなのに思った以上に順序の変更をしないようです。PowerPCアーキテクチャでは逆で結構強引に実行順序の入れ替わりが起こるようです。
x86/x64環境で起こる実行順序の入れ替わりは、“読み取りを書き込みの先に移動する “というケースのようです。x86/x64のPC環境では MemoryBarrier() という関数があり、これを実行することでメモリバリアをはることができます。
一方でコンパイラの最適化によるメモリ読み書きバリアとしては、_ReadBarrier(), _WriteBarrier(), _ReadWriteBarrier() があります。これはコンパイラに対しての指示となっています。ただこの_ReadBarrier命令は日本語のMSDNヘルプを見ると、メモリバリアとして機能しそうな訳になっていますが、英語のMSDNを見ると単にコンパイラへの命令であるという点がきちんと書かれています。
日本語訳だとこんな感じでした。
_ReadWriteBarrier は、次に続くメモリ アクセスが開始される前に、前のすべてのメモリ アクセスを強制的に完了させます。
_WriteBarrier は、次に続く書き込み処理が開始される前に、前のすべてのメモリの書き込み処理を強制的に完了させます。
_ReadBarrier は、次に続く読み取り処理が開始される前に、前のすべてのメモリの読み取り処理を強制的に完了させます。
英語の文章では、次のように書かれていました。
The _ReadBarrier, _WriteBarrier, and _ReadWriteBarrier compiler intrinsics prevent only compiler re-ordering.
To prevent the CPU from re-ordering read and write operations, use the MemoryBarrier macro