メモリバリアを理解するために必要な3つのこと
Togetter - 「メモリバリアとガチャピン先生」
上の話において、「メモリバリア」の意味するところをもう少し明確にすべきかなあと思ったので、簡単にまとめてみます。
「メモリバリア」が満たすべき性質は、以下の3つに分類することができます。
- atomicity
- release/acquire fence
- sequential consistency
これらの性質は、C++0xのmemory_orderと以下のように対応しています。
C++0x memory_order | 持っている性質 |
---|---|
memory_order_relaxed | atomicity |
memory_order_acquire, memory_order_release, memory_order_acq_rel | atomicity + release/acquire fence |
memory_order_seq_cst | atomicity + release/acquire fence + sequential consistency |
atomicity とは文字通り「不可分な」操作を保証するものですが、その影響は操作対象のオブジェクトに限定されます。つまり、あるatomic変数に対する memory_order_relaxed でのアクセスは、他の変数に対するアクセスの順序付けには何も影響しません。
一方 release/acquire fence では、release操作とacquire操作の対によってアクセスの順序付けを行ないます。この順序付けの効果は、releaseより前とacquireより後にある全ての変数に対して影響します。
そして、この release/acquire fence を用いても実装できない、「複数のatomic変数に対する書き込みの見た目の順序に依存するようなアルゴリズム」を実装するために必要なのが sequential consistency になります。
これら3つの性質について、x86アーキテクチャでの実現方法は以下のようになります。
単純なread, write | read-modify-write | |
---|---|---|
atomicity | 対象の変数が正しくalignされていればよい | lock-prefix付き命令を用いる |
release/acquire fence | 自動的に保証される | 自動的に保証される |
sequential consistency | writeを XCHG または MOV+MFENCE にする | lock-prefix付き命令を用いる |
このように、CMPXCHGやXADDなどのread-modify-write命令では atomicity の実現のためだけでも lock-prefix が必要となりますが、その結果 sequential consistency までもオマケに付いてくることになるわけです。