mfence和asm volatile之间的差异(“”:::“memory”)

问题描述 投票:20回答:3

据我所知,mfence是一个硬件内存屏障,而asm volatile ("" : : : "memory")是编译器的障碍。但是,可以用asm volatile ("" : : : "memory")代替mfence。

我迷茫的原因是this link

gcc x86 memory-barriers
3个回答
23
投票

好吧,只有内存排序较弱的架构才需要内存屏障。 x86和x64没有弱内存排序。在x86 / x64上,所有商店都有一个释放围栏,所有货物都有一个获取围栏。所以,你应该只需要asm volatile ("" : : : "memory")

有关英特尔和AMD的详细概述以及相关制造商规范的参考,请参阅http://bartoszmilewski.com/2008/11/05/who-ordered-memory-fences-on-an-x86/

通常,诸如“volatile”之类的东西在每个字段的基础上使用,其中对该字段的加载和存储本身是原子的。如果字段的加载和存储已经是原子的(即所讨论的“操作”是对单个字段的加载或存储,因此整个操作是原子的)x86 / x64上不需要volatile字段修饰符或内存屏障。尽管是便携式代码。

当谈到非原子的“操作”时 - 例如。加载或存储到大于本机字的字段或加载或存储到“操作”中的多个字段 - 无论CPU架构如何,都可以将操作视为原子操作。通常,这是通过像互斥体这样的同步原语来完成的。互斥体(我使用的那些)包括内存屏障,以避免处理器重新排序等问题,因此您不必添加额外的内存屏障指令。我一般认为不使用同步原语过早优化;但是,过早优化的本质当然是97%的时间:)

如果您不使用同步原语并且处理多字段不变量,那么确保处理器不会将存储和负载重新排序到不同内存位置的内存障碍非常重要。

现在,就不在asm volatile中发出“mfence”指令而在clobber列表中使用“memory”而言。从我能够read

如果汇编程序指令以不可预测的方式访问内存,请将“memory”添加到clobbered寄存器列表中。这将导致GCC不保持跨汇编指令缓存在寄存器中的内存值,而不是优化存储器或加载到该内存。

当他们说“GCC”并且没有提及有关CPU的任何内容时,这意味着它仅适用于编译器。缺乏“mfence”意味着没有CPU内存障碍。您可以通过反汇编生成的二进制文件来验证这一点。如果没有发出“mfence”指令(取决于目标平台),那么很明显CPU没有被告知发出内存栅栏。

根据您所处的平台以及您正在尝试做的事情,可能会出现“更好”或更清晰......无法承受的便携性。


12
投票
  • asm volatile ("" ::: "memory")只是一个编译器障碍。
  • asm volatile ("mfence" ::: "memory")既是编译屏障又是MFENCE
  • __sync_synchronize()也是一个编译器障碍和完整的内存屏障。

所以asm volatile ("" ::: "memory")本身不会阻止CPU重新排序独立的数据指令。正如所指出的,x86-64具有强大的内存模型,但StoreLoad重新排序仍然是可能的。如果您的算法需要完整的内存屏障,那么您需要__sync_synchronize


5
投票

有两个重新排序,一个是编译器重新排序,另一个是CPU重新排序。

x86 / x64具有相对强大的内存模型,但在x86 / x64 StoreLoad重新排序(稍后加载通过早期存储)可能会发生。见http://en.wikipedia.org/wiki/Memory_ordering

  • asm volatile ("" ::: "memory")只是一个编译器障碍。
  • asm volatile ("mfence" ::: "memory")既是编译器障碍又是CPU障碍。

这意味着,只使用编译器屏障,您只能阻止编译器重新排序,但您无法阻止CPU重新排序。这意味着在编译源代码时没有重新排序,但重新排序可能在运行时发生。

因此,它取决于您的需求,使用哪一个。

© www.soinside.com 2019 - 2024. All rights reserved.