硬件层的并发优化基础知识
数据层一致协议
每个cpu有内部的高速缓存区,也有外部的高速缓存或者内存。当一个数据在外部高速缓存或者内存中,当cpu要处理数据的时候,会先查内部高速缓存,再查外部高速缓存,再查内存,将数据拿回来处理,那么多个cpu同时处理一个数据,如何保证数据一致性?
在老的cpu中,会对总线加锁,这样效率很低。所以在新的cpu中,会使用各种各样的数据层一致协议,因特尔使用的MESI协议,还有各种其他协议
MESI协议
MESI缓存锁。内存中保存对象的4个状态,通过4个状态来保证数据一致性
- Modified 当前cpu修改过
- Exclusive 只有当前cpu在用
- Shared 当前cpu和其他cpu都在用
- invalid 数据已经被别的cpu修改过
但是当遇到无法缓存的数据(例如数据过大、或者跨越多个缓存),则缓存锁失效。此时就需要总线锁配合。
所以现在cpu的数据一致性=缓存锁(MESI)+总线锁
cacheline 缓存行对齐 伪共享
cpu从内存中读取数据的时候,会读取整行数据(一般为64字节),例如有一个int类型,4字节。cpu读取的时候,不仅仅是读取4个字节,而且读取了4个字节和之后的60个字节。这64个字节就是缓存行。
伪共享:当2个数据在同一个缓存行的时候,2个cpu一个修改x,一个修改y,就会互相影响,互相让对方更新数据。但其实每个cpu只用到了自己想用的数据。
面试题:位于同一缓存行的两个不同数据,被2个不同cpu锁定,产生互相影响的伪共享问题
使用缓存行对齐,就可以提高效率。比如一个long对象占了8字节,那么声明的时候,可以在前创建7个对象,后创建7个对象,保证这个对象单独的占据一个缓存行,不和其他对象公用一个缓存行。
cpu乱序问题
cpu运算速度是内存的100倍,那为了提高指令执行效率,会在一条指令执行过程中,去同时执行另一条指令,前提是两条指令没有依赖关系。比如第一条cpu指令在执行时,还在等待内存的响应,其他指令也在分析与运行
写也可以同时进行。比如cpu要把一个值写入到内存中,但是因为内存运算速度慢,还没写进去的时候,cpu又操作了这个值,又要保存一次。此时,cpu会把相同的写入操作,放到wc(wirteCombining) buffer中,一次执行。
现在的wc buffer只有4个位置,操作指令大于4个位置的,就要分多次写
如何保证特定情况不乱序?
硬件级别:
cpu内存屏障 ,不同cpu不同
x86cpu,
sfence:s =save在sfence指令前的写操作必须在sfence指令后的写操作前完成
lfence:l = load在lfence指令前的读操作必须在lfence指令后的读操作前完成
mfence:m = 在mfence指令前的读写操作必须在mfence指令后的读写操作前完成
lock指令
jvm级别如何规范(JSR133)
jvm的有序性,可以基于多种硬件级别的保证方式来实现.其实就是jvm用硬件级别的各种方式,实现各种有序。
- LoadLoad屏障
- 对于这样的语句load1;loadLoad;load2,在load2以及后续的读取操作要读取的数据被访问前,保证load1读取的数据读取完毕
- StoreStore屏障
- 对于这样的语句store1;storeStore;store2,在store2以及后续的写入操作前,保证store1的写入操作对其他处理器可见
- LoadStore屏障
- 对于这样的语句Load1;loadStore;store2,在store2以及后续的写入操作前,保证load1读取的数据读取完毕
- StoreLoad屏障
- 对于这样的语句store1;storeLoad;load2,在load2以及后续的读取操作前,保证store1的写入操作对其他处理器可见
volatile 的实现细节
volatile关键字,会先变成字节码,再给jvm,最后由jvm调用操作系统来实现。所以要按层级来看3个地方的实现
- 字节码层面:字节码层面只加了VOLATILE
- jvm层面:
- 在volatile写操作:storeStoreBarrier,volatile写,StoreLoadBarrier.
- 在volatile读操作:LoadLoadBarrier,volatile读,LoadStoreBarrier
- os和硬件层面
- windows是用lock指令实现
synchronized的实现细节
- 字节码层面:
- 在方法前加锁和volatile类似,在access_flag增加了一个synchronized
- 在方法内加锁,是增加了一条monitorenter和两条monitorexit两条指令(一条是正常退出,一条是发生异常退出)
- jvm层面:c c++调用了操作系统提供的同步机制
- os和硬件层面
- X86 lock指令实现