Pebble's Diary

プログラマーの作業メモ

std::atomic における memory_order_relaxed の動作(C++11)

memory_order_relaxedの使い方がいまいち分からなかったのだが、やっと理解できたので、それついて書いておく。

memory_order_relaxedの説明を探すと以下が見つかる。

  • Each memory location has a total modification order

  • Memory operations performed by the same thread on the same memory location are not reordered with respect to the modification order.

  • Operations on the same variable within a single thread still obey happens-before relationships

2番目の説明が最も判りやすい。 コードで説明すると、

// スレッドA
std::atomic<int> val;
val.store(1, std::memory_order_relaxed); // M1
val.load(std::memory_order_relaxed); // M2

上記をスレッドAで実行した時、M1,M2の順番で実行されることが保証されるということである。
これがつまり、
「同じスレッドから同じメモリロケーションに対するメモリ操作が変更の順番に対して順序が変わることがない。」
の意味である。
「そんなの当たり前じゃないの?」という人は、以下のケースを考えてみよう。

// スレッドA
std::atomic<int> val1;
std::atomic<int> val2;
val1.store(1, std::memory_order_relaxed); // M1
val2.store(1, std::memory_order_relaxed); // M2

この場合、M1,M2の順番で実行しても、M2,M1の順番で実行してもどちらでも良いことになっている。
びっくりするだろう?俺もさ。。
同じメモリロケーションではないから、処理の順番が変わろうがその方がCPUが早く動作できて全体のパフォーマンスが上がる可能性があるっていうんだがら、
それでいいじゃないかというわけである。恐るべきC++仕様である。

他のメモリオーダーはすべてstd::memory_order_relaxedよりも強い条件なので、他のメモリオーダーを指定した場合も、すべてこの動作をすると考えてよいことになる。
つまり、以下のように書いた場合は、M1,M2の順番で実行されるのが保証されるということである。

// スレッドA
std::atomic<int> val;
val.load(std::memory_order_relaxed); // M1
val.store(1, std::memory_order_release); // M2