lock xchg与mfence具有相同的行为吗?

Val*_*uca 10 x86 assembly multithreading cpu-architecture memory-barriers

我想知道的是lock xchg,mfence从一个线程访问内存位置的角度来看是否会有类似的行为,这个内存位置正在被其他线程进行变异(让我们随便说).它能保证我获得最新的价值吗?之后的内存读/写指令?

我混淆的原因是:

8.2.2"读取或写入不能通过I/O指令,锁定指令或序列化指令重新排序."

-Intel 64 Developers Manual Vol.3

这是否适用于线程?

mfence 状态:

对MFENCE指令之前发出的所有内存加载和存储到内存指令执行序列化操作.此序列化操作保证在MFENCE指令之前的任何加载或存储指令全局可见之前,在程序顺序之前的每条加载和存储指令都是全局可见的.MFENCE指令针对所有加载和存储指令,其他MFENCE指令,任何SFENCE和LFENCE指令以及任何序列化指令(例如CPUID指令)进行排序.

-Intel 64 Developers Manual Vol 3A

听起来更有力.因为它听起来mfence几乎正在刷写写缓冲区,或者至少延伸到写缓冲区和其他内核以确保我未来的加载/存储是最新的.

当基准标记时,两个指令都需要约100个循环才能完成.所以我无论如何都看不出那么大的差异.

主要是我只是困惑.我的指令基于lock互斥体使用,但后来这些包含没有内存栅栏.然后,我看到锁免费使用内存栅栏编程,但没有锁.我知道AMD64有一个非常强大的内存模型,但过时的值可以在缓存中持续存在.如果lock行为与行为不同,mfence那么互斥量如何帮助您查看最新值?

Bee*_*ope 6

我相信您的问题与询问是否mfence具有与lockx86上的-prefixed指令相同的屏障语义,或者在某些情况下它是否提供较少的1或额外的保证相同。

我当前的最佳答案是这是Intel的意图,并且ISA文档保证mfencelocked指令提供相同的防护语义,但是由于实施方面的监督,mfence实际上在最近的硬件上提供了更强的防护语义(至少从Haswell起)。特别是,它mfence可以隔离来自WC类型的存储区域的后续非时间负载,而locked指令则不能。

我们之所以知道这一点,是因为英特尔在诸如HSD162(Haswell)SKL155(Skylake)之类的处理器勘误表中告诉了我们这一点,它们告诉我们锁定指令不会限制随后从WC-memory读取的非临时性读取:

WC内存中的MOVNTDQA可能会通过早期的锁定指令

问题:从WC(写合并)存储器加载的(V)MOVNTDQA(流加载指令)的执行可能似乎传递了访问其他高速缓存行的较早的锁定指令。

含义:期望锁锁定后续(V)MOVNTDQA指令的软件可能无法正常运行。

解决方法:未确定。依靠锁定的指令来隔离(V)MOVNTDQA的后续执行的软件应在锁定的指令与后续的(V)MOVNTDQA指令之间插入MFENCE指令。

据此,我们可以确定(1)Intel可能打算让锁定指令阻止WC型内存中的NT加载,否则这不是勘误表0.5,而(2)锁定指令实际上并没有这样做。英特尔无法或选择不通过微码更新来解决此问题,mfence因此建议使用。

根据SKL079,在Skylake中,mfence实际上失去了针对NT负载的附加防护功能:来自WC内存的MOVNTDQA可能会通过早期的MFENCE指令 -这与- 指令lock勘误表几乎相同,但适用于mfence。但是,此勘误的状态为“ BIOS可能包含针对此勘误的解决方法。”通常,这是Intel所说的“微码更新可解决此问题”。

这种勘误序列可以用时间来解释:Haswell勘误仅出现在该处理器发布数年后的2016年初,因此我们可以假设此问题在此之前引起了英特尔的注意。此时,几乎可以肯定的是Skylake已经在野外使用了,显然采用了一种不太保守的mfence实现方式,也没有阻止WC型内存区域上的NT负载。鉴于锁定指令的广泛使用,修复锁定指令一直到回到Haswell的方式可能是不可能的,也可能是昂贵的,但是需要某种方式来限制NT负载。mfence显然已经在Haswell上完成了工作,Skylake也将得到修复,因此也可以mfence在那里工作。

但这并不能真正解释为什么SKL079(那个mfence)在2016年1月出现,比SKL155(那个locked)在2017年末出现快了将近两年,或者为什么后者在相同的Haswell勘误表之后出现了那么多。

人们可能会猜测英特尔将来会做什么。由于他们无法/不愿意lock通过Skylake 更改Haswell 的指令,代表了已部署的亿万个芯片,因此他们永远无法保证锁定的指令会限制NT负载,因此他们可能会考虑这样做记录下来的,未来的行为。否则他们可能会更新锁定的指令,因此会围堵此类读取,但实际上,您可能十年或更长时间都不能依赖此指令,直到具有当前非隔离行为的芯片几乎已经流通为止。

与Haswell相似,根据BV116BJ138,NT负载可能分别通过先前在Sandy Bridge和Ivy Bridge上锁定的指令。早期的微体系结构也可能会遭受此问题的困扰。Skylake之后的Broadwell和微体系结构中似乎不存在此“错误”。

彼得·科德斯(Peter Cordes)mfence此答案的最后写了一些有关Skylake 变化的文章。

这个答案的其余部分是我最初的答案,在我知道勘误之前,主要是出于历史利益。

旧答案

我对答案的明智猜测是,它mfence提供了其他屏障功能:在使用弱排序指令的访问之间(例如,NT存储),以及在访问弱排序区域之间的访问(例如,WC类型的内存)。

就是说,这只是一个明智的猜测,您将在下面找到我的调查详细信息。

细节

文献资料

目前尚不清楚- 前缀指令(包括带有隐式锁定的内存操作数)mfence所提供的内存一致性影响的程度不同。lockxchg

我认为可以肯定地说,仅就回写存储区而言,不涉及任何非临时访问,mfence可以提供与lock-prefixed操作相同的排序语义。

值得商debate的是,在上述以外的情况下,特别是在访问涉及WB区域以外的区域或涉及非临时(流式)操作时,是否mfencelock前缀指令完全不同。

例如,您可以找到一些建议(例如herehere),这些建议mfence在涉及WC类型的操作(例如NT商店)时暗含强烈的屏障语义。

例如,在此线程中引用McCalpin博士(添加了重点):

仅在绝对确定在以后的“普通”存储区之前所有非临时存储区都可见时,才需要使用fence指令。最重要的情况是在并行代码中,其中并行区域末尾的“屏障”可能包含“普通”存储。没有围栏,处理器可能仍会在Write-Combining缓冲区中修改数据,但会通过该屏障并允许其他处理器读取写入组合数据的“陈旧”副本。这种情况也可能适用于OS将OS从一个内核迁移到另一个内核的单个线程(不确定这种情况)。

我不记得详细的推理方法(今天早上还没有足够的咖啡),但是您要在非临时存储之后使用的说明是MFENCE。 根据SWDM第3卷的8.2.5节,MFENCE是唯一的栅栏指令,它防止在栅栏完成之前执行后续的加载和后续的存储。 令我惊讶的是,在第11.3.1节中没有提到这一点,它告诉您在使用写合并时手动确保一致性非常重要,但没有告诉您如何做到这一点!

让我们检查一下英特尔SDM的8.2.5节:

增强或减弱内存排序模型

英特尔64和IA-32体系结构提供了几种机制,可以增强或削弱内存排序模型来处理特殊的编程情况。这些机制包括:

•I / O指令,锁定指令,LOCK前缀和序列化指令强制对处理器进行更严格的排序。

•SFENCE指令(在Pentium III处理器中引入IA-32体系结构)以及LFENCE和MFENCE指令(在Pentium 4处理器中引入)为特定类型的存储器操作提供了存储器排序和序列化功能。

这些机制可以按如下方式使用:

总线上的内存映射设备和其他I / O设备通常对对其I / O缓冲区的写入顺序敏感。可以使用I / O指令(IN和OUT指令)对此类访问施加强的写入顺序,如下所示。在执行I / O指令之前,处理器将等待程序中所有先前的指令完成,并等待所有缓冲的写入操作耗尽到内存。只有指令提取和页表遍历可以传递I / O指令。直到处理器确定I / O指令已完成,后续指令的执行才开始。

多处理器系统中的同步机制可能取决于强大的内存排序模型。在这里,程序可以使用诸如XCHG指令或LOCK前缀之类的锁定指令,以确保原子地执行对存储器的读-修改-写操作。锁定操作通常类似于I / O操作,因为它们等待所有先前的指令完成,并且等待所有缓冲的写操作排入内存(请参见第8.1.2节“总线锁定”)。

程序同步也可以通过序列化指令执行(请参见第8.3节)。这些指令通常用于关键过程或任务边界,以强制所有先前的指令完成,然后跳转到新的代码段或进行上下文切换。像I / O和锁定指令一样,处理器在执行序列化指令之前要等到所有先前的指令已完成并且所有缓冲的写操作都已排入内存为止。

SFENCE,LFENCE和MFENCE指令提供了一种性能高效的方式,可确保在产生弱排序结果的例程与消耗该数据的例程之间加载和存储内存排序。这些指令的功能如下:

•SFENCE —序列化程序指令流中在SFENCE指令之前发生的所有存储(写)操作,但不影响装入操作。

•LFENCE —序列化程序指令流中在LFENCE指令之前发生的所有加载(读取)操作,但不影响存储操作。

•MFENCE —序列化程序指令流中在MFENCE指令之前发生的所有存储和装入操作。

请注意,与CPUID指令相比,SFENCE,LFENCE和MFENCE指令提供了一种更有效的控制内存顺序的方法。

与McCalpin博士的解释2相反,我认为本节对于是否需要mfence做些额外的事情有些模棱两可。涉及IO,锁定指令和序列化指令的三个部分确实暗示它们在操作之前和之后的存储器操作之间提供了完全的屏障。对于弱指令存储器,它们也不例外。对于IO指令,人们还认为它们需要与弱指令存储器区域保持一致的工作方式,因为此类存储器常用于IO。

然后,在FENCE指令部分,它明确提到了弱内存区域:“ SFENCE,LFENCE和MFENCE指令**提供了一种性能高效的方法,可确保在产生弱排序结果的例程与消耗弱例程的例程之间加载和存储内存排序该数据。”

我们是否在各行之间阅读并表示这是完成此操作的唯一指令,并且前面提到的技术(包括锁定指令)对弱内存区域没有帮助?我们注意到篱笆指令是与弱顺序非临时存储指令同时引入3的,并且通过类似于11.6.13 Cacheability Hint指令中的文本专门处理弱顺序指令的方式,可以找到对此思想的某种支持:

在这些情况下,数据使用者知道数据是弱排序的程度可能会有所不同。结果,应该使用SFENCE或MFENCE指令来确保生成弱排序数据的例程和使用该数据的例程之间的顺序。SFENCE和MFENCE通过确保程序顺序中在SFENCE / MFENCE之前的每条存储指令在栅栏之后的存储指令之前都是全局可见的,从而提供了一种高效的方式来确保排序。

同样,这里特别提到了围栏指令,适用于围栏弱命令。

我们还发现以下观点的支持:锁定的指令可能不会在上述已引用的最后一句的弱排序访问之间提供障碍:

请注意,与CPUID指令相比,SFENCE,LFENCE和MFENCE指令提供了一种更有效的控制内存顺序的方法。

从根本上讲,这意味着这些FENCE指令实质上会取代以前cpuid在存储顺序方面由序列化提供的功能。但是,如果lock前缀指令提供的屏障功能与相同cpuid,则可能是以前建议的方式,因为这些指令通常比cpuid通常花费200个或更多周期的速度快得多。这意味着存在某些情况(可能是顺序较弱的情况),这些lock前缀指令无法处理,cpuid使用的mfence是哪里,现在建议在哪里替换,这意味着比lock前缀指令更强的屏障语义。

但是,我们可以用不同的方式解释上述内容:请注意,在围栏指令的上下文中,经常提到它们是确保排序的性能有效方式。因此,这些说明可能无意提供其他障碍,而只是提供了更有效的障碍。

确实,sfence在几个周期上比序列化指令(例如cpuidlock-prefixed指令,通常是20个周期或更多)要快得多。在另一方面mfence 一般比锁定指令快4,至少在现代的硬件。不过,何时出台,或一些未来的设计,或许这是它可能是更快的预期要快,但没有做成。

因此,我无法根据手册的这些部分进行一定的评估:我认为您可以提出合理的论据,以任何方式解释它。

我们可以进一步查看英特尔ISA指南中有关各种非临时性存储说明的文档。例如,在非临时存储的文档中,movnti您可以找到以下引号:

由于WC协议使用的是弱排序的内存一致性模型,因此,如果多个处理器可能使用不同的内存类型来读取/写入目标内存位置,则应将通过SFENCE或MFENCE指令实现的防护操作与MOVNTI指令一起使用。

“如果多个处理器可能使用不同的内存类型来读取/写入目标内存位置”这一部分让我有些困惑。我希望这是说些类似的话,例如“使用弱排序的提示在指令之间以全局可见的写入顺序强制执行排序”。确实,实际的内存类型(例如,由MTTR定义的)可能甚至在这里都不起作用:使用弱排序的指令时,排序问题仅会出现在WB内存中。

性能

mfence指令被报道采取33个周期(背到后面延迟)基于昂纳雾的指令时序现代的CPU,但更复杂的锁定指令寻址像lock cmpxchg报道只取18个周期。

如果mfence提供的屏障语义不强于屏障语义lock cmpxchg,后者将严格地做更多的工作,并且没有明显的原因mfence花费更长的时间。当然,您可能会争辩说,这lock cmpxchg比更为重要mfence,因此可以获得更多的优化。所有锁定的指令都比mfence甚至不常用的指令都快得多的事实削弱了该论点。同样,您会想象如果所有lock指令共享一个障碍实现,mfence将简单地使用相同的实现,因为这是最简单,最容易验证的方法。

因此mfence,在我看来,较慢的性能mfence是正在做些额外工作的重要证据。


0.5这不是一个水密的论点。有些事情可能会在勘误表中出现,显然是“设计使然”的,而不是错误,例如popcnt对目标寄存器的错误依赖-因此,某些勘误表可以被视为更新期望值的一种文档形式,而不是总是暗示硬件错误。

1显然,lock-prefixed指令执行原子操作,这是无法单独使用实现的mfence,因此lock-prefixed指令肯定具有附加功能。因此,为了mfence有用,我们希望它在某些情况下具有附加的障碍语义,或者表现得更好。

2他完全有可能阅读了散文不同的其他版本的手册。

3 SFENCE在SSE,lfencemfence在SSE2。

4而且通常速度较慢:Agner在最新的硬件上列出了33个周期的延迟,而锁定的指令通常约为20个周期。