性能杀手—伪共享
来源:互联网 发布:ui平面设计软件 编辑:程序博客网 时间:2024/04/29 19:14
在之前的一篇文章<java缓存行与volatile>中有讲到缓存行(可以到我的公众号中查找,公众号二维码在下方),缓存行是CPU缓存的最小单位,缓存行的大小不固定,根据具体CPU架构而定,一般是64BYTE。而就是因为缓存行的存在,导致了一个潜在的性能问题—伪共享。
了解缓存行带来的性能问题前先来了解以下概念:
一、CPU的缓存结构,现代CPU除去寄存器外就是三级缓存(L1、L2、L3),其中L1、L2是CPU各个核心独有的,而L3是所有核心共有的,CPU读取一个数据首先会从寄存器中读取,然后是从三级缓存中查找,如果在缓存查找不到则会去内存查找,图示如下:
同时在三级缓存中L1又是最快的,所以对于比较频繁使用的数据尽量保证能在L1中命中是最好的。
二、CPU读取一个数据时如果该数据不能完全填充一个缓存行,那么CPU将会多读取一部分数据进来填充缓存行。
有了以上基础,我们重新来看我们的问题,在java中,如果想要保证一个变量的修改在线程间是可见的,那么需要加上volatile关键字修饰,volatile会保证CPU不会读取到过期的数据,那么具体是如何做的呢?首先CPU的某个核心A会将volatile关键字修饰的变量读取(copy一份副本)到L1缓存或L2缓存中以供后续使用,同时另外一个核心B也会做同样的操作,在过了一段时间后,核心B将该变量的值修改了,由于此变量是volatile修饰的,该值会被同步到L3缓存,同时核心A会得到通知(如果没有volatile关键字修饰,那么核心A可能永远不知道该变量已经被修改失效了),知道该缓存已经失效,核心A会重新从L3中获取该变量的值。
volatile修饰的变量修改大概逻辑就是这样,乍一看好像没有什么问题,但是结合前边的基础——CPU不仅会将当前需要使用的数据读取进来,由于缓存最小单位是缓存行,也就是如果该缓存行如果没有被填充满CPU会顺带往后继续读取一部分数据,问题就出来了。具体问题如下:
假设现在有两个变量,两个变量的大小都是32byte(只要两个变量大小都不等于64byte即可),同时都用volatile修饰,又有两个核心分别使用它们,也就是核心A使用变量a,核心B使用变量b,而恰巧a和b又在同一缓存行上,那么现在问题来了,如果核心A修改了变量a的值,由于volatile的原因和a与b在同一缓存行的前提下,核心B的缓存b也会失效,为什么?因为核心A修改了a的值使该缓存行失效了,而b也在该缓存行上,虽然b的值没有被A修改,但是b在核心B的缓存依然失效了,也就是说B此时需要重新从比较慢的L3缓存读取最新值,从逻辑上来讲a和b是互不相干的,但是在底层的CPU缓存操作上他们却又是互相影响的,而L1、L2缓存的失效则会导致CPU读取速度变慢,最终导致程序变慢,该场景图示如下:
该问题称为伪共享,因为你访问a(缓存a)的同时也会把你不需要的b缓存下来。
那么如何应对这种情况呢?有一个很简单同时被很多高性能框架采用的办法,那就是缓存行填充,即通过一些无用的字段将你的volatile修饰的对象(变量)填充成64byte,不过该解决方案在某些情况下依然存在问题,例如缓存行大小不是64byte的情况下该解决方案就会失效。
该问题在JDK8中可以使用注解sun.misc.Contended解决,JVM会自动将使用该注解的变量分配到不同的缓存行。
时间紧,写的比较凌乱,就大概看下有个了解即可,深入了解的话可以自行再去看相关方面的书籍文献,也可以与我一起讨论研究~
没有关注的可以扫下方二维码关注我(公众号),如果在使用过程中有任何问题还可以加我QQ1213812243询问~
长按二维码关注我吧
不要错过
- 性能杀手—伪共享
- 伪共享,并发编程无声的性能杀手
- 伪共享(false sharing),并发编程无声的性能杀手
- Squid性能杀手——fwdFail分析
- 十佳性能杀手
- SQLServer性能杀手
- JavaScript 性能优化杀手
- 伪共享
- 伪共享
- 共享与伪共享
- 性能杀手:”潜伏”的memset
- 性能杀手:”潜伏”的memset
- 性能杀手:”潜伏”的memset
- 性能杀手:”潜伏”的memset .
- 性能杀手:”潜伏”的memset
- 性能杀手:”潜伏”的memset
- 识别SQL Server 性能杀手
- 【并发】伪共享 —— False Sharing
- POJ 1185
- C#Arraylist集合的方法
- mysql开发技巧2
- 使用 Java 测试网络连通性的几种方法
- 03-创建测试脚本框架
- 性能杀手—伪共享
- 激活函数(relu,prelu,elu,+BN)对比on cifar10
- 20171030
- LeetCode之两数之和
- javah提示找不到类文件
- [DFS] POJ 1426
- Java集合类
- 20171030
- 20171030