c++ - 是否有一个真正有效的示例显示 x86_64 上 Store-Load 重新排序的副作用?

标签 c++ multithreading c++11 concurrency x86-64

  • 众所周知,在 x86_64 上可以进行存储-加载重新排序,前提是存储和加载之间没有MFENCE

Intel® 64 and IA-32 Architectures

8.2.3.4 Loads May Be Reordered with Earlier Stores to Different Locations

  • 众所周知,在这样的示例中可以是存储加载重新排序

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-Load 重新排序的副作用?

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

或者在 x86_64 上是否允许 Store-Load 重新排序,因为它无法在程序中检测和显示?

最佳答案

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

首先,我们严格证明我们代码的正确性。然后在这段代码中,我们将移除 STORE 和 LOAD 之间的 mfence 屏障,然后看到算法失效。

有自定义锁(自旋锁),它在没有 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);
    }
};

  1. 首先我们严格证明算法的正确性,具有acquire-release-semantic:

enter image description here


  1. 然后我们将展示如何阻止我们的锁定算法 - 结果应为:20000:

C++ 差异:

enter image description here


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

Asm x86_64 差异:

enter image description here

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

此外,至少有一个 Store-Load 重新排序的示例 - 有点像我们的示例:Can x86 reorder a narrow store with a wider load that fully contains it?

关于c++ - 是否有一个真正有效的示例显示 x86_64 上 Store-Load 重新排序的副作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42909137/

相关文章:

c++ - 对齐 QCheckbox 文本和它下面的 QLabel

c++ - 使用IAudioSessionManager时出现链接错误

c++ - 使用std::vector创建自定义复数类的vector

c++ - 为什么在成员函数上为 *this 指定左值引用与不指定任何内容不同?

c++ - 在具有菱形继承和虚拟基类的类中调用函数的策略

C++ - 引用,参数中的指针

c++ - 导出为 PDF 时出错(RPC 服务器不可用)

java - 当在同步块(synchronized block)中使用wait()方法时,JVM在等待notify()时会释放监视器吗?

python - 在 python 中结束线程的生命?

multithreading - 是否因为 “lock”而没有使用 “cache coherency mechanism”操作码前缀?