硬件上存储器的层次结构
有一个问题:
当我们数据在L3到L6之间的时会被Load到不同的CPU之中,不同的CPU之间的数据怎么一致性
也就是说,一个线程改了CPU内部的数据,另一个线程在另一个CPU上怎么才能知道呢
解决办法
1. 把总线锁住(L2和L3之间加把锁),一个CPU访问L3上的一个数的时候,另外一个CPU不允许访问
2. 缓存一致性协议(不同的CPU型号协议不同)
Intel的CPU则是用的MESI Cache一致性协议,给每个缓存做个标记(额外两位)
Modified 数据和主存的内容相比,更改过
Exclusive 数据我独享
Shared 数据在我读的时候 别的CPU也在读
Invalid 无效的:我读的时候 被别的CPU改过了
参考文章:https://www.cnblogs.com/z00377750/p/9180644.html
缓存行对齐(CacheLine)
缓存行Cache Line:当我们把内存中的某些数据放到CPU的缓存中去,不会仅仅把这一个数放进去,
缓存行越大:局部空间效率越高,读取时间慢
缓存行越小:局部空间效率越低,读取时间快
取一个折中值:目前多用64字节。
两个int类型的数据 x,y位于同一个缓存行,我一个CPU只用x,但是会把y读进来,另一个CPU只用y,读的时候也会把x,y都读进来。
当其中一个CPU把x改了之后,通知其他CPU整个缓存行都改变了(其他CPU可能只用到了y)
同样:当其中一个CPU把y改了之后,通知其他CPU整个缓存行都改变了(其他CPU可能只用到了x)
demo1 数组中的两个值可能位于同一个缓存行,并且两个线程位于不同的CPU
package com.mashibing.juc.c_028_FalseSharing; import java.util.Random; public class T01_CacheLinePadding { private static class T { public volatile long x = 0L; } public static T[] arr = new T[2]; static { arr[0] = new T(); arr[1] = new T(); } public static void main(String[] args) throws Exception { Thread t1 = new Thread(()->{ for (long i = 0; i < 1000_0000L; i++) { arr[0].x = i; } }); Thread t2 = new Thread(()->{ for (long i = 0; i < 1000_0000L; i++) { arr[1].x = i; } }); final long start = System.nanoTime(); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println((System.nanoTime() - start)/100_0000); } }
demo2 首先声明了7个Long类型的变量 占用了(7X8=56字节),因为CacheLine的长度是64个字节,所以在此声明Long类型数组的时候,数组中的两个元素会位于不同的缓存行
package com.mashibing.juc.c_028_FalseSharing; public class T02_CacheLinePadding { private static class Padding { public volatile long p1, p2, p3, p4, p5, p6, p7; } private static class T extends Padding { public volatile long x = 0L; } public static T[] arr = new T[2]; static { arr[0] = new T(); arr[1] = new T(); } public static void main(String[] args) throws Exception { Thread t1 = new Thread(()->{ for (long i = 0; i < 1000_0000L; i++) { arr[0].x = i; } }); Thread t2 = new Thread(()->{ for (long i = 0; i < 1000_0000L; i++) { arr[1].x = i; } }); final long start = System.nanoTime(); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println((System.nanoTime() - start)/100_0000); } }
demo1打印时间200 demo2打印时间100。
证明了:位于同一缓存行的两个不同数据,被两个不同CPU锁定,产生互相影响的伪共享问题,使用缓存行对齐可以提高效率