Java 理解CPU缓存(CPU Cache)

从Java视角理解系统结构连载, 关注我的微博(链接)了解最新动态 

众所周知, CPU是计算机的大脑, 它负责执行程序的指令; 内存负责存数据, 包括程序自身数据. 同样大家都知道, 内存比CPU慢很多. 其实在30年前, CPU的频率和内存总线的频率在同一个级别, 访问内存只比访问CPU寄存器慢一点儿. 由于内存的发展都到技术及成本的限制, 现在获取内存中的一条数据大概需要200多个CPU周期(CPU cycles), 而CPU寄存器一般情况下1个CPU周期就够了. 

CPU缓存 
网页浏览器为了加快速度,会在本机存缓存以前浏览过的数据; 传统数据库或NoSQL数据库为了加速查询, 常在内存设置一个缓存, 减少对磁盘(慢)的IO. 同样内存与CPU的速度相差太远, 于是CPU设计者们就给CPU加上了缓存(CPU Cache). 如果你需要对同一批数据操作很多次, 那么把数据放至离CPU更近的缓存, 会给程序带来很大的速度提升. 例如, 做一个循环计数, 把计数变量放到缓存里,就不用每次循环都往内存存取数据了. 下面是CPU Cache的简单示意图.  
 
随着多核的发展, CPU Cache分成了三个级别: L1, L2, L3. 级别越小越接近CPU, 所以速度也更快, 同时也代表着容量越小. L1是最接近CPU的, 它容量最小, 例如32K, 速度最快,每个核上都有一个L1 Cache(准确地说每个核上有两个L1 Cache, 一个存数据 L1d Cache, 一个存指令 L1i Cache). L2 Cache 更大一些,例如256K, 速度要慢一些, 一般情况下每个核上都有一个独立的L2 Cache; L3 Cache是三级缓存中最大的一级,例如12MB,同时也是最慢的一级, 在同一个CPU插槽之间的核共享一个L3 Cache. 

从CPU到大约需要的CPU周期大约需要的时间(单位ns)
寄存器1 cycle 
L1 Cache~3-4 cycles~0.5-1 ns
L2 Cache~10-20 cycles~3-7 ns
L3 Cache~40-45 cycles~15 ns
跨槽传输 ~20 ns
内存~120-240 cycles~60-120ns

感兴趣的同学可以在Linux下面用cat /proc/cpuinfo, 或Ubuntu下lscpu看看自己机器的缓存情况, 更细的可以通过以下命令看看: 
Shell代码  收藏代码
  1. $ cat /sys/devices/system/cpu/cpu0/cache/index0/size  
  2. 32K  
  3. $ cat /sys/devices/system/cpu/cpu0/cache/index0/type  
  4. Data  
  5. $ cat /sys/devices/system/cpu/cpu0/cache/index0/level   
  6. 1  
  7. $ cat /sys/devices/system/cpu/cpu3/cache/index3/level     
  8. 3  

就像数据库cache一样, 获取数据时首先会在最快的cache中找数据, 如果没有命中(Cache miss) 则往下一级找, 直到三层Cache都找不到,那只要向内存要数据了. 一次次地未命中,代表取数据消耗的时间越长. 

缓存行(Cache line) 
为了高效地存取缓存, 不是简单随意地将单条数据写入缓存的.  缓存是由缓存行组成的, 典型的一行是64字节. 读者可以通过下面的shell命令,查看cherency_line_size就知道知道机器的缓存行是多大. 
Shell代码  收藏代码
  1. $ cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size   
  2. 64  

CPU存取缓存都是按行为最小单位操作的. 在这儿我将不提及缓存的associativity问题, 将问题简化一些. 一个Java long型占8字节, 所以从一条缓存行上你可以获取到8个long型变量. 所以如果你访问一个long型数组, 当有一个long被加载到cache中, 你将无消耗地加载了另外7个. 所以你可以非常快地遍历数组. 

实验及分析 
我们在Java编程时, 如果不注意CPU Cache, 那么将导致程序效率低下. 例如以下程序, 有一个二维long型数组, 在我的32位笔记本上运行时的内存分布如图: 
 
32位机器中的java的数组对象头共占16字节(详情见 链接), 加上62个long型一行long数据一共占512字节. 所以这个二维数据是顺序排列的. 
Java代码  收藏代码
  1. public class L1CacheMiss {  
  2.     private static final int RUNS = 10;  
  3.     private static final int DIMENSION_1 = 1024 * 1024;  
  4.     private static final int DIMENSION_2 = 62;  
  5.   
  6.     private static long[][] longs;  
  7.   
  8.     public static void main(String[] args) throws Exception {  
  9.         Thread.sleep(10000);  
  10.         longs = new long[DIMENSION_1][];  
  11.         for (int i = 0; i < DIMENSION_1; i++) {  
  12.             longs[i] = new long[DIMENSION_2];  
  13.             for (int j = 0; j < DIMENSION_2; j++) {  
  14.                 longs[i][j] = 0L;  
  15.             }  
  16.         }  
  17.         System.out.println("starting....");  
  18.   
  19.         final long start = System.nanoTime();  
  20.         long sum = 0L;  
  21.         for (int r = 0; r < RUNS; r++) {  
  22. //          for (int j = 0; j < DIMENSION_2; j++) {  
  23. //              for (int i = 0; i < DIMENSION_1; i++) {  
  24. //                  sum += longs[i][j];  
  25. //              }  
  26. //          }  
  27.   
  28.             for (int i = 0; i < DIMENSION_1; i++) {  
  29.                 for (int j = 0; j < DIMENSION_2; j++) {  
  30.                     sum += longs[i][j];  
  31.                 }  
  32.             }  
  33.         }  
  34.         System.out.println("duration = " + (System.nanoTime() - start));  
  35.     }  
  36. }  

编译后运行,结果如下 
Shell代码  收藏代码
  1. $ java L1CacheMiss   
  2. starting....  
  3. duration = 1460583903  

然后我们将22-26行的注释取消, 将28-32行注释, 编译后再次运行,结果是不是比我们预想得还糟? 
Shell代码  收藏代码
  1. $ java L1CacheMiss   
  2. starting....  
  3. duration = 22332686898  

前面只花了1.4秒的程序, 只做一行的对调要运行22秒. 从上节我们可以知道在加载longs[i][j]时, longs[i][j+1]很可能也会被加载至cache中, 所以立即访问longs[i][j+1]将会命中L1 Cache, 而如果你访问longs[i+1][j]情况就不一样了, 这时候很可能会产生 cache miss导致效率低下. 
下面我们用perf来验证一下,先将快的程序跑一下. 
Shell代码  收藏代码
  1. $ perf stat -e L1-dcache-load-misses java L1CacheMiss   
  2. starting....  
  3. duration = 1463011588  
  4.   
  5.  Performance counter stats for 'java L1CacheMiss':  
  6.   
  7.        164,625,965 L1-dcache-load-misses                                         
  8.   
  9.       13.273572184 seconds time elapsed  

一共164,625,965次L1 cache miss, 再看看慢的程序 
Shell代码  收藏代码
  1. $ perf stat -e L1-dcache-load-misses java L1CacheMiss   
  2. starting....  
  3. duration = 21095062165  
  4.   
  5.  Performance counter stats for 'java L1CacheMiss':  
  6.   
  7.      1,421,402,322 L1-dcache-load-misses                                         
  8.   
  9.       32.894789436 seconds time elapsed  

这回产生了1,421,402,322次 L1-dcache-load-misses, 所以慢多了. 

以上我只是示例了在L1 Cache满了之后才会发生的cache miss. 其实cache miss的原因有下面三种: 
1. 第一次访问数据, 在cache中根本不存在这条数据, 所以cache miss, 可以通过prefetch解决. 
2. cache冲突, 需要通过补齐来解决. 
3. 就是我示例的这种, cache满, 一般情况下我们需要减少操作的数据大小, 尽量按数据的物理顺序访问数据. 
具体的信息可以参考这篇论文. 

转载于:https://www.cnblogs.com/kool/p/6695727.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/375173.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Homebrew OS X 不可或缺的套件管理器

Homebrew OS X 不可或缺的套件管理器,可以说Homebrew就是mac下的apt-get、yum. 1.安装homebrew brew的安装很简单&#xff0c;使用一条ruby命令即可&#xff0c;Mac系统上已经默认安装了ruby。 ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install…

【BZOJ】【1003】【ZJOI2006】物流运输trans

最短路/DP 这题数据规模并不大&#xff01;&#xff01;这是重点……… 所以直接暴力DP就好了&#xff1a;f[i]表示前 i 天的最小花费&#xff0c;则有$f[i]min\{f[j]cost[j1][i]k\} (0\leq j \leq i-1)$其中cost数组表示第L天到第R天只用一种运输方案连续运$R-L1$天的最小代价…

[傅里叶变换及其应用学习笔记] 二十四. 级联,脉冲响应

我们上节课学习了 在离散有限维空间中&#xff0c;任何线性系统都是通过矩阵间的相乘得到的在连续无限维空间中&#xff0c;任何线性系统都是通过对核函数的积分得到的脉冲响应&#xff08;impulse response&#xff09; 级联线性系统&#xff08;Cascading linear system&…

团队开发——用户需求调研报告

用户需求调研报告 项目名称&#xff1a; 躲避小球 项目编号&#xff1a;001 调研主题&#xff1a; 用户需求 访谈时间&#xff1a;2015.4.10 调研地点&#xff1a; 石家庄铁道大学图书馆 访谈部门&#xff1a; 三个人行 参与人员&#xff1a; 林彦汝 1. 访谈目的 1、让用…

设计模式(十五):解释器模式

一、定义 在设定环境中&#xff0c;定义一种规则或者语法&#xff0c;通过解释器来解释规则或者语法的含义. 二、实例&#xff1a;将 二十一 —> 21 2.1 设定我们的环境 Context public class Context{public string Input { get; set; }public int Output { get; se…

MySQL 5.7.10 免安装配置

# 配置环境&#xff1a;windows 64bit # 安装版本&#xff1a;mysql-5.7.10-win32&#xff08;zip archive版本&#xff09; 1. ZIP Archive版是免安装的&#xff0c;只需把mysql-5.7.10-win32.zip解压到安装目录即可。 2. 在D:\Program Files\mysql-5.7.10-win32文件夹下新建配…

fortran语法笔记

1&#xff0c;数据类型&#xff0c;fortran支持整形&#xff0c;real型&#xff0c;logical型&#xff0c;char型&#xff0c;复数型。整形分为为长整形和短整形定义长整形的方法 同时声明多个变量的话可以用逗号隔开。 加两个冒号的话可以直接在声明的时候赋值。 fortran是唯一…

BSA基础数据维护

平台 BSA基础数据维护 。扇区五个字段的内容 本来值为0&#xff0c;经过107上计算解析&#xff0c;得出正常的数值。然后106上报&#xff08;200050&#xff09;&#xff0c;得到回复&#xff08;200051&#xff09;。 查看回复数据&#xff0c;是否有错误。比如提示104 基站拼…

API函数MessageBox的参数与返回值

Win32汇编函数的参数,参数类型,返回值都是一个dword类型(4字节) 返回值永远放在EAX中,如超过4个字节则返回一个数据的指针(指向返回值存放的缓冲区地址).data titleS db helloworld,0 messageS db hello,welcome to win32,0.code start:invoke MessageBox,NULL,offset message…

The initialize list of C++ Class

性能问题之外&#xff0c;有些时场合初始化列表是不可或缺的&#xff0c;以下几种情况时必须使用初始化列表 常量成员&#xff0c;因为常量只能初始化不能赋值&#xff0c;所以必须放在初始化列表里面 Error1(constchar* constmsg) :data(msg) { //data msg; } 引用类型&…

PYTHON--定期监测服务器端口,并将结果写入MYSQL

定时监测服务器端口&#xff0c;然后将结果入写数据库。 监测用NC命令&#xff0c;入库就用PYTHON的MYSQL模块 再调一个基于函数的多线程。。。 妥妥的。。 是网上两个功能的合成。。 俺不生产代码&#xff0c;俺只是BAIDU的搬运工&#xff01; #!/usr/bin/env python import m…

MS_DOS头部 IMAGE_DOS_HEADER

MS_DOS头部 IMAGE_DOS_HEADER STRUCT{00H WORD e_magic ;DOS可执行文件标记字符串MZ(4D 5A)3CH DWORD e_1fanew ;指向PE文件头} IMAGE_DOS_HEADER ENDS用十六进制编辑器打开一个EXE文件 如QQ.EXEe_magic5A 4D e_1fanew00000100H---->此地址指向PE头文件

计算机原理学习(6)-- x86-32 CPU和内存管理之分页管理

前言 上一篇我们了解了x86-16 CPU计算机的内存访问方式&#xff0c;寻址方式&#xff0c;以及基于MS-DOS的应用程序的内存布局。这一篇会主要介绍32位处理器的内存访问&#xff0c;内存管理以及应用程序的内存布局。虽然目前64位CPU已经非常普及了&#xff0c;不过相对于32位的…

socket通信简介

前言 我们深谙信息交流的价值&#xff0c;那网络中进程之间如何通信&#xff0c;如我们每天打开浏览器浏览网页时&#xff0c;浏览器的进程怎么与web服务器通信的&#xff1f;当你用QQ聊天时&#xff0c;QQ进程怎么与服务器或你好友所在的QQ进程通信&#xff1f;这些都得靠sock…

SQL Server自动化运维系列——关于邮件通知那点事(.Net开发人员的福利)

需求描述 在我们的生产环境中&#xff0c;大部分情况下需要有自己的运维体制&#xff0c;包括自己健康状态的检测等。如果发生异常&#xff0c;需要提前预警的&#xff0c;通知形式一般为发邮件告知。 邮件作为一种非常便利的预警实现方式&#xff0c;在及时性和易用性方面也有…

So easy Webservice 1.Socket建设web服务

socket 是用来进行网络通讯的&#xff0c;简单来说&#xff0c;远程机器和本地机器各建一个socket&#xff0c;然后通过该socket进行连接通讯 socket简单模型图&#xff1a; socket的原理图&#xff1a; 代码实现&#xff1a; 1.创建socket服务端&#xff1a;用于处理客户端发送…

Java 自带MD5 校验文件

http://www.iteye.com/topic/1127319 前天第一次发表博客到论坛&#xff0c;关于Java文件监控一文&#xff0c;帖子地址在&#xff1a;http://www.iteye.com/topic/1127281 评论的朋友很多&#xff0c;下载代码的朋友很不少&#xff0c;感谢在论坛上看我帖子的朋友&#xff0c;…

决策树资料汇总

2012年8月26日决策树&#xff08;Decision tree&#xff09;决策树是以实例为基础的归纳学习算法。它从一组无次序、无规则的元组中推理出决策树表示形式的分类规则。它采用自顶向下的递归方式&#xff0c;在决策树的内部结点进行属性值的比较&#xff0c;并根据不同的属性值从…

metasploitable2渗透测试

一、系统弱密码登录 1、在kali上执行命令行telnet 192.168.26.129 2、Login和password都输入msfadmin 3、登录成功&#xff0c;进入系统 4、测试如下&#xff1a; 二、MySQL弱密码登录&#xff1a; 1、在kali上执行mysql –h 192.168.26.129 –u root 2、登录成功&#…

Portainer.io:让容器管理变得更加直观

在现代软件开发和部署中&#xff0c;容器化技术已经变得越来越流行。Docker 是其中一种领先的容器化平台&#xff0c;而 Portainer.io 则是一个优秀的管理工具&#xff0c;使得 Docker 的使用变得更加简单和可视化。本文将介绍 Portainer.io 的基本功能和如何在 Docker 上安装和…