C 无序执行和内存隔离

C 无序执行和内存隔离,c,x86,cpu,memory-barriers,memory-fences,C,X86,Cpu,Memory Barriers,Memory Fences,我知道,现代CPU可以无序执行,但它们总是按照维基百科所描述的顺序退出结果 无序处理器用其他准备就绪的指令及时填充这些“插槽”,然后在最后对结果重新排序,使其看起来指令已正常处理。 现在,据说在使用多核平台时需要内存围栏,因为由于无序执行,x的错误值可以在这里打印出来 Processor #1: while f == 0 ; print x; // x might not be 42 here Processor #2: x = 42; // Memory fence requir

我知道,现代CPU可以无序执行,但它们总是按照维基百科所描述的顺序退出结果

无序处理器用其他准备就绪的指令及时填充这些“插槽”,然后在最后对结果重新排序,使其看起来指令已正常处理。

现在,据说在使用多核平台时需要内存围栏,因为由于无序执行,x的错误值可以在这里打印出来

Processor #1:
 while f == 0
  ;
 print x; // x might not be 42 here

Processor #2:
 x = 42;
 // Memory fence required here
 f = 1
现在我的问题是,由于无序处理器(我假设多核处理器的情况下是内核)总是按顺序退出结果,那么内存围栏的必要性是什么。多核处理器的内核不是只看到其他内核失效的结果,还是也看到正在运行的结果

我的意思是,在我上面给出的示例中,当处理器2最终退出结果时,x的结果应该在f之前,对吗?我知道在无序执行期间,它可能在x之前修改了f,但在x之前它一定没有退出,对吗


现在,为了使结果退役和缓存一致性机制就位,为什么在x86中需要内存围栏?

本教程解释了以下问题:

FWIW是现代x86处理器上出现内存排序问题的地方,其原因是,虽然x86内存一致性模型提供了相当强的一致性,但需要明确的障碍来处理读写一致性。这是由于所谓的“存储缓冲区”

也就是说,x86在顺序上是一致的(很好,也很容易推理),只是加载可能会在以前的存储中重新排序。也就是说,如果处理器执行该序列

store x
load y
然后在处理器总线上,这可能被视为

load y
store x
这种行为的原因是前面提到的存储缓冲区,它是写入在系统总线上发出之前的一个小缓冲区。负载延迟是性能的一个关键问题,因此允许负载“跳转队列”


请参阅

中的第8.2节,内存围栏确保围栏之前对变量的所有更改对所有其他内核都可见,以便所有内核都具有最新的数据视图

如果不设置内存围栏,则内核可能使用错误的数据,这一点在场景中尤其明显,在场景中,多个内核将使用相同的数据集。在这种情况下,您可以确保当CPU 0执行某些操作时,对数据集所做的所有更改现在对所有其他内核都可见,这些内核随后可以使用最新信息

一些体系结构,包括无处不在的x86/x64,提供了几种 内存屏障指令,包括有时称为 “全围栏”。全围栏可确保所有装载和存储操作 在进行任何装载和安装之前,应先安装围栏 商店紧跟着栅栏

如果核心开始处理数据集上过时的数据,它怎么可能得到正确的结果?不管最终结果是否以正确的顺序呈现,都是不可能的

密钥位于缓存和CPU之间的存储缓冲区中,执行以下操作:

存储缓冲区对远程CPU不可见

存储缓冲区允许将对内存和/或缓存的写入保存到 优化互连访问

这意味着将向该缓冲区写入内容,然后在某个时候将该缓冲区写入缓存。因此,缓存可能包含非最新数据的视图,因此另一个CPU通过缓存一致性也将不包含最新数据。要使最新数据可见,存储缓冲区刷新是必要的,我认为这基本上就是内存隔离在硬件级别所导致的

编辑:

对于您用作示例的代码,Wikipedia说:

可以在处理器#2分配给f之前插入内存屏障 确保x的新值在或处对其他处理器可见 在f值发生变化之前


为了明确前面答案中隐含的内容,这是正确的,但与内存访问不同:

CPU可以无序执行,但它们总是按顺序退出结果

指令的失效与执行内存访问是分开的,内存访问可能在与指令失效不同的时间完成

每一个内核的行为就像它自己的内存访问在失效时发生一样,但其他内核可能在不同的时间看到这些访问

(在x86和ARM上,我认为只有存储区明显受此影响,但例如,Alpha可能会从内存中加载旧值。x86 SSE2的指令具有比正常x86行为更弱的保证)


另外,从记忆中可以看出,被遗弃的斯巴克岩石实际上可能会因故障而退役,它花费了能量和晶体管来确定什么时候是无害的。由于功耗和晶体管计数,它被废弃了。。。我不相信有任何通用CPU是在无序报废的情况下上市的。

Janneb,你能稍微解释一下存储缓冲区,以及为什么它们在这种情况下很重要吗?缓存一致性难道不能确保x86中存在读写一致性吗?@metallicprade:啊,不过第二点,我怀疑在您的具体示例中实际上不需要障碍。我编辑了这篇文章来反映这一点,还添加了对x86内存模型中允许的重新排序的解释。@janneb,他从维基百科关于内存障碍的文章中选取了一个例子。FWIW和OTOH减去一。请注意,内存障碍总是成对出现在正确的代码中:当两个线程通信时,每个线程都必须执行一些内存访问顺序(=围栏)。通常,这些围栏中的一个有