Concurrency 以一致的顺序查看并发存储

Concurrency 以一致的顺序查看并发存储,concurrency,x86,cpu-cache,smp,memory-barriers,Concurrency,X86,Cpu Cache,Smp,Memory Barriers,2012年8月,第3A卷,第三节。8.2.2: 任何两个存储都以一致的顺序被处理器看到,而不是 那些表演商店的人 但这是真的吗 我问的原因是:考虑一个具有超线程的双核英特尔i7处理器。根据手册第1卷图2-8,i7的逻辑处理器0和1共享一个一级/二级缓存,但其逻辑处理器2和3共享一个不同的一级/二级缓存-而所有逻辑处理器共享一个三级缓存。假设逻辑处理器0和2(它们不共享一级/二级缓存)大约同时写入同一内存位置,并且写入的深度暂时不超过二级缓存。逻辑处理器1和3不是执行存储的处理器,它们不能以不一致

2012年8月,第3A卷,第三节。8.2.2:

任何两个存储都以一致的顺序被处理器看到,而不是 那些表演商店的人

但这是真的吗

我问的原因是:考虑一个具有超线程的双核英特尔i7处理器。根据手册第1卷图2-8,i7的逻辑处理器0和1共享一个一级/二级缓存,但其逻辑处理器2和3共享一个不同的一级/二级缓存-而所有逻辑处理器共享一个三级缓存。假设逻辑处理器0和2(它们不共享一级/二级缓存)大约同时写入同一内存位置,并且写入的深度暂时不超过二级缓存。逻辑处理器1和3不是执行存储的处理器,它们不能以不一致的顺序看到这两个存储吗

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

更新


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

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

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

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

处理器0写入后的值,即处理器2先写入,然后处理器0重写

处理器2写入后的值,即处理器0先写入,然后处理器2重写

处理器1无法看到处理器0写入后的值,但同时处理器3可以看到处理器2写入后的值,反之亦然


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

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

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

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

处理器0写入后的值,即处理器2先写入,然后处理器0重写

处理器2写入后的值,即处理器0先写入,然后处理器2重写

处理器1无法看到处理器0写入后的值,但同时处理器3可以看到处理器2写入后的值,反之亦然


请记住,由于允许处理器内重新排序,请参见第8.2.3.5节处理器的0和2可能会有不同的看法。

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

写入深度不超过L2

这基本上是不可能的,因为Intel使用包含式缓存。写入L1的任何数据也将发生在L2和L3中,除非您通过CR0/MTRR禁用它们来阻止缓存


这就是说,我猜想存在仲裁机制:处理器发出写入数据的请求,仲裁器从每个请求队列的未决请求中选择授予哪个请求。选定的请求将广播给窥探者,然后广播给缓存。我想这将防止竞争,强制执行处理器看到的一致顺序,而不是执行请求的处理器。

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

写入深度不超过L2

这基本上是不可能的,因为Intel使用包含式缓存。写入L1的任何数据也将发生在L2和L3中,除非您通过CR0/MTRR禁用它们来阻止缓存


这就是说,我猜想存在仲裁机制:处理器发出写入数据的请求,仲裁器从每个请求队列的未决请求中选择授予哪个请求。选定的请求将广播给窥探者,然后广播给缓存。我认为这将防止竞争,强制执行执行请求的处理器以外的处理器所看到的一致顺序。

与所有普通SMP系统一样,Intel CPU使用的变体确保缓存加载/存储的缓存一致性。i、 e.所有内核通过其缓存看到相同的内存视图

核心只能在执行所有权RFO读取后写入缓存线,使缓存线处于独占状态。其他缓存都没有能够满足负载的有效缓存线副本。相关:原子RMW 操作会阻止其他内核对目标缓存线执行任何操作

为了测试这种重新排序,您需要另外两个线程同时读取这两个存储。您提出的方案是,一个核心读取器2从内存或L3读取旧值,或者在另一个核心读取器1读取writer1存储的同一行的新值后,它自己的私有L2/L1读取旧值。这是不可能的:为了让reader1看到writer1的存储,writer1必须已经完成一个RFO,该RFO会使缓存线的所有其他副本在任何地方失效。不允许直接从DRAM读取而不有效地窥探任何写回缓存。有图表

当存储从core内的存储缓冲区提交到L1d缓存时,它将同时对所有其他core全局可见。在此之前,只有本地内核可以通过store->load forwarding从store缓冲区看到它

在数据从一个核心传播到另一个核心的唯一方式是通过全局缓存一致性域的系统上,MESI缓存一致性单独保证存在单个全局存储顺序,所有线程都可以同意。x86强大的内存排序规则使得这个全局存储顺序是程序顺序的交错,我们称之为总存储顺序内存模型

x86的强内存模型不允许加载重新排序,因此加载按程序顺序从缓存中获取数据,而读卡器线程中没有任何障碍指令。1

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

一致顺序规则是一个非常弱的要求。许多非x86 ISA不能在纸面上保证它,但很少有实际的非x86 CPU设计有一种机制,通过这种机制,一个内核可以看到另一个内核存储的数据,然后才对所有内核全局可见。IBM POWER with SMT就是这样一个例子:解释了一个物理核心内的逻辑核心之间的转发是如何导致这种情况的。这与您建议的类似,但在存储缓冲区中,而不是在L2中

通过在一个物理核上的逻辑核之间静态分区存储缓冲区,采用Hyperreading或AMD在Ryzen的SMT的x86微体系结构符合这一要求。因此,即使在一个物理内核中,存储也必须提交到L1d并在另一个逻辑内核加载新数据之前全局可见

不将一个逻辑核中的失效但未提交的存储转发到同一物理核上的其他逻辑核可能更简单

x86的TSO内存模型的其他要求,如按程序顺序显示的加载和存储,则更加困难。现代x86 CPU执行无序,但使用内存顺序缓冲区来维持这种错觉,并让存储以程序顺序提交到L1d。负载可以推测地提前获取值,然后稍后进行检查。这就是英特尔CPU内存顺序错误的原因:

正如@BeeOnRope指出的,HT和保持无负载重新排序的幻觉之间存在交互作用:通常CPU可以在负载实际读取缓存线之后,但在体系结构允许读取缓存线之前,检测另一个内核何时接触缓存线:加载端口可以跟踪缓存线的失效。但对于HT,加载端口还必须监视另一个超线程提交到L1d缓存的存储,因为它们不会使该行无效。其他机制也是可能的,但如果CPU设计者想要在正常负载下获得高性能,就必须解决这个问题

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

writer线程只进行一次存储,因此围栏是没有意义的。 由于所有核心共享一个一致的缓存域,fences只需要控制核心内的本地重新排序。每个核心中的存储缓冲区已经尝试在遵守ISA的排序规则的同时,尽可能快地使存储全局可见,因此屏障只会使CPU在执行后续操作之前等待

x86 lfence基本上没有内存排序用例,而sfence只对NT存储有用。当一个线程正在写东西,然后读取另一个位置时,只有mfence对普通的东西有用。因此,它会阻止StoreLoad重新排序和跨越屏障的存储转发

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

否,L1d和L2是回写缓存:。同一行的重复存储可被L1d吸收


但是Intel使用包含式L3缓存,那么一个内核中的L1d怎么可能只有一个副本?L3实际上是包含标签的,这就是L3标签作为监听过滤器工作所需的全部,而不是向每个核心广播RFO请求。脏线中的实际数据是每个核心内部缓存的专用数据,但是L3知道哪个核心拥有一条线的当前数据,因此当另一个核心想要读取另一个核心处于修改状态的线时,L3知道在哪里发送请求。处于共享状态的干净缓存线是包含三级缓存线的数据,但写入缓存线不会写入三级缓存线。

与所有普通SMP系统一样,英特尔CPU使用的变体可确保缓存加载/存储的缓存一致性。i、 e.所有内核通过其缓存看到相同的内存视图

核心只能在执行所有权RFO读取后写入缓存线,使缓存线处于独占状态。其他缓存都没有能够满足负载的有效缓存线副本。相关:原子RMW操作阻止其他内核对目标缓存线执行任何操作

为了测试这种重新排序,您需要另外两个线程同时读取这两个存储。您提出的方案是,一个核心读取器2从内存或L3读取旧值,或者在另一个核心读取器1读取writer1存储的同一行的新值后,它自己的私有L2/L1读取旧值。这是不可能的:为了让reader1看到writer1的存储,writer1必须已经完成一个RFO,该RFO会使缓存线的所有其他副本在任何地方失效。不允许直接从DRAM读取而不有效地窥探任何写回缓存。有图表

当存储从core内的存储缓冲区提交到L1d缓存时,它将同时对所有其他core全局可见。在此之前,只有本地内核可以通过store->load forwarding从store缓冲区看到它

在数据从一个核心传播到另一个核心的唯一方式是通过全局缓存一致性域的系统上,MESI缓存一致性单独保证存在单个全局存储顺序,所有线程都可以同意。x86强大的内存排序规则使得这个全局存储顺序是程序顺序的交错,我们称之为总存储顺序内存模型

x86的强内存模型不允许加载重新排序,因此加载按程序顺序从缓存中获取数据,而读卡器线程中没有任何障碍指令。1

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

一致顺序规则是一个非常弱的要求。许多非x86 ISA不能在纸面上保证它,但很少有实际的非x86 CPU设计有一种机制,通过这种机制,一个内核可以看到另一个内核存储的数据,然后才对所有内核全局可见。IBM POWER with SMT就是这样一个例子:解释了一个物理核心内的逻辑核心之间的转发是如何导致这种情况的。这与您建议的类似,但在存储缓冲区中,而不是在L2中

通过在一个物理核上的逻辑核之间静态分区存储缓冲区,采用Hyperreading或AMD在Ryzen的SMT的x86微体系结构符合这一要求。因此,即使在一个物理内核中,存储也必须提交到L1d并在另一个逻辑内核加载新数据之前全局可见

不将一个逻辑核中的失效但未提交的存储转发到同一物理核上的其他逻辑核可能更简单

x86的TSO内存模型的其他要求,如按程序顺序显示的加载和存储,则更加困难。现代x86 CPU执行无序,但使用内存顺序缓冲区来维持这种错觉,并让存储以程序顺序提交到L1d。负载可以推测地提前获取值,然后稍后进行检查。这就是英特尔CPU内存顺序错误的原因:

正如@BeeOnRope指出的,HT和保持无负载重新排序的幻觉之间存在交互作用:通常CPU可以在负载实际读取缓存线之后,但在体系结构允许读取缓存线之前,检测另一个内核何时接触缓存线:加载端口可以跟踪缓存线的失效。但对于HT,加载端口还必须监视另一个超线程提交到L1d缓存的存储,因为它们不会使该行无效。其他机制也是可能的,但C PU设计者必须解决的问题是,如果他们想要正常负载的高性能

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

writer线程只进行一次存储,因此围栏是没有意义的。 由于所有核心共享一个一致的缓存域,fences只需要控制核心内的本地重新排序。每个核心中的存储缓冲区已经尝试在遵守ISA的排序规则的同时,尽可能快地使存储全局可见,因此屏障只会使CPU在执行后续操作之前等待

x86 lfence基本上没有内存排序用例,而sfence只对NT存储有用。当一个线程正在写东西,然后读取另一个位置时,只有mfence对普通的东西有用。因此,它会阻止StoreLoad重新排序和跨越屏障的存储转发

根据@Benoit的回答,提出了以下问题:因此,L1和L2的唯一目的是加速负载。是L3加快了存储速度。是这样吗

否,L1d和L2是回写缓存:。同一行的重复存储可被L1d吸收


但是Intel使用包含式L3缓存,那么一个内核中的L1d怎么可能只有一个副本?L3实际上是包含标签的,这就是L3标签作为监听过滤器工作所需的全部,而不是向每个核心广播RFO请求。脏线中的实际数据是每个核心内部缓存的专用数据,但是L3知道哪个核心拥有一条线的当前数据,因此当另一个核心想要读取另一个核心处于修改状态的线时,L3知道在哪里发送请求。处于共享状态的干净缓存线是包含L3的数据,但写入缓存线不会写入L3。

谢谢您和+1的周到回答,但当您说这不可能时,为什么不呢?我无法想象会有什么机制使它变得不可能。缓存窥探无法做到这一点,因为不存在任何可以引发窥探的东西:只有普通的存储和从缓存加载。缓存是多级的:我认为这就是问题所在。无论如何,我不确定我是否完全理解你的答案,尽管它在任何情况下都是部分启发性的。我会调查一下,但也许芯片有一种正确的方法使缓存失效。写入是否可以在一级/二级缓存级别停止,而不传播失效?我不确定哪种方式,我必须再多读一点你说的是两个writer内核都存储在同一个位置的情况。i、 这是一个正确的答案。但英特尔的文档中提到的是任意两个商店,不管它们位于同一个或不同的位置。他们不需要进行区分,因为x86的总存储顺序内存模型要求存在一个全局顺序。谢谢你和+1给出了周到的答案,但是当你说这不可能时,为什么不呢?我无法想象会有什么机制使它变得不可能。缓存窥探无法做到这一点,因为不存在任何可以引发窥探的东西:只有普通的存储和从缓存加载。缓存是多级的:我认为这就是问题所在。无论如何,我不确定我是否完全理解你的答案,尽管它在任何情况下都是部分启发性的。我会调查一下,但也许芯片有一种正确的方法使缓存失效。写入是否可以在一级/二级缓存级别停止,而不传播失效?我不确定哪种方式,我必须再多读一点你说的是两个writer内核都存储在同一个位置的情况。i、 这是一个正确的答案。但英特尔的文档中提到的是任意两个商店,不管它们位于同一个或不同的位置。它们不需要进行区分,因为x86的总存储顺序内存模型要求存在一个全局顺序。我不明白这一点。因此,L1和L2的唯一目的是加速负载。是L3加快了存储速度。是这样吗?这一定意味着,在一个机架式服务器中——其中几个物理CPU包可能安装在服务器主板上的单独插槽中,每个包都有自己的三级缓存——芯片组禁止将同一个未经验证的缓存线保存在两个不同的三级缓存中。也就是说,当包0从主内存加载缓存线时,芯片组必须首先使包1上的缓存线失效。你说得对:这是一个棘手的问题。1事实上,包容性缓存加快了加载速度,但我看不出它会加快存储速度。你能详细说明一下吗?2如果将您置于multisockets板的情况下,您是在谈论NUMA系统;这与SMP在讨论内存一致性时有很大的不同。我认为你的问题仅限于SMP。谢谢。回答:1你已经在上面的主要回答中详细阐述了

. 我不相信我能说得更好。你的反对意见回答了我的问题。你是对的:虽然我没有意识到这一点,但我的问题确实是SMP特有的。因此,由于仅SMP问题就已经够难了,让我们把NUMA问题推迟到另一天。我会看看是否没有某种SMP标签可以添加到原始问题中。如果你有时间,你可以将你的评论添加到上面的主要答案中。这大大增加了答案。不,Intel CPU使用回写L1d和L2。L3仅包含标记,如果它包含所有标记的话。第二段不够明确,足以与MESI Core兼容,它通过RFO独占缓存线的所有权来获得写入权限,从而使所有其他副本无效。不过,将数据写入单个共享包容性缓存的含义是完全错误的。多套接字系统不在套接字之间共享缓存,只为每个套接字中的内核共享缓存,但一致性机制并没有本质上的不同。我明白了。我不明白这一点。因此,L1和L2的唯一目的是加速负载。是L3加快了存储速度。是这样吗?这一定意味着,在一个机架式服务器中——其中几个物理CPU包可能安装在服务器主板上的单独插槽中,每个包都有自己的三级缓存——芯片组禁止将同一个未经验证的缓存线保存在两个不同的三级缓存中。也就是说,当包0从主内存加载缓存线时,芯片组必须首先使包1上的缓存线失效。你说得对:这是一个棘手的问题。1事实上,包容性缓存加快了加载速度,但我看不出它会加快存储速度。你能详细说明一下吗?2如果将您置于multisockets板的情况下,您是在谈论NUMA系统;这与SMP在讨论内存一致性时有很大的不同。我认为你的问题仅限于SMP。谢谢。回答:1你已经在上面的主要回答中详细阐述了。我不相信我能说得更好。你的反对意见回答了我的问题。你是对的:虽然我没有意识到这一点,但我的问题确实是SMP特有的。因此,由于仅SMP问题就已经够难了,让我们把NUMA问题推迟到另一天。我会看看是否没有某种SMP标签可以添加到原始问题中。如果你有时间,你可以将你的评论添加到上面的主要答案中。这大大增加了答案。不,Intel CPU使用回写L1d和L2。L3仅包含标记,如果它包含所有标记的话。第二段不够明确,足以与MESI Core兼容,它通过RFO独占缓存线的所有权来获得写入权限,从而使所有其他副本无效。不过,将数据写入单个共享包容性缓存的含义是完全错误的。多套接字系统不会在套接字之间共享缓存,只针对每个套接字中的内核,但一致性机制并没有本质上的不同。实际上,在内存一致性方面,超级同级存在一种特殊情况,这就是为什么由于跨线程嗅探命中加载缓冲区,所以会清除内存顺序。我认为发生的情况是,由于积极的负载排序,为了保持负载有序出现,当挂起的负载中涉及的某些缓存线在失效之前失效时,MOB将发出一个排序机器清除。条件可能比这稍微窄一点,但这是一般的想法。然而,在超级兄弟姐妹的情况下,没有无效。。。。。。因为该线在M状态下仅停留在L1中。因此,您需要这种特殊的跨线程嗅探,以避免破坏内存顺序。在这种情况下,像PPC这样较弱的平台可以采取不同的方法,这就是如何在那里获得IRIW重新排序。@BeeOnRope:啊,对了,这将导致超线程内的加载重新排序,这可能是IRIW重新排序的来源,因此必须加以防范。PowerPC可能根本不需要让这种情况变得特别:没有障碍,它可以让Load重新排序发生。有了屏障,以后的加载一开始就不会过早地获取其值,除非实现推测不需要像x86那样的屏障,但这似乎不太可能。您的答案是令人生畏的。它值得拥有比我这个晦涩难懂的问题更广泛的听众。无论如何,我很感激。事实上,当涉及到内存一致性时,超级兄弟姐妹有一个特殊的情况,这就是为什么由于跨线程嗅探命中加载缓冲区的事情,你会得到内存顺序的清除。我认为发生的情况是,由于积极的负载排序,为了保持负载有序出现,当挂起的负载中涉及的某些缓存线在失效之前失效时,MOB将发出一个排序机器清除。条件可能比这稍微窄一点,但这是一般的想法。但是,对于超级同级,没有inv
验证。。。。。。因为该线在M状态下仅停留在L1中。因此,您需要这种特殊的跨线程嗅探,以避免破坏内存顺序。在这种情况下,像PPC这样较弱的平台可以采取不同的方法,这就是如何在那里获得IRIW重新排序。@BeeOnRope:啊,对了,这将导致超线程内的加载重新排序,这可能是IRIW重新排序的来源,因此必须加以防范。PowerPC可能根本不需要让这种情况变得特别:没有障碍,它可以让Load重新排序发生。有了屏障,以后的加载一开始就不会过早地获取其值,除非实现推测不需要像x86那样的屏障,但这似乎不太可能。您的答案是令人生畏的。它值得拥有比我这个晦涩难懂的问题更广泛的听众。无论如何,我很感激。