我们知道两条指令可以通过 OoOE processor 重新排序。例如,在不同的线程之间共享两个全局变量。

int data;
bool ready;

写入器线程生成 data 并打开标志 ready 以允许读取器使用该数据。
data = 6;
ready = true;

现在,在 OoOE 处理器上,这两条指令可以重新排序(指令获取、执行)。但是结果的最终提交/写回呢?即,商店会按顺序排列吗?

据我所知,这完全取决于处理器的内存模型。例如,x86/64 具有强大的内存模型,并且不允许重新排序存储。相反,ARM 通常有一个弱模型,其中可能发生存储重新排序(以及其他几个重新排序)。

此外,直觉告诉我我是对的,否则我们将不需要在典型的多线程程序中使用的这两条指令之间的存储屏障。

但是,这是我们的 wikipedia 所说的:



我很困惑。是说必须按顺序写回结果吗?真的,在 OoOE 处理器中,可以将存储到 dataready 重新排序吗?

最佳答案

架构的一致性模型(或内存模型)决定了哪些内存操作可以重新排序。我们的想法始终是从代码中获得最佳性能,同时保留程序员所期望的语义。这就是维基百科的观点,内存操作对程序员来说是按顺序出现的,即使它们可能已经重新排序。当代码是单线程时,重新排序通常是安全的,因为处理器可以很容易地检测到潜在的违规行为。

在 x86 上,常见模型是写入不会与其他写入重新排序。然而,处理器正在使用乱序执行 (OoOE),因此指令会不断地重新排序。通常,处理器有几个额外的硬件结构来支持 OoOE,例如重新排序缓冲区和加载存储队列。重新排序缓冲区确保所有指令都按顺序执行,这样中断和异常就会中断程序中的特定点。加载存储队列的功能类似,可以根据内存模型恢复内存操作的顺序。加载存储队列还消除了地址的歧义,以便处理器可以识别何时对相同或不同的地址进行操作。

回到 OoOE,处理器在每个周期执行 10 到 100 条指令。加载和存储正在计算它们的地址等。处理器可能会为访问预取缓存行(可能包括缓存一致性),但在安全之前,它实际上无法访问该行以进行读取或写入(根据内存模型) ) 这样做。

插入存储屏障、内存栅栏等告诉编译器和处理器有关重新排序内存操作的进一步限制。编译器是实现内存模型的一部分,因为某些语言(如 java)具有特定的内存模型,而其他语言(如 C)则遵守“内存访问应该按顺序执行”。

总之,是的,数据和就绪可以在 OoOE 中重新排序。但这取决于内存模型是否真的存在。因此,如果您需要特定的顺序,请使用屏障等提供适当的指示,以便编译器、处理器等不会选择不同的顺序以获得更高的性能。

关于c++ - 内存存储真的可以在 OoOE 处理器中重新排序吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/25329348/

10-12 16:14