メモリーバリア
読み:メモリーバリア
外語:memory barrier
メモリー操作をする命令の実行順序を維持する、プロセッサーの機能。メモリーフェンスとも。
概要
現在のマイクロプロセッサーは、高速処理のための最適化として、アウトオブオーダー実行を導入するものが多い。この時、命令は並び替えられて処理されるが、この命令にはメモリーのロード命令やストア命令も含まれる。
この命令の入れ替えは、単一のスレッド内では破綻が生じないように動作するが、マルチスレッド環境では支障が出ることがある。そのような命令があった場合は、それらの命令の影響が全て完了した後で、後続の命令が実行されることを保証する動作のシーケンスが必要となる。
そこでこのような場合は、手動で(プログラマーによるプログラムの記述によって)アウトオブオーダー実行を一時的に抑止する必要があり、このような目的のための動作をメモリーバリアという。
実装
x86
専用命令
x86の場合は、次の3命令が用意されている。
lfence (0F AE /5)
mfence (0F AE /6)
sfence (0F AE /7)
なお、lfenceは50サイクル強、sfenceは50サイクル弱程度の実行レイテンシーがあるが、mfenceは130サイクル程度を要し、非常に遅い命令の一つである。
- lfence命令をプログラムに挿入すると、lfence命令より後ろのロード命令が、lfence命令より先に実行されることを防ぐ
- sfence命令をプログラムに挿入すると、sfence命令より前のストア操作が完了するのを待つ
- mfence命令は、lfenceとsfenceを足したもの
例えば変数への代入を終えたあとフラグをセットする処理と、フラグがセットされたら変数を読みだして処理する処理があり、マルチスレッドで動作していたとする。この時、フラグのセットは非常に重要である。
フラグをセットする側の処理では、変数への代入とフラグのセットの間にsfenceを置き、確実に書き込まれてからフラグがセットされるようにする。
フラグを確認して処理する側では、フラグ確認と後の処理の間にlfenceを置き、フラグの確認が済む前に後の処理が実行されないようにする。
代用方法
x86/x64アーキテクチャーにおいては、メモリーアクセスの順序保証が必要な場合でも、殆どの場合は明示的なメモリーバリア命令を使わずに済む。
C++のstd::atomicの実装においても、基本的には単純なmov命令を発行するだけでよい。ただしstd::memory_order_seq_cstだけはxchg命令か、movの後でmfence命令が必要になる。
ARM
ARMでは、ISB、DSB、DMBの各命令がある。
- ISB ‐ 命令同期バリア
- DSB ‐ データ同期バリア
- DMB ‐ データメモリーバリア
ARMの処理系では、例えば、次のような組み込み関数が用意される。動作は、単にDMB/DSB/ISB命令を発行するだけである。
- __ISB(void)
- __DSB(void)
- __DMB(void)
また、より新しいバージョンのARM用C/C++コンパイラーでは、内部的にはchar型の引数を取る、次のような組み込み関数がある。
- __isb(<option>)
- __dsb(<option>)
- __dmb(<option>)
互換性のため、次のようなdefine文が定義されている。
#define __ISB() __isb(0xf);
#define __DSB() __dsb(0xf);
#define __DMB() __dmb(0xf);
この引数付きの組み込み関数のパラメータは、バリア命令にある4ビットのオプションフィールドにあてられ、オプションで処理中の制限を定義するために使われる。
命令のオペコードは次の通りである(x=option)。
- ISB ‐ F57FF06x / F3BF 8F6x
- DSB ‐ F57FF04x / F3BF 8F4x
- DMB ‐ F57FF05x / F3BF 8F5x
再検索