You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
引用<Is Parallel Programming Hard, And, If So, What Can You Do About It?>中对它作用的说明:
The effect of this is that a read memory barrier orders only loads on the CPU that executes it, so that all loads preceding the read memory barrier will appear to have completed before any load following the read memory barrier.
这是在x86上唯一没有被保证的,原因猜测是store是异步操作,load很容易跑到store前面。Java使用lock指令前缀来实现,而mfence也有同样的效果。
这一篇文章实锤了x86不保证StoreLoad: Memory Reordering Caught in the Act mfence的作用是排干store buffer,在这之前封锁后续load操作。
但是是否x86保证了顺序,就意味着实际上完全没有重排发生了呢?可能不是这样的,重排可能还是有的,只不过是不可见的,比如对a和b的写入,两个变量恰好位于当前CPU的同一个缓存行,处于独占状态,其它CPU无法观察到两个变量写入的先后顺序。
参考: Does an x86 CPU reorder instructions?
JMM
根据: The JSR-133 Cookbook for Compiler Writers,Java内存模型定义了四种内存屏障:
其实就是load和store操作的全排列。
Linux
而Linux定义了三种内存屏障:
smp_rmb
smp_wmb
smp_wb
关系
从字面上来看,
smp_rmb
对应LoadLoad,smp_wmb
对应StoreStore,smp_mb
对应LoadStore和StoreLoad,这样来说Java的定义更加细致一些,区分了Load和Store重排的两个不同方向。读屏障
引用<Is Parallel Programming Hard, And, If So, What Can You Do About It?>中对它作用的说明:
重排
那么什么会导致相反地结果?我觉得可能有两种情况:
那么在这种情况下读屏障可以来保证顺序,怎么做到呢,猜测是禁止重排并且在第一条完成加载后再执行第二条。
Invalidate Queue
Is Parallel Programming Hard, And, If So, What Can You Do About It?书中给出了在存在Invalidate Queue的情况下读屏障的作用,代码如下:
即使我们读到了
b == 1
,a还是有可能为0并导致断言失败。这里的原因并不是重排,而是执行bar
函数的CPU没有将Invalidate Queue中的使无效消息应用到cache中,所以导致CPU读到了无效(过期)的a值。所以需要一个读屏障来drain Invalidate Queue:
所以我认为这里读屏障有两种实际的作用:禁止重排和drain Invalidate Queue。
X86
这两点就保证了Java的LoadLoad和Linux的
smp_rmb
只是针对编译器的,而在硬件层面是一个nop
。写屏障
通常CPU有store buffer这一结构(其实我也不知道是不是全都有,但X86和ARM是都存在)。假设CPU先后执行两个store操作:
和Load类似,我认为也有两种情形会导致b先于a被存储:
所以写屏障的作用就是:
X86
x86平台会把所有写操作放到store buffer中,并保证写操作的先后顺序,所以在x86上StoreStore也是只针对编译器,对硬件是一个
nop
。LoadStore
x86保证了前面的load会先于store发生,至于原理不得而知,可能和store是异步的,而load是实时的有关。所以在x86上Java的LoadStore是一个只针对编译器的
nop
。StoreLoad
这是在x86上唯一没有被保证的,原因猜测是store是异步操作,load很容易跑到store前面。Java使用
lock
指令前缀来实现,而mfence
也有同样的效果。这一篇文章实锤了x86不保证StoreLoad: Memory Reordering Caught in the Act
mfence
的作用是排干store buffer,在这之前封锁后续load操作。但是是否x86保证了顺序,就意味着实际上完全没有重排发生了呢?可能不是这样的,重排可能还是有的,只不过是不可见的,比如对a和b的写入,两个变量恰好位于当前CPU的同一个缓存行,处于独占状态,其它CPU无法观察到两个变量写入的先后顺序。
参考: Does an x86 CPU reorder instructions?
总结
感觉内存屏障的细节真的很难理解,因为需要知道CPU内部的实现细节,而这一点则很难实现,比如对于ARM处理器就无法知道其store buffer是不是FIFO,有没有Invalidate Queue。
The text was updated successfully, but these errors were encountered: