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