MFENCE
。Intel® 64 and IA-32 Architectures
8.2.3.4负载可能会与较早的商店重新排序到不同的位置
c.store(relaxed)
< - > b.load(seq_cst)
:https://stackoverflow.com/a/42857017/1558037
// Atomic load-store
void test() {
std::atomic<int> b, c;
c.store(4, std::memory_order_relaxed); // movl 4,[c];
int tmp = b.load(std::memory_order_seq_cst); // movl [b],[tmp];
}
可以重新排序:
// Atomic load-store
void test() {
std::atomic<int> b, c;
int tmp = b.load(std::memory_order_seq_cst); // movl [b],[tmp];
c.store(4, std::memory_order_relaxed); // movl 4,[c];
}
因为x86_64上没有MFENCE
:
但有没有一个真正有效的例子显示了商店负载重新排序对x86_64的副作用?
示例,在使用Store(seq_cst), Load(seq_cst)
时显示正确的结果,但在使用Store(relaxed), Load(seq_cst)
时显示错误的结果。
或者x86_64上是否允许存储加载重新排序,因为它无法检测到并显示在程序中?
答案 0 :(得分:1)
是的,在C ++ 11和x86_64上有一个Store-Load重新排序的例子。
首先,我们严格证明代码的正确性。然后在这段代码中,我们将删除STORE和LOAD之间的mfence
障碍,并看到算法崩溃。
有自定义锁定(自旋锁定),没有CAS / RMW操作,只有Load&amp;存储有限数量的线程,其中每个线程编号为0-4:
// example of Store-Load reordering if used: store(release)
struct lock_t {
static const size_t max_locks = 5;
std::atomic<int> locks[max_locks];
bool lock(size_t const thread_id) {
locks[thread_id].store(1, std::memory_order_seq_cst); // Store
// store(seq_cst): mov; mfence;
// store(release): mov;
for (size_t i = 0; i < max_locks; ++i)
if (locks[i].load(std::memory_order_seq_cst) > 0 && i != thread_id) { // Load
locks[thread_id].store(0, std::memory_order_release); // undo lock
return false;
}
return true;
}
void unlock(size_t const thread_id) {
locks[thread_id].store(0, std::memory_order_release);
}
};
acquire-release
- 语义:然后我们将展示如何制定锁定算法 - 结果应为:20000 :
C ++ diff:
然后我们展示了汇编代码之间的区别:
mfence
):https://godbolt.org/g/WrCiyW mfence
):https://godbolt.org/g/Eo3TXR Asm x86_64 diff:
因为严格证明“好”算法是正确的。因为我们发现“坏”算法无法正常工作(结果19976不等于20000)。它们之间的唯一区别是 - STORE和LOAD之间的障碍mfence
。因此,我们提供了发生Store-Load重新排序的算法。
此外,至少有一个Store-Load重新排序示例 - 有点像我们的示例:Can x86 reorder a narrow store with a wider load that fully contains it?
答案 1 :(得分:0)
编译器不会在std::memory_order_seq_cst
操作周围重新排序加载和存储。
CPU可能会对这些进行重新排序,因为存储和加载之间没有依赖关系。换句话说,商店可能在加载后完成。但是,没有办法观察差异,因为负载没有副作用。