java 之CPU~CPU缓存(转)

在大多数人观念里,在Java,c#中关于CPU这样的底层是可以不用了解的,那都是从c++/c的事情。但是随着东西深入接触,剖析,结果却非如此。接下来给大家 说下CPU—CPU缓存

CPU一般包括三级缓存分别为一级,二级,三级,随着级别越大速度越慢。

一级缓存,一般比较小,也是靠近CPU最近的,执行速度最快,在CPU一般有两个一级缓存,分别用来存储数据和指令

二级缓存,较之一级缓存来说距离稍远些,容量变大些,具体可能为256k等,一般CPU只有一个二级缓存

三级缓存,又离CPU更远了,不过他却是三个级别中最大,比如12m,也是速度最慢的相较一二级缓存来说,一般一个CPU插槽共享一个三级缓存,而一二级缓存,一般是一个核对应一个二级缓存,和两个一级缓存

所以当CPU运行时,级别越大,距离CPU越远,执行的耗时越明显,一般数据过程,先从一级缓存开始查找数据,没有的话,找到二级缓存,仍没有,则查找三级缓存,最后仍然没有数据,就会从内存中获取数据,这也意味着数据查找的路径越长,执行过程越耗时,所以可以将数据放在一级缓存处,提高执行速度。

关于这三个级别缓存的具体情况,各位还是通过相关工具或者相应的命令具体了解一下。

那么接下来了解一个内容,缓存行

缓存行一般是64个字节,也是缓存读取的最小单位,缓存读取数据,按照一行一行来读取,而非一个字节来读取。举个例子

public class L1CacheMiss {

   private static final int RUNS = 10;

   private static final int DIMENSION_1 = 1024 * 1024;

   private static final int DIMENSION_2 = 6;

   private static long[][] longs;

   public static void main(String[] args) throws Exception {

       Thread.sleep(10000);

       longs = new long[DIMENSION_1][];

       for (int i = 0; i < DIMENSION_1; i++) {

           longs[i] = new long[DIMENSION_2];

           for (int j = 0; j < DIMENSION_2; j++) {

               longs[i][j] = 0L;

           }

       }

       System.out.println("starting....");

       long sum = 0L;

       for (int r = 0; r < RUNS; r++) {

           final long start = System.nanoTime();

           //slow

//            for (int j = 0; j < DIMENSION_2; j++) {

//                for (int i = 0; i < DIMENSION_1; i++) {

//                    sum += longs[i][j];

//                }

//            }

           //fast

           for (int i = 0; i < DIMENSION_1; i++) {

               for (int j = 0; j < DIMENSION_2; j++) {

                   sum += longs[i][j];

               }

           }

           System.out.println((System.nanoTime() - start));

       }

   }

}

每次开始内循环时,从内存抓取的数据块实际上覆盖了longs[i][0]到longs[i][5]的全部数据(刚好64字节)。因此,内循环时所有的数据都在L1缓存可以命中,遍历将非常快。

假如,将32-36行‘代码注释而用25-29行代码代替,那么将会造成大量的缓存失效。因为每次从内存抓取的都是同行不同列的数据块(如longs[i][0]到longs[i][5]的全部数据),但循环下一个的目标,却是同列不同行(如longs[0][0]下一个是longs[1][0],造成了longs[0][1]-longs[0][5]无法重复利用)。

一般来说,缓存失效有三种情况:

1. 第一次访问数据, 在cache中根本不存在这条数据, 所以cache miss, 可以通过prefetch解决。

2. cache冲突, 需要通过补齐来解决(伪共享的产生)。

3. cache满, 一般情况下我们需要减少操作的数据大小, 尽量按数据的物理顺序访问数据。

猜你喜欢

转载自dalan-123.iteye.com/blog/2230090
今日推荐