是否有一个真正有效的示例来显示 x86_64 上存储加载重新排序的副作用?

Ale*_*lex 3 c++ concurrency multithreading x86-64 c++11

    \n
  • 众所周知,在 x86_64 上,如果 Store 和 Load 之间没有,则可以进行 Store-Load 重新排序MFENCE
  • \n
\n\n

Intel\xc2\xae 64 和 IA-32 架构

\n\n
\n

8.2.3.4 可以将早期存储的负载重新排序到不同位置

\n
\n\n
    \n
  • 还已知,在这样的示例中可以是存储加载重新排序
  • \n
\n\n

c.store(relaxed)<--> b.load(seq_cst)https ://stackoverflow.com/a/42857017/1558037

\n\n
// Atomic load-store\nvoid test() {\n    std::atomic<int> b, c;\n    c.store(4, std::memory_order_relaxed);          // movl 4,[c];\n    int tmp = b.load(std::memory_order_seq_cst);    // movl [b],[tmp];\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

可以重新排序为:

\n\n
// Atomic load-store\nvoid test() {\n    std::atomic<int> b, c;\n    int tmp = b.load(std::memory_order_seq_cst);    // movl [b],[tmp];\n    c.store(4, std::memory_order_relaxed);          // movl 4,[c];\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

MFENCE因为, x86_64 上没有:

\n\n\n\n
\n\n

但是是否有一个真正有效的示例可以显示 x86_64 上存储加载重新排序的副作用?

\n\n

例如,使用时显示正确的结果Store(seq_cst), Load(seq_cst),但使用时显示错误的结果Store(relaxed), Load(seq_cst)

\n\n

或者 x86_64 上是否允许存储加载重新排序,因为它无法在程序中检测和显示?

\n

Ale*_*lex 5

是的,有 C++11 和 x86_64 上存储加载重新排序的示例。

首先,我们严格证明我们代码的正确性。然后在这段代码中,我们将移除mfenceSTORE 和 LOAD 之间的障碍,并看到算法崩溃了。

有一个自定义锁(自旋锁),其实现无需 CAS/RMW 操作,仅对有限数量的线程进行加载和存储,其中每个线程编号 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);
    }
};
Run Code Online (Sandbox Code Playgroud)
  1. 首先我们严格证明算法的正确性,具有acquire-release-语义:

在此输入图像描述


  1. 然后我们将展示如何分解我们的锁定算法 -结果应该是: 20000

C++ 差异:

在此输入图像描述


  1. 然后我们展示一下汇编代码的区别:

Asm x86_64 差异:

在此输入图像描述

因为它是经过严格证明的,一个“好”的算法是正确的。因为我们看到“坏”算法无法正常工作(结果 19976 不等于 20000)。mfence它们之间唯一的区别是 - STORE 和 LOAD 之间的障碍。因此,我们提供了发生存储加载重新排序的算法。

另外,至少有一个存储-加载重新排序的示例 -这有点像我们的示例x86 可以使用完全包含它的更宽的加载来重新排序一个狭窄的存储吗?