volatile

缓存一致性

在多处理器系统中,每个cpu都有自己的缓存,它们都有一个共享的内存。内存和缓存中的数据要保持一致,通过一个协议MESI,是由硬件来完成缓存一致性的。一个cpu总能读到最新的数据,不管它是在自己的cache中,还是在其他cpu的cache中,还是在内存中,这就是缓存一致性。

我看很多资料上都写volatile可以保证缓存一致性,写时写到缓存,同时写到内存,读时直接读内存。我觉得它这么做不是为了保证,一个线程的修改对别的线程可见,因为这个可见性硬件可以保证(我目前觉得),而是为了告诉编译器,不要对我进行优化。如下面的代码:

int i;
i = 1;
i = 2

如果编译器优化的话,可能第一个赋值语句i=1就没有了,单从一个线程来看,有没有i=1这个赋值语句,都没有差别。但如果是多个线程同时执行的话,可能会对别的线程由影响,加上volatile就不会省略掉第一个赋值语句了,会严格按照程序编写编译。

顺序一致性

程序可能会被重排序,重排序会体现在两个地方:编译器cpu
在不影响单个线程执行结果的情况下,编译器可能会重新排列执行的顺序;cpu因为指令流水技术,互不影响的指令,可能会被发送到不同的流水线上,哪个先执行完就不一定了,这就间接形成了重排序。

变量设置了volatile关键字,会防止这两种重排序。

(下面是我目前的理解,不一定正确)
volatile最终是通过内存栅栏(也就是下面的内联汇编)防止重排序。

编译时内存栅栏实现:

__asm__ __volatile__("" ::: "memory")

它会防止编译时指令重排,但不会防止执行时cpu重排。

__asm__ __volatile__("mfence" ::: "memory");
__asm__ __volatile__("lfence" ::: "memory");

硬件内存栅栏实现:
不同系统实现不同,下面是x86,x86-64

lfence (asm), void _mm_lfence(void)
sfence (asm), void _mm_sfence(void)[10]
mfence (asm), void _mm_mfence(void)[

编译器对硬件内存栅栏的支持:

  • GCC,[13] version 4.4.0 and later,[14] has __sync_synchronize.
  • Since C11 and C++11 an atomic_thread_fence() command was added.
  • The Microsoft Visual C++ compiler[15] has MemoryBarrier().
  • Sun Studio Compiler Suite[16] has __machine_r_barrier, __machine_w_barrier and __machine_rw_barrier.

unofficial open jdk – github
openjdk8 源码下载

这个类里面有内存屏障的代码,针对不同的平台有不同的实现:
openjdk/src/hotspot/os_cpu/xxx/orderAccess_xxx.hpp

// A compiler barrier, forcing the C++ compiler to invalidate all memory assumptions
inline void compiler_barrier() {
  __asm__ volatile ("" : : : "memory");
}

inline void OrderAccess::loadload()   { compiler_barrier(); }
inline void OrderAccess::storestore() { compiler_barrier(); }
inline void OrderAccess::loadstore()  { compiler_barrier(); }
inline void OrderAccess::storeload()  { fence();            }

inline void OrderAccess::acquire()    { compiler_barrier(); }
inline void OrderAccess::release()    { compiler_barrier(); }

inline void OrderAccess::fence() {
#ifdef AMD64
  __asm__ volatile ("lock; addl $0,0(%%rsp)" : : : "cc", "memory");
#else
  __asm__ volatile ("lock; addl $0,0(%%esp)" : : : "cc", "memory");
#endif
  compiler_barrier();
}

#endif // OS_CPU_SOLARIS_X86_VM_ORDERACCESS_SOLARIS_X86_HPP

关键词:顺序一致性 缓存一致性 java内存模型 多级缓存 多核 流水线
参考:
Memory ordering – wikipedia
Memory barrier – wikipedia
http://m.techweb.com.cn/article/2017-09-04/2582013.shtml
[SPDK/NVMe存储技术分析]006 - 内存屏障(MB)

https://www.jianshu.com/p/ef8de88b1343
https://www.jianshu.com/p/506c1e38a922

To read:
LINUX KERNEL MEMORY BARRIERS

展开阅读全文

没有更多推荐了,返回首页