从硬件角度看内存排序

ust*_*ion 5 c++ multithreading atomic memory-barriers stdatomic

我想在阅读了网上的一些材料后,我在某种程度上理解了内存排序保证的各个方面。然而,仅从软件和理论的角度来看规则似乎有点神奇。这里解释了为什么两个处理器似乎会重新排序的一个例子,它对我实际可视化该过程有很大帮​​助。所以我的理解是,预取器可以为一个处理器提前加载读取,而不会为另一个处理器加载读取,然后对于外部观察者来说,第一个处理器看起来比第二个处理器更早读取(并且现在可能有可能)在没有同步的情况下具有陈旧的价值),因此会看到指令重新排序。

之后,我实际上从 CPU 的角度寻找更多关于如何产生这种效果的解释。例如,考虑acquire-release栅栏。通常引用的一个经典示例如下:

thread-0: x.store(true,std::memory_order_release);
thread-1: y.store(true,std::memory_order_release);

thread-2:
while(!x.load(std::memory_order_acquire));
if(y.load(std::memory_order_acquire)) ++z;

thread-3:
while(!y.load(std::memory_order_acquire));
if(x.load(std::memory_order_acquire)) ++z;
Run Code Online (Sandbox Code Playgroud)

由于顺序一致性中不存在全序,因此线程 2 可以看到线程 0 首先执行其操作,然后是线程 1,而线程 3 可以看到线程 1 首先执行其操作,然后是线程 0。这z==0可能是一个可能的结果。

如果有一个解释(比如使用四个 cpu,每个 cpu 运行上面的一个线程)以及硬件中会发生什么让我们看到这种重新排序,那将非常有帮助。它不一定是非常复杂的现实世界详细案例(如果这是理解它的唯一方法,也可以是这样)。只是像上面链接的答案所做的那样的近似值,加上有关缓存(或任何参与因素)的内容,我想它应该为我(可能还有许多其他人?)做这件事。

另一种是:

thread-0:
x.store(true,std::memory_order_relaxed);
y.store(true,std::memory_order_release);

thread-1:
while(!y.load(std::memory_order_acquire)); // <------ (1)
if(x.load(std::memory_order_relaxed)) ++z;
Run Code Online (Sandbox Code Playgroud)

再次遵循规则,我可以理解这永远不会得到z==0(假设所有初始值都是 0)以及为什么更改(1)relaxed可能会得到我们z==0。但它再次显得有点神奇,直到我能想到它是如何在物理上发生的。

因此,任何使用足够数量的处理器及其缓存等进行解释的帮助(或指针)都将是巨大的。