伪共享问题-并发编程无声的性能杀手

本文以LongAdder源码为例进行说明。关于原子累加器的论述可以参考文章:原子累加器LongAdder与AtomicLong

1.LongAdder部分源码在这里插入图片描述
2.多核机器的存储结构
CPU为了提升性能,在设计上都设计了多级缓存。一个CPU会分多个核心,每个CPU核心都有自己的一级缓存、二级缓存,多个核心之间可以共享三级缓存,多个CPU之间可以共享内存。
在这里插入图片描述
时间对比
在这里插入图片描述
3.伪共享问题
因为CPU与内存的速度差异很大,需要靠预读数据至缓存来提升效率。而缓存以缓存行为单位,每个缓存行对应着一块内存,一般是64byte(8个long)。缓存的加入会造成数据副本的产生,即同一份数据会缓存在不同核心的缓存行中。CPU要保证数据的一致性,如果某个CPU核心更改了数据,其它CPU核心对应的长高缓存行必须失效。
在这里插入图片描述
因为Cell是数组形式,在内存中是连续存储的,一个Cell为24字节(16个字节的对象头和8字节的value),因此缓存行可以存下2个的Cell对象,这样问题就来了:
Core-0 要修改Cell[0]
Core-1 要修改Cell[1]
无论谁修改成功,都会导致对方Core的缓存行失效,比如Core-0中Cell[0]=6000,Cell[1]=8000要累加Cell[0]=6001,Cell[1]=8000,这时会让Core-1的缓存行失效。
总结:缓存系统中是以缓存行(cache line)为单位存储的,当多线程修改互相独立的变量时,如果这些变量共享同一个缓存行,就会无意中影响彼此的性能,这就是伪共享。即由于读写对应同一块内存的缓存行失效,导致的效率降低。
4.伪共享问题的解决方案
@sun.misc.Contended用来解决这个问题,它的原理是在使用此注解的对象或字段的前后各增加128自己大小的padding,从而让CPU将对象预读至缓存时占用不同的缓存行,这样,不会造成对方缓存行的失效。
在对象前后加上间隙,防止占用同一个缓存行,从而避免伪共享。代价就是让对象占用的内存变大了,鱼与熊掌不能兼得。
在这里插入图片描述
欢迎大家积极留言交流学习心得,点赞的人最美丽,谢谢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值