据我所知,mfence
是硬件内存障碍,而asm volatile ("" : : : "memory")
是编译器障碍。但是,可以使用asm volatile ("" : : : "memory")
代替mfence。
我感到困惑的原因是this link
答案 0 :(得分:23)
嗯,只有内存排序较弱的架构才需要内存屏障。 x86和x64没有弱内存排序。在x86 / x64上,所有商店都有一个释放围栏,所有货物都有一个获取围栏。所以,你应该只需要asm volatile ("" : : : "memory")
有关英特尔和AMD的详细概述以及对相关制造商规范的参考,请参阅http://bartoszmilewski.com/2008/11/05/who-ordered-memory-fences-on-an-x86/
通常,像“volatile”这样的东西在每个字段的基础上使用,其中对该字段的加载和存储本身是原子的。如果字段的加载和存储已经是原子的(即,所讨论的“操作”是对单个字段的加载或存储,因此整个操作是原子的),则不需要volatile
字段修饰符或内存屏障在x86 / x64上。尽管是便携式代码。
当涉及非原子的“操作”时 - 例如。加载或存储到大于本机字的字段或加载或存储到“操作”中的多个字段 - 无论CPU是什么,都可以将操作视为 atomic 建筑。 通常这是通过像互斥锁这样的同步原语来完成的。互斥体(我使用的那些)包括内存屏障,以避免处理器重新排序等问题,因此您不必添加额外的内存屏障指令。我一般认为不使用同步原语过早优化;但是,过早优化的本质当然是97%的时间:)
如果您不使用同步原语并且处理多字段不变量,那么确保处理器不会将存储和加载重新排序到不同内存位置的内存障碍非常重要。
现在,就不在asm volatile中发出“mfence”指令而在clobber列表中使用“memory”而言。从我能够read
开始如果汇编程序指令以不可预测的方式访问内存,请将“memory”添加到clobbered寄存器列表中。这将导致GCC不在汇编器指令的寄存器中保持缓存的内存值,也不会优化存储器或加载到该存储器。
当他们说“GCC”并且没有提及CPU的任何内容时,这意味着它仅适用于编译器。缺乏“mfence”意味着没有CPU内存障碍。您可以通过反汇编生成的二进制文件来验证这一点。如果没有发出“mfence”指令(取决于目标平台),那么很明显CPU没有被告知发出内存栅栏。
根据您所使用的平台以及您尝试做的事情,可能会出现“更好”或更清晰......无法承受的便携性。
答案 1 :(得分:12)
asm volatile ("" ::: "memory")
只是一个编译器障碍。 asm volatile ("mfence" ::: "memory")
既是编译器障碍又是MFENCE
__sync_synchronize()
也是一个编译器屏障和一个完整的内存屏障。 所以asm volatile ("" ::: "memory")
本身不会阻止CPU重新排序独立的数据指令。正如所指出的,x86-64具有强大的内存模型,但StoreLoad重新排序仍然是可能的。如果您的算法需要完整的内存屏障,那么您需要__sync_synchronize
答案 2 :(得分:5)
有两个重新排序,一个是编译器重新排序,另一个是CPU重新排序。
x86 / x64具有相对强大的内存模型,但在x86 / x64 StoreLoad重新排序(稍后加载通过早期存储)可能会发生。 见http://en.wikipedia.org/wiki/Memory_ordering
asm volatile ("" ::: "memory")
只是一个编译器障碍。asm volatile ("mfence" ::: "memory")
既是编译器障碍又是CPU障碍。这意味着,只使用编译器屏障,您只能阻止编译器重新排序,但不能阻止CPU重新排序。这意味着在编译源代码时没有重新排序,但重新排序可能在运行时发生。
因此,这取决于您的需求,使用哪一个。