文档章节

理解CacheLine与写出更好的JAVA

Float_Luuu
 Float_Luuu
发布于 2015/12/06 00:40
字数 1747
阅读 3279
收藏 25

今天查了很多资料,主要是想搞清楚写JAVA和CacheLine有什么关系以及我们如何针对CacheLine写出更好的JAVA程序。


CPU和内存


CPU是计算机的大脑,它负责运算,内存是数据,它为CPU提供数据。这里之所以忽略其他存储设备是为了简化模型。假设我们面对的是具有两个核心的CPU,那么我们的模型大概如下面的样子:

CPU计算核心不会直接和内存打交道,它会直接从缓存拿数据,如果缓存没拿到,专业点说即缓存未命中的时候才会去内存去拿,同时会更新缓存。这个过程CPU不会仅仅读取需要的某个字节或字的内容,而会按策略读取一块内容。典型的处理器策略是向前进的方向读取小于2048字节的数据。如上图所示,L1缓存(一级缓存)离CPU内核最近,容量也最小同时造价也最高,属于内核独立使用。L2缓存离得远些,容量比L1大写但是还是属于独立的内核使用。L3缓存离的最远,也是最慢的缓存,这层缓存为所有内核共享。


缓存行


上面一节我们介绍了CPU和内存之间的模型,本节介绍下缓存行。CPU从缓存中读取内容并不是一个字节或一个字读的,而是一行一行,也可以理解为一块一块读的。CPU是这样设计的,我们想想,相邻数据的相关性往往很大,这么设计可以提高缓存的命中率,也降低访问缓存的次数。上面提到的一行叫做缓存行。典型的大小是32-256字节,其中最常见的是64字节。这是缓存一致性的最小粒度,如果一行中有一个字节哪怕一个位的内容内修改并写回内存,那么其他内核的该缓存行将会被标志位无效。假设两个内核正在执行不同的线程,并且操作同一个缓存行,A线程修改了缓存行的第一个字节,B线程需要访问第二个字节,这个时候该缓存行其实已经被进行了一次和内存同步的操作,保证该段和内存中该行数据一致,然而这个过程B线程访问的这个字节和第一个线程访问的字节并没有关系。这个时候有同学有疑问了,那么如果两个内核的线程同时对该段进行操作,也就是没有谁先谁后的情况,会出现什么情况呢。其实这里涉及到另外一个概念,叫缓存一致性协议(Cache Coherency)。即我们已经有了一个保证:在任意时刻,任意级别的缓存段中的内容,等同于它对应的内存中的内容。


关于AtomicReference


AtomicReference是由JAVA5引入的,用于对一个对象引用进行原子操作,我们可以看到AtomicReference的实现是用CAS技术对引用进行指令级别的原子修改然后再利用volatile带来的内存屏障特性保证引用的修改对其他线程立即可见。这里提一点,由volatile修饰的变量在写之后会插入一个store屏障,在读之前插入一个load屏障。store屏障保证写操作被后面的线程立即可见。load屏障保证所有的读操作之前的写立即生效。然而AtomicReference并没有避免缓存行带来的缓存命中率问题。一个AtomicReference对象包括一个volatile的对象引用,即这个对象在32位操作系统中占4个字节,在64位操作系统中占8个字节。虽然多个线程对同一个AtomicReference对象操作没有并发问题,但是当多个线程对多个AtomicReference操作的时候就有可能有缓存命中率问题。借着上文中的模型我们假设两个AtomicReference变量A和B位于同一内存相邻区域,当在核心1执行的线程对A变量操作的时候CPU会将A变量读入核心1的缓存区域,同时捎带把B变量读入缓存区域,此时和A变量位于同一缓存行。核心2执行的另外一个线程同时对B进行操作,这个时候该缓存行已经失效,会发生一次读内存操作。


缓存行填充


Exchanger类是JAVA5提供的用于多线程之间交换数据的工具类,我们看看Exchanger的内部类Slot的实现:

 private static final class Slot extends AtomicReference<Object> {
        // Improve likelihood of isolation on <= 64 byte cache lines
        long q0, q1, q2, q3, q4, q5, q6, q7, q8, q9, qa, qb, qc, qd, qe;
    }


Slot只是简单的继承了AtomicReference类,并声明了15个long类型的变量。如果不懂CacheLine的话不会明白这段无用变量的意义,这里声明了15个long类型的变量,一个long类型为8个字节,加上上面的引用在64位操作系统环境下为128字节,32位操作系统环境下的124字节也没问题,因为两个Slot类型变量不可能位于同一缓存行,这也就解决了多核CPU环境下的缓存航失效问题。



后记


很多JAVA程序员可能并不关心计算机底层的运行机制,认为了解这方面的知识略枯燥,对于实际开发然并卵。其实我并不这么认为,能够了解操作系统甚至计算机硬件的工作原理更有利于我们写出更好更快的程序。比如本文讨论的cacheline,知道cacheline的原理我们可以写出一定程度上避免缓存失效的JAVA代码,这是不是很有意思呢。其次要提到的问题是有些时候我们也并不适合用缓存行填充的方式写,比如在变量不会被频繁的更新的情况下,就不会有缓存失效,那么就不需要考虑这个问题,这么写反而使得CPU需要读取无用的数据,浪费了资源。







© 著作权归作者所有

共有 人打赏支持
Float_Luuu
粉丝 203
博文 46
码字总数 102357
作品 0
长宁
高级程序员
计算机科学中抽象的好处与问题—伪共享实例分析

David John Wheeler有一句名言“计算机科学中的任何问题都可以通过加上一层间接层来解决”,一层不够就再加一层。后半句是我加的 (* ̄︶ ̄) ,虽然有点玩笑的意思,但是也的确能说明一些问题...

MageekChiu
01/10
0
0
JAVA 拾遗 — CPU Cache 与缓存行

最近的两篇文章,介绍了我参加的中间件比赛中一些相对重要的优化,但实际上还存在很多细节优化,出于篇幅限制并未提及,在最近的博文中,我会将他们整理成独立的知识点,并归类到我的系列文章...

徐靖峰
07/21
0
0
从一个 JDK6 BUG 看 JAVA 数组创建

前几天在调试一段程序, 奇怪的发现: 程序性能和缓存大小是负相关的——缓存开越大,程序越慢,最快和最慢之间差 2 个数量级。开 jprofiler 查了下,发现了 JDK6 里有这样一段代码 (版本 buil...

长源
2012/06/08
0
0
Java 中的伪共享详解及解决方案

什么是伪共享 CPU 缓存系统中是以缓存行(cache line)为单位存储的。目前主流的 CPU Cache 的 Cache Line 大小都是 64 Bytes。在多线程情况下,如果需要修改“共享同一个缓存行的变量”,就...

Java技术栈
06/05
0
0
Java 8里面lambda的最佳实践

Java 8已经推出一段时间了,越来越多开发人员选择升级JDK,这条热门动弹里面看出,JDK7最多,其次是6和8,这是好事! 在8 里面Lambda是最火的主题,不仅仅是因为语法的改变,更重要的是带来了...

OSC闲人
2015/04/30
0
41

没有更多内容

加载失败,请刷新页面

加载更多

下一页

ES6 Promise

Promise promise是异步编程的一种解决方案 1 什么是异步? 异步模式,每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则...

NDweb
23分钟前
0
0
Python百分登顶年度编程语言排行榜

今天 IEEE Spectrum 发布了 2018 年度顶级编程语言排行榜。该榜单可根据流行趋势、职业方向、语言类型等多个标准各自细分生成榜单,也可以综合多个标准进行排列,使用者可以根据自己的需求过...

六库科技
27分钟前
0
0
consul笔记

安装和启动就不说了,很简单。 注册一个服务: http://localhost:8500/v1/agent/service/register put请求,json格式内容如下 { "ID": "userServiceId", //服务id "Name": "userService", //服...

朝如青丝暮成雪
27分钟前
0
0
iOS CollectionView 的那些事

UICollectionView是开发中用的比较多的一个控件,本文记录UICollectionView在开发中常用的方法总结,包括使用UICollectionViewFlowLayout实现Grid布局、添加Header/Footer、自定义layout布局...

aron1992
27分钟前
0
0
linux tar.gz zip 解压缩 压缩命令

http://apps.hi.baidu.com/share/detail/37384818 download ADT link http://dl.google.com/android/ADT-0.9.6.zip download SDK link http://dl.google.com/Android/android-sdk_r11-linux_......

owensliu
30分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部