【发布时间】:2018-06-27 06:17:21
【问题描述】:
首先我想列出我对此的一些理解,如果我错了,请纠正我。
- x86 中的
MFENCE可以确保完全屏障 -
Sequential-Consistency 防止 STORE-STORE、STORE-LOAD、LOAD-STORE 和 LOAD-LOAD 重新排序
这是根据Wikipedia.
-
std::memory_order_seq_cst不保证防止 STORE-LOAD 重新排序。这是根据Alex's answer,“加载可能会与早期存储重新排序到不同的位置”(对于 x86)并且不会总是添加 mfence。
时表示顺序一致性std::memory_order_seq_cst是否表示顺序一致性?根据第 2/3 点,这对我来说似乎不正确。std::memory_order_seq_cst仅在- 至少一个明确的
MFENCE添加到LOAD或STORE - LOAD(无围栏)和 LOCK XCHG
- LOCK XADD (0) 和 STORE(无栅栏)
否则仍有可能重新订购。
根据@LWimsey 的评论,我这里弄错了,如果
LOAD和STORE都是memory_order_seq_cst,则没有重新排序。 Alex 可能会指出使用非原子或非 SC 的情况。 - 至少一个明确的
-
std::atomic_thread_fence(memory_order_seq_cst)总是生成一个完整的屏障这是根据Alex's answer。所以我总是可以用
std::atomic_thread_fence(memory_order_seq_cst)替换asm volatile("mfence" ::: "memory")这对我来说很奇怪,因为
memory_order_seq_cst在原子函数和栅栏函数之间的用法似乎有很大不同。
现在我来看看MSVC 2015标准库头文件中的这段代码,它实现了std::atomic_thread_fence
inline void _Atomic_thread_fence(memory_order _Order)
{ /* force memory visibility and inhibit compiler reordering */
#if defined(_M_ARM) || defined(_M_ARM64)
if (_Order != memory_order_relaxed)
{
_Memory_barrier();
}
#else
_Compiler_barrier();
if (_Order == memory_order_seq_cst)
{ /* force visibility */
static _Uint4_t _Guard;
_Atomic_exchange_4(&_Guard, 0, memory_order_seq_cst);
_Compiler_barrier();
}
#endif
}
所以我的主要问题是_Atomic_exchange_4(&_Guard, 0, memory_order_seq_cst); 如何创建一个完整的屏障MFENCE,或者实际上做了什么来启用像MFENCE 这样的等效机制,因为_Compiler_barrier() 显然不足以满足完整的记忆障碍,还是这个说法有点类似于第 3 点?
【问题讨论】:
-
关于您的第 3 点“std::memory_order_seq_cst 不保证防止 STORE-LOAD 重新排序”.. 它确实保证了这一点,但前提是这两个操作都被标记为这样.
-
@LWimsey 你的意思是如果我使用
atomic_store(memory_order_seq_cst )和atomic_load(memory_order_seq_cst ),就不会有重新排序。但是,如果我使用atomic_store(memory_order_release)和atomic_load(memory_order_acquire),那么我应该在其中任何一个上添加MFENCE,以避免STORE-LOAD 重新排序? -
是的,如果您在
store和load上都使用seq_cst,则所有线程将按此顺序观察这两个操作。在两者之间插入atomic_thread_fence(seq_cst)也是如此(您可以/不应该真正插入MFENCE,将其留给编译器)。 -
@calvin 这实际上取决于您是否谈论相同的内存位置。如果您执行
x.store(1, memory_order_release); x.load(memory_order_acquire);,则不需要栅栏(尽管这样的构造很值得怀疑,因此您可能意味着它们位于不同的内存位置)。 -
@LWimsey 1) 所有线程?哪些线程? 2)什么和什么之间的围栏?其他线程必须使用栅栏吗?
标签: c++ x86 memory-barriers stdatomic