以一致的顺序看到的并发商店

时间:2013-01-09 04:23:26

标签: concurrency x86 cpu-cache smp memory-barriers

Intel Architectures Software Developer's Manual, 2012年8月,第一卷。 3A,sect。 8.2.2:

  

除了以外的处理器,任何两个商店都按照一致的顺序看待   那些表演商店。

但这可以吗?

我问的原因是:考虑采用HyperThreading的双核Intel i7处理器。根据手册卷。如图1,图2-8所示,i7的逻辑处理器0和1共享一个L1 / L2高速缓存,但其逻辑处理器2和3共享不同的L1 / L2高速缓存 - 而所有逻辑处理器共享一个L3高速缓存。假设逻辑处理器0和2(不共享L1 / L2高速缓存)几乎同时写入同一存储器位置,并且写入的时间不会比L2深。逻辑处理器1和3(“执行商店以外的处理器”)不能看到“两个商店的订单不一致”吗?

为了实现一致性,逻辑处理器0和2不能发出SFENCE指令,逻辑处理器1和3发出LFENCE指令吗?尽管如此,手册似乎不这么认为,而且它在这件事上的意见并不仅仅是印刷错误。它看起来很刻意。我很困惑。

更新

根据@Benoit的回答,提出以下问题:因此,L1和L2的唯一目的是加速负载。它是L3加速商店。是吗?

3 个答案:

答案 0 :(得分:2)

Intel CPU(像所有普通的SMP系统一样)使用MESI(的一个变体)来确保缓存的加载/存储的缓存一致性。也就是说,所有内核都可以通过其缓存看到相同的内存视图。

内核只有在执行“读取所有权”(RFO)之后才能将其写入高速缓存行,并使该行处于“排他”状态(没有其他高速缓存具有该行的有效副本,可以满足负载)。相关:原子RMW操作阻止其他内核对目标缓存行by locking it in Modified state for the duration of the operation进行任何操作。

要测试这种重新排序,您还需要另外两个线程,它们都读取两个 个存储区(in opposite order)。 您提出的方案有一个核心(reader2)从内存(或L3或它自己的私有L2 / L1)中读取旧值 ,而另一个核心(reader1)已读取了新值writer1存储的同一行。这是不可能的:要使reader1看到writer1的商店,writer1必须已经完成了一个RFO,该RFO使缓存行的所有其他副本无效。不允许直接从DRAM读取而没有(有效地)监听任何回写缓存。 (Wikipedia's MESI article有图表。)

存储(从核心内部的存储缓冲区中)提交给L1d缓存时,该存储将同时对所有其他核心全局可见。在此之前,只有本地核心可以“看到“(通过store->从存储缓冲区转发负载)。

在一个数据从一个核心传播到另一个核心的唯一途径是通过全局缓存一致性域的系统上,MESI缓存一致性仅保证存在一个全局存储顺序,所有线程都可以达成共识。 x86强大的内存排序规则使此全局存储顺序成为程序顺序的某种交织,因此我们将其称为“总存储顺序”内存模型。

x86的强大内存模型不允许对LoadLoad进行重新排序,因此,负载按程序顺序从缓存中获取其数据,而在读取器线程中没有任何屏障指令。 1

在从一致性缓存中获取数据之前,装入程序实际上会监听本地存储缓冲区。 这就是您引用的一致订单规则排除了其中两个存储都是由进行加载的同一核完成的情况的原因。有关更多信息,请参见Globally Invisible load instructions负载数据的真正来源。但是,当加载地址不与任何最近的存储区重叠时,我上面所说的适用:加载顺序是从共享的全局一致缓存域中采样的顺序。


一致顺序规则是一个非常弱的要求。许多非x86 ISA并不能保证它在纸面上,但是很少有实际的(非x86)CPU设计具有一种机制,通过该机制,一个内核可以在全局可见之前看到另一内核的数据。所有核心带有SMT的IBM POWER就是这样的一个例子:Will two atomic writes to different locations in different threads always be seen in the same order by other threads?解释了一个物理核心内逻辑核心之间的转发如何导致它。 (这与您提出的建议类似,但是位于存储缓冲区而不是L2中。)

带有HyperThreading的xp微体系结构(或Ryzen中的AMD SMT)通过在一个物理核心上的逻辑核心之间静态划分存储缓冲区来满足该要求。 What will be used for data exchange between threads are executing on one Core with HT? 因此,即使在一个物理核心内,商店也必须提交给L1d(并在全局可见),然后另一个逻辑核心才能加载新数据。

从一个逻辑核心中已退休但未提交的存储转发到同一物理核心上的其他逻辑核心可能更简单。

(x86的TSO内存模型的其他要求,如按程序顺序出现的加载和存储,要困难得多。现代的x86 CPU执行不规则,但使用内存顺序缓冲区来维持这种错觉,并让存储提交给L1d。程序顺序。负载可以推测性地早于“应有”的值,然后再进行检查,这就是为什么英特尔CPU具有“内存顺序错误推测”流水线:What are the latency and throughput costs of producer-consumer sharing of a memory location between hyper-siblings versus non-hyper siblings?。)

正如@BeeOnRope指出的那样,HT和之间存在交互作用,并保持了没有LoadLoad重新排序的错觉:通常,CPU可以检测到另一个内核何时触摸了缓存行。在实际加载后读取但在结构上允许它读取之前:加载端口可以跟踪对该高速缓存行的无效操作。但是使用HT,加载端口还必须监听其他超线程提交给L1d缓存的存储,因为它们不会使该行无效。 (其他机制也是可行的,但是CPU设计人员是否需要为“正常”负载提供高性能是一个问题。)


脚注1 :在弱排序的ISA上,您将使用负载排序障碍来控制每个读取器中的2个负载从全局一致的缓存域中获取数据的顺序。

writer线程每个都只做一个存储,因此隔离栅是没有意义的。  因为所有核心共享一个统一的缓存域,所以篱笆只需要控制核心内的 local 重新排序。每个内核中的存储缓冲区已经尝试使存储在全球范围内尽快可见(同时遵守ISA的排序规则),因此壁垒只会使CPU等待进行后续操作。

x86 lfence基本上没有内存排序用例,而sfence仅对NT存储有用。当一个线程正在写东西然后读取另一位置时,只有mfence对“普通”东西有用。 http://preshing.com/20120515/memory-reordering-caught-in-the-act/。因此,它会阻止StoreLoad重新排序和跨障碍进行存储转发。


  

根据@Benoit的回答,存在以下问题:L1和L2的唯一目的是加快负载。加快存储速度的是L3。是吗?

否,L1d和L2是回写式高速缓存:Which cache mapping technique is used in intel core i7 processor?。重复存储到同一行可以被L1d吸收。

但是Intel使用包含性的L3缓存,那么一个内核中的L1d如何拥有唯一的副本? L3实际上是包含 tag 的,这是L3标签所需的全部功能,它可以作为探听过滤器(而不是向每个内核广播RFO请求)。脏行中的实际数据是每个内核内部高速缓存专用的,但是L3知道哪个内核具有该行的当前数据(因此,当另一个内核想要读取另一个内核在Modified中的行时,向哪里发送请求州)。干净的缓存行(处于“共享”状态)包含L3的数据,但写入缓存行不会直写到L3。

答案 1 :(得分:1)

我相信英特尔文档所说的是x86芯片的机制将确保其他处理器始终以一致的顺序看到写入。

因此,其他处理器在读取内存位置时只能看到以下结果之一:

    写入之前的
  • 值(即,两次写入之前的读取)

  • 处理器0写入后的
  • 值(即,处理器2首先写入,然后处理器0覆盖)

  • 处理器2写入后的
  • 值(即,处理器0首先写入,然后处理器2覆盖)

处理器1无法在处理器0写入后看到该值,但同时处理器3会在处理器2写入后看到该值(反之亦然)。

请记住,由于允许内部处理器重新排序(请参阅第8.2.3.5节),处理器0和2可能会看到不同的内容。

答案 2 :(得分:0)

哎呀,这是一个棘手的问题!但我会试试......

  

写入不比L2更深

基本上这是不可能的,因为英特尔使用包容性缓存。写入L1的任何数据也将在L2和L3中进行,除非您通过CR0 / MTRR禁用缓存来阻止缓存。

话虽如此,我猜有仲裁机制:处理器发出写入数据的请求,仲裁器从每个请求队列的待处理请求中选择授予的请求。所选择的请求被广播给窥探者,然后广播到缓存。我想它会阻止竞争,强制执行请求之外的处理器所看到的一致顺序。