以下是在x86/x86_64中实现顺序一致性的四种方法:
就像这里写的:http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html
C/C 11操作x86实施
注意:有一个C/C 11到x86的替代映射,它不是锁定(或Geofence)Seq Cst存储锁定/GeofenceSeq Cst负载:
GCC4.8.2(x86_64GDB)对C 11d::memory_order_seq_cst使用第一(1)种方法,即LOAD(无Geofence)和STORE MFENCE:
std::atomic<int> a;
int temp = 0;
a.store(temp, std::memory_order_seq_cst);
0x4613e8 <+0x0058> mov 0x38(%rsp),%eax
0x4613ec <+0x005c> mov %eax,0x20(%rsp)
0x4613f0 <+0x0060> mfence
众所周知,那个MFENCE=LFENCE SFENCE。然后这段代码我们可以重写为:LOAD(不带Geofence)和STORE LFENCE SFENCE
问题:
x86唯一能做的重新排序(对于正常的内存访问)是它可能会重新排序跟随存储的负载。
SFENCE保证Geofence前的所有存储在Geofence后的所有存储之前完成。LFENCE保证Geofence前的所有加载在Geofence后的所有加载之前完成。对于正常的内存访问,默认情况下已经提供了单个SFENCE或LFENCE操作的排序保证。基本上,LFENCE和SFENCE本身只对x86较弱的内存访问模式有用。
LFENCE、SFENCE和LFENCE SFENCE都不会阻止后跟加载的存储被重新排序。MFENCE会。
相关参考是Intel x86架构手册。
考虑以下代码:
#include <atomic>
#include <cstring>
std::atomic<int> a;
char b[64];
void seq() {
/*
movl $0, a(%rip)
mfence
*/
int temp = 0;
a.store(temp, std::memory_order_seq_cst);
}
void rel() {
/*
movl $0, a(%rip)
*/
int temp = 0;
a.store(temp, std::memory_order_relaxed);
}
关于原子变量“a”,seq()和rel()在x86架构上都是有序和原子的,因为:
将常量值存储到原子变量中不需要栅栏。栅栏在那里是因为std::memory_order_seq_cst意味着所有内存都是同步的,而不仅仅是保存原子变量的内存。
效果可以通过以下set和get函数来演示:
void set(const char *s) {
strcpy(b, s);
int temp = 0;
a.store(temp, std::memory_order_seq_cst);
}
const char *get() {
int temp = 0;
a.store(temp, std::memory_order_seq_cst);
return b;
}
strcpy是一个库函数,如果在运行时可用,它可能会使用较新的sse指令。由于sse指令在旧处理器中不可用,因此不需要向后兼容性,并且内存顺序未定义。因此,一个线程中的strcpy结果可能在其他线程中不直接可见。
上面的set和get函数使用原子值来强制内存同步,以便strcpy的结果在其他线程中可见。现在栅栏很重要,但是它们在对atomic::store的调用中的顺序并不重要,因为atomic::store内部不需要栅栏。
SFENCE LFENCE不是StoreLoad屏障(MFENCE),因此问题的前提是不正确的。(另请参阅我对同一用户相同问题的另一个版本的回答为什么(或不是?)SFENCE LFENCE等同于MFENCE?)
LFENCE SFENCE不包含任何阻止存储在稍后加载之前被缓冲的内容。MFENCE确实防止了这种情况。
普雷辛的博客文章更详细地解释了StoreLoad屏障的特殊之处,并有一个实际的工作代码示例,演示了在没有MFENCE的情况下重新排序。任何对内存排序感到困惑的人都应该从那个博客开始。
x86有一个强大的内存模型,每个普通存储都有发布语义学,每个普通加载都有获取语义学。这篇文章有细节。
LFENCE和SFENCE仅用于movnt
存储,这些存储是弱有序的,并且绕过了缓存。(后来的SSE4.1movntdqa
从WC内存加载也是弱有序的,但不是从WB内存加载,因此它不会绕过可缓存内存的缓存。)LFENCE在实践中主要是用于阻止非内存指令的乱序执行,如rdtsc
。(在它被引入很久之后,在某些情况下阻止推测性执行以缓解幽灵。)
以防这些链接消失,在我对另一个类似问题的回答中还有更多信息。