原文地址:http://ifeve.com/false-sharing/
作者:Martin Thompson 译者:丁一
缓存系统中是以缓存行(cache line)为单位存储的。缓存行是2的整数幂个连续字节,一般为32-256个字节。最常见的缓存行大小是64个字节。当多线程修改互相独立的变量时,如果这些变量共享同一个缓存行,就会无意中影响彼此的性能,这就是伪共享。缓存行上的写竞争是运行在SMP系统中并行线程实现可伸缩性最重要的限制因素。有人将伪共享描述成无声的性能杀手,因为从代码中很难看清楚是否会出现伪共享。
为了让可伸缩性与线程数呈线性关系,就必须确保不会有两个线程往同一个变量或缓存行中写。两个线程写同一个变量可以在代码中发现。为了确定互相独立的变量是否共享了同一个缓存行,就需要了解内存布局,或找个工具告诉我们。Intel VTune就是这样一个分析工具。本文中我将解释Java对象的内存布局以及我们该如何填充缓存行以避免伪共享。
|
图 1.
|
图1说明了伪共享的问题。在核心1上运行的线程想更新变量X,同时核心2上的线程想要更新变量Y。不幸的是,这两个变量在同一个缓存行中。每个线程都要去竞争缓存行的所有权来更新变量。如果核心1获得了所有权,缓存子系统将会使核心2中对应的缓存行失效。当核心2获得了所有权然后执行更新操作,核心1就要使自己对应的缓存行失效。这会来来回回的经过L3缓存,大大影响了性能。如果互相竞争的核心位于不同的插槽,就要额外横跨插槽连接,问题可能更加严重。
Java内存布局(Java Memory Layout)
对于HotSpot JVM,所有对象都有两个字长的对象头。第一个字是由24位哈希码和8位标志位(如锁的状态或作为锁对象)组成的Mark Word。第二个字是对象所属类的引用。如果是数组对象还需要一个额外的字来存储数组的长度。每个对象的起始地址都对齐于8字节以提高性能。因此当封装对象的时候为了高效率,对象字段声明的顺序会被重排序成下列基于字节大小的顺序:
- doubles (8) 和 longs (8)
- ints (4) 和 floats (4)
- shorts (2) 和 chars (2)
- booleans (1) 和 bytes (1)
- references (4/8)
- <子类字段重复上述顺序>
(译注:更多HotSpot虚拟机对象结构相关内容:http://www.infoq.com/cn/articles/jvm-hotspot)
了解这些之后就可以在任意字段间用7个long来填充缓存行。在Disruptor里我们对RingBuffer的cursor和BatchEventProcessor的序列进行了缓存行填充。
为了展示其性能影响,我们启动几个线程,每个都更新它自己独立的计数器。计数器是volatile long类型的,所以其它线程能看到它们的进展。
01
|
public final class FalseSharing
|
04
|
public final static int NUM_THREADS = 4 ; // change
|
05
|
public final static long ITERATIONS = 500L * 1000L * 1000L;
|
06
|
private final int arrayIndex;
|
08
|
private static VolatileLong[] longs = new VolatileLong[NUM_THREADS];
|
11
|
for ( int i = 0 ; i < longs.length; i++)
|
13
|
longs[i] = new VolatileLong();
|
17
|
public FalseSharing( final int arrayIndex)
|
19
|
this .arrayIndex = arrayIndex;
|
22
|
public static void main( final String[] args) throws Exception
|
24
|
final long start = System.nanoTime();
|
26
|
System.out.println( "duration = " + (System.nanoTime() - start));
|
29
|
private static void runTest() throws InterruptedException
|
31
|
Thread[] threads = new Thread[NUM_THREADS];
|
33
|
for ( int i = 0 ; i < threads.length; i++)
|
35
|
threads[i] = new Thread( new FalseSharing(i));
|
38
|
for (Thread t : threads)
|
43
|
for (Thread t : threads)
|
51
|
long i = ITERATIONS + 1 ;
|
54
|
longs[arrayIndex].value = i;
|
58
|
public final static class VolatileLong
|
60
|
public volatile long value = 0L;
|
61
|
public long p1, p2, p3, p4, p5, p6; // comment out
|
结果(Results)
运行上面的代码,增加线程数以及添加/移除缓存行的填充,下面的图2描述了我得到的结果。这是在我4核Nehalem上测得的运行时间。
|
图 2.
|
从不断上升的测试所需时间中能够明显看出伪共享的影响。没有缓存行竞争时,我们几近达到了随着线程数的线性扩展。
这并不是个完美的测试,因为我们不能确定这些VolatileLong会布局在内存的什么位置。它们是独立的对象。但是经验告诉我们同一时间分配的对象趋向集中于一块。
所以你也看到了,伪共享可能是无声的性能杀手。
注意:更多伪共享相关的内容,请阅读我后续blog。
原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: 伪共享(False Sharing)
伪共享(False Sharing)相关推荐
- 从缓存行出发理解volatile变量、伪共享False sharing、disruptor
volatile关键字 当变量被某个线程A修改值之后,其它线程比如B若读取此变量的话,立刻可以看到原来线程A修改后的值 注:普通变量与volatile变量的区别是volatile的特殊规则保证了新值能 ...
- 从Java视角理解伪共享(False Sharing)
从我的前一篇博文中, 我们知道了CPU缓存及缓存行的概念, 同时用一个例子说明了编写单线程Java代码时应该注意的问题. 下面我们讨论更为复杂, 而且更符合现实情况的多核编程时将会碰到的问题. 这些问 ...
- @sun.misc.Contended避免伪共享(false sharing)
转载自:http://www.myexception.cn/program/1630142.html Java8中使用sun.misc.Contended注解来避免伪共享(false sharing) ...
- Java8中用sun.misc.Contended避免伪共享(false sharing)
转自:http://budairenqin.iteye.com/blog/2048257 关于伪共享这个概念,请先参照http://ifeve.com/falsesharing/ 伪共享的样子: Ja ...
- C语言多线程教程(pthread)(线程创建pthread_t,指定线程run方法pthread_create,加mutex锁,解锁,伪共享 false sharing【假共享】)
[C语言]多线程程序入门教程 文章目录 查看pthread_create()函数文档 · Demo1 单线程(创建线程pthread_t .创建线程run方法pthread_create) · Dem ...
- 好理解的Java内存虚假共享(False Sharing)性能损耗以及解决方案
虚假共享(False Sharing)也有人翻译为伪共享 参考 https://en.wikipedia.org/wiki/False_sharing 在计算机科学中,虚假共享是一种性能降低的使用模式 ...
- 伪共享(false sharing),并发编程无声的性能杀手
在并发编程过程中,我们大部分的焦点都放在如何控制共享变量的访问控制上(代码层面),但是很少人会关注系统硬件及 JVM 底层相关的影响因素.前段时间学习了一个牛X的高性能异步处理框架 Disruptor ...
- 多线程中的volatile和伪共享
伪共享 false sharing,顾名思义,"伪共享"就是"其实不是共享".那什么是"共享"?多CPU同时访问同一块内存区域就是&qu ...
- 伪共享,并发编程无声的性能杀手
伪共享,并发编程无声的性能杀手 在并发编程过程中,我们大部分的焦点都放在如何控制共享变量的访问控制上(代码层面),但是很少人会关注系统硬件及 JVM 底层相关的影响因素.前段时间学习了一个牛X的高性能 ...
最新文章
- C# winform中MouseDoubleClick与DoubleClick的区别
- 为Process.waitFor设置超时
- iphone无法连接电脑_电脑开机无线网卡无法自动连接网络怎么办?
- 全屏背景:15个jQuery插件实现全屏背景图像或媒体
- (2)谷歌2011校园招聘:
- jax-ws和jax-rs_JAX-RS和OpenAPI对Hypermedia API的支持:任重而道远
- 关于HTTPS的几个问题
- 设计干货栅格系统素材 | UI设计师应用好帮手
- 微信“再罚”今日头条;马云:没有女性就没有阿里;有赞强推 996 工作制 | 极客头条...
- 前后端分离的思考与实践(六)
- FPGA入门——初学建议
- 〖EXP〗NSA MS17010永恒之蓝漏洞一键工具
- ES集群不通,日志报[node-3] not enough master nodes discovered during pinging (found [[Candidate{node={node-3
- VR全景的拍摄以及优势
- 数分笔记整理25 - 数据处理项目 - 中国城市资本流动问题探索
- HDMI2.0/HDCP2.2 4x2 矩阵——GSV2008
- Cortana小娜失败背后,微软的傲慢与偏见
- Elasticsearch7.x索引index的备份和恢复,index索引误删恢复
- 2021京东Java面试真题:c和java哪个更适合开发游戏
- 前端需要了解的颜色模型,RGB、HSL和HSV
热门文章
- spring源码分析之cache demo
- JDK动态代理实现原理--转载
- The declared package does not match the expected package
- error_reporting()
- Fabric 链码Chaincode 的安装、初始化、调用、升级
- 机器学习中用来防止过拟合的方法有哪些?
- 盘点过去一年,MIT人工智能实验室的那些创新
- C语言基础排序算法-选择排序
- 如何备份被独占文件?
- Apache ZooKeeper - 节点实操常用zookeeper命令