原文:False Shareing && Java 7 (依然是马丁的博客) 译者:杨帆 校对:方腾飞
在我前一篇有关伪共享的博文中,我提到了可以加入闲置的long字段来填充缓存行来避免伪共享。但是看起来Java 7变得更加智慧了,它淘汰或者是重新排列了无用的字段,这样我们之前的办法在Java 7下就不奏效了,但是伪共享依然会发生。我在不同的平台上实验了一些列不同的方案,并且最终发现下面的代码是最可靠的。(译者注:下面的是最终版本,马丁在大家的帮助下修改了几次代码)
01 |
import java.util.concurrent.atomic.AtomicLong;
|
03 |
public final class FalseSharing
|
06 |
public final static int NUM_THREADS = 4 ;
|
07 |
public final static long ITERATIONS = 500L * 1000L * 1000L;
|
08 |
private final int arrayIndex;
|
10 |
private static PaddedAtomicLong[] longs = new PaddedAtomicLong[NUM_THREADS];
|
13 |
for ( int i = 0 ; i < longs.length; i++)
|
15 |
longs[i] = new PaddedAtomicLong();
|
19 |
public FalseSharing( final int arrayIndex)
|
21 |
this .arrayIndex = arrayIndex;
|
24 |
public static void main( final String[] args) throws Exception
|
26 |
final long start = System.nanoTime();
|
28 |
System.out.println( "duration = " + (System.nanoTime() - start));
|
31 |
private static void runTest() throws InterruptedException
|
33 |
Thread[] threads = new Thread[NUM_THREADS];
|
35 |
for ( int i = 0 ; i < threads.length; i++)
|
37 |
threads[i] = new Thread( new FalseSharing(i));
|
40 |
for (Thread t : threads)
|
45 |
for (Thread t : threads)
|
53 |
long i = ITERATIONS + 1 ;
|
56 |
longs[arrayIndex].set(i);
|
61 |
public static long sumPaddingToPreventOptimisation( final int index)
|
63 |
PaddedAtomicLong v = longs[index];
|
64 |
return v.p1 + v.p2 + v.p3 + v.p4 + v.p5 + v.p6;
|
67 |
public static class PaddedAtomicLong extends AtomicLong
|
69 |
public volatile long p1, p2, p3, p4, p5, p6 = 7L;
|
用以上这种办法我获得了和上一篇博客里提到的相近的性能,读者可以把PaddedAtomicLong里面那行填充物注释掉再跑测试看看效果。
我想我们大家都有权去跟Oracle投诉,让他们在JDK里默认加入缓存行对齐的函数或者是被填充好的原子类型,这和其他一些底层改变会让Java成为一门真真正正的并发编程语言。我们一直以来不断的在听到他们讲多核时代正在到来,但是我要说的是在这方面Java需要快点赶上来。
———————————————–
(译者注:博文后面的评论和交流也很精彩,也讲述了这段示例代码的进化过程,一起翻译出来:)
1楼:Ashwin Jayaprakash
在前一篇博文中你创建了一个数组来放VolatileLong,这次你又用一个数组放AtomicLongArray(译者注:此处我觉得他可能是写错了,应该是说AtomicLong吧)。
但是如何能保证AtomicLongArray或VolatileLong会被紧挨着分配在内存中?
那么,就算你在一个循环中创建他们,并且很幸运的,他们获得了连续的内存空间,但是依然无法保证这四个实例会在堆空间里紧挨着。如果他们被分布在JVM的旧生代堆里并且没有被压实的话,直到一次主要GC压实旧生代之前,重新分配填充是没必要的,因为他们在堆中是分散的。
所以你最好对读者说明,我们无法控制JVM如何在堆中对这些实例分配内存。(译者注:没办法,要精确控制内存来保证性能的话就不要用Java了,要不直接用C好了)
———————————————–
2楼:马丁
你大体上说的是对的,Ashwin,我们无法保证如何在堆空间中放置Java对象,这是伪共享问题发生的根源。如果你有一些跨线程的指针或者计数器,那么确保他们在不同的缓存行中是非常重要的,否则的话程序就无法按CPU的核数扩展。填充的根本意义在于保护这些跨线程的指针和计数器,以确保他们在不同的缓存行中。
这个是有意义的吧?
———————————————–
3楼:Ashwin Jayaprakash
嗯,有道理。那你可不可以创建一个大的AtomicLongArray,然后让不同的线程去更新第8,16,32个元素呢?(译者注:也算是消除竞争的一个办法,但是既然完全没有竞争还要多线程做什么?)而不是搞四个AtomicLongArray,而每个线程都去竞争访问同一个数组元素。
谢谢马丁花时间写了这么多。
———————————————–
4楼:马丁
如果我可以提前知道更多的业务逻辑那么你说的方式是可行的。但通常情况下在设计一个大型系统的时候,我们无法提前知道很多事情,或者我们要为其他的应用创造一个通用的类库。
我很难为很多不同的上下文场景写一个足够小巧简单的示例,而我上面写的示例是为了说明当伪共享发生的时候有多糟糕。如果你在你的数据结构中做了填充,那么你就不必担心他们在内存中如何分配。我们用一个更好的方案来替代AtomicLong,并且你可以使用AtomicLong的所有常规方法:
static class PaddedAtomicLong extends AtomicLong
{
public volatile long p1, p2, p3, p4, p5, p6, p7 = 7L;
}
我是多希望Java委员会可以认识到这个问题的严重性,并且在JDK里加入对缓存行对齐和填充的基础方法。这是在Disruptor中有关性能BUG的最大根源。
我也根据以上的反馈更新了文章。
———————————————–
5楼:Gil Tene
马丁,我很同意你的观点,如果我们有一种办法可以指定某个字段占有独自的缓存行,并且让JVM自动处理如何在对象布局上的正确填充,那这个世界会和谐的多。你搞的这个人造填充将会是很美好的一个事情,但是你也知道,实际上的对象布局情况要取决于JVM的特定实现。
我是一个偏执狂,我给你的填充方案里加了一些东西,使那些个用于填充的字段很难被JVM优化掉。一个耍小聪明的JVM还是会把你用于填充的P1-P7的字段优化掉,原理是这样滴:PaddedAtomicLong类如果只对final的FalseSharing类可见(就是说PaddedAtomicLong不能再被继承了)。这样一来编译器就会“知道”它正在审视的是所有可以看到这个填充字段的代码,这样就可以证明没有行为依赖于p1到p7这些字段。那么“聪明”的JVM会把上面这些丝毫不占地方的字段统统优化掉。
那么针对这样的情况,你可以巧妙的让PaddedAtomicLong类在FalseSharing类之外可见,比如直接加一个依赖于p1到p7的公开的访问函数,并且这个函数在理论上可以被外界访问到。
———————————————–
6楼:马丁
我根据Gil的反馈做了修改。
———————————————–
7楼:Stanimir Simeonoff
直接用一个数组并且把元素放在中间的位置上(或者直接用bytebuffer,而你却为了这个写了这么一大篇),Java是不会重排他们的,我就是这样来消除伪共享的。
———————————————–
8楼:马丁
我以前经常像你这么干,比如搞一个长度是15的数组,把元素放在正中间,但是,如果我们需要volatile这个语意就行不通了。对于你的情况来说,你只需要用AtomicLongArray或者类似的。根据我的测量,在一个算法中,这种间接引用(译者注:原词是indirection,我理解也许是指间接引用,即不是直接使用数组,而是使用AtomicLongArray这种包装过的数组)和边界检查的消耗是显著的。
据我所知,一些人建议加入@Contened注解来标记一个字段,让这个被标记的字段拥有独立的缓存行,我希望这个快点到来。
8.1楼:John
你好,马丁,我看到在Disruptor当前的版本中Sequence类用的是unsafe.compareAndSwapLong(..)来更新第七个下标的long。
为什么不数组的长度不是15或者是其他的数值?如果长度是15的话会把2级缓存的缓存行也填充掉么?
谢谢。
8.2楼:马丁
因为用7个下标保证了会有56个字节填充在数值的任何一边,56字节的填充+8字节的long数值正好装进一行64字节的缓存行。
———————————————–
9楼:Stanimir Simeonoff
是的,马丁,我指的就是AtomicLongArray,如果你不想为间接引用和边界检查买单,Unsafe 是一个选项(甚至总是这样)。
———————————————–
10楼:Mohan Radhakrishnan
哪里有一些简单硬件说明书是讲述缓存行的关键概念么?我想找一些插图什么的来理解核心和缓存直接如何交互造成了伪共享。
———————————————–
11楼:马丁
你可以参照下面这个PDF的第3和第4章:
http://img.delivery.net/cm50content/intel/ProductLibrary/100412_Parallel_Programming_02.pdf
———————————————–
12楼:ying
你的博客太NB了,多谢马丁,我关于填充有两个疑问:
1.long占8字节,对象引用占16字节,但是这个实现是
1 |
public final static class VolatileLong
|
3 |
public volatile long value = 0L;
|
4 |
public long p1, p2, p3, p4, p5, p6;
|
看起来好像是72个字节啊。
2.你是发现这个问题的?是去查汇编代码吗?
12.1楼:马丁
我不希望在缓存行中的标记字在取出锁或者垃圾回收器在老化对象的时候被修改。
就算默认启用64位模式的压缩指针,它还是会包含类指针在对象的头部。
https://wikis.oracle.com/display/HotSpotInternals/CompressedOops
这个伪共享的问题我是在多年前发现的,但是我为一个应用做性能测试,发现性能时高时低,追查原因下去发现是伪共享问题。
12.2楼:ying
那你是如何缩小问题的范围最后发现问题的呢?需要深入分析汇编代码么?
11.3楼:马丁
汇编代码是不会显示出问题的,你需要去追查为什么CPU的2级缓存总是不命中,追查下去就知道了。
———————————————–
13楼:Joachim
关于@Contended注解的提案在这里:
http://mail.openjdk.java.net/pipermail/hotspot-dev/2012-November/007309.html
牛文啊,赞!
相关推荐
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
MMC整流器技术解析:基于Matlab的双闭环控制策略与环流抑制性能研究,Matlab下的MMC整流器技术文档:18个子模块,双闭环控制稳定直流电压,环流抑制与最近电平逼近调制,优化桥臂电流波形,高效并网运行。,MMC整流器(Matlab),技术文档 1.MMC工作在整流侧,子模块个数N=18,直流侧电压Udc=25.2kV,交流侧电压6.6kV 2.控制器采用双闭环控制,外环控制直流电压,采用PI调节器,电流内环采用PI+前馈解耦; 3.环流抑制采用PI控制,能够抑制环流二倍频分量; 4.采用最近电平逼近调制(NLM), 5.均压排序:电容电压排序采用冒泡排序,判断桥臂电流方向确定投入切除; 结果: 1.输出的直流电压能够稳定在25.2kV; 2.有功功率,无功功率稳态时波形稳定,有功功率为3.2MW,无功稳定在0Var; 3.网侧电压电流波形均为对称的三相电压和三相电流波形,网侧电流THD=1.47%<2%,符合并网要求; 4.环流抑制后桥臂电流的波形得到改善,桥臂电流THD由9.57%降至1.93%,环流波形也可以看到得到抑制; 5.电容电压能够稳定变化 ,工作点关键词:MMC
Boost二级升压光伏并网结构的Simulink建模与MPPT最大功率点追踪:基于功率反馈的扰动观察法调整电压方向研究,Boost二级升压光伏并网结构的Simulink建模与MPPT最大功率点追踪:基于功率反馈的扰动观察法调整电压方向研究,Boost二级升压光伏并网结构,Simulink建模,MPPT最大功率点追踪,扰动观察法采用功率反馈方式,若ΔP>0,说明电压调整的方向正确,可以继续按原方向进行“干扰”;若ΔP<0,说明电压调整的方向错误,需要对“干扰”的方向进行改变。 ,Boost升压;光伏并网结构;Simulink建模;MPPT最大功率点追踪;扰动观察法;功率反馈;电压调整方向。,光伏并网结构中Boost升压MPPT控制策略的Simulink建模与功率反馈扰动观察法
STM32F103C8T6 USB寄存器开发详解(12)-键盘设备
科技活动人员数专指直接从事科技活动以及专门从事科技活动管理和为科技活动提供直接服务的人员数量
Matlab Simulink仿真探究Flyback反激式开关电源性能表现与优化策略,Matlab Simulink仿真探究Flyback反激式开关电源的工作机制,Matlab Simulimk仿真,Flyback反激式开关电源仿真 ,Matlab; Simulink仿真; Flyback反激式; 开关电源仿真,Matlab Simulink在Flyback反激式开关电源仿真中的应用
基于Comsol的埋地电缆电磁加热计算模型:深度解析温度场与电磁场分布学习资料与服务,COMSOL埋地电缆电磁加热计算模型:温度场与电磁场分布的解析与学习资源,comsol 埋地电缆电磁加热计算模型,可以得到埋地电缆温度场及电磁场分布,提供学习资料和服务, ,comsol;埋地电缆电磁加热计算模型;温度场分布;电磁场分布;学习资料;服务,Comsol埋地电缆电磁加热模型:温度场与电磁场分布学习资料及服务
1、文件内容:ibus-table-chinese-yong-1.4.6-3.el7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/ibus-table-chinese-yong-1.4.6-3.el7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、更多资源/技术支持:公众号禅静编程坊
基于51单片机protues仿真的汽车智能灯光控制系统设计(仿真图、源代码) 一、设计项目 根据本次设计的要求,设计出一款基于51单片机的自动切换远近光灯的设计。 技术条件与说明: 1. 设计硬件部分,中央处理器采用了STC89C51RC单片机; 2. 使用两个灯珠代表远近光灯,感光部分采用了光敏电阻,因为光敏电阻输出的是电压模拟信号,单片机不能直接处理模拟信号,所以经过ADC0832进行转化成数字信号; 3. 显示部分采用了LCD1602液晶,还增加按键部分电路,可以选择手自动切换远近光灯; 4. 用超声模块进行检测距离;
altermanager的企业微信告警服务
MyAgent测试版本在线下载
Comsol技术:可调BIC应用的二氧化钒VO2材料探索,Comsol模拟二氧化钒VO2的可调BIC特性研究,Comsol二氧化钒VO2可调BIC。 ,Comsol; 二氧化钒VO2; 可调BIC,Comsol二氧化钒VO2材料:可调BIC技术的关键应用
C++学生成绩管理系统源码
基于Matlab与Cplex的激励型需求响应模式:负荷转移与电价响应的差异化目标函数解析,基于Matlab与CPLEX的激励型需求响应负荷转移策略探索,激励型需求响应 matlab +cplex 激励型需求响应采用激励型需求响应方式对负荷进行转移,和电价响应模式不同,具体的目标函数如下 ,激励型需求响应; matlab + cplex; 负荷转移; 目标函数。,Matlab与Cplex结合的激励型需求响应模型及其负荷转移策略
scratch介绍(scratch说明).zip
内容概要:本文全面介绍了深度学习模型的概念、工作机制和发展历程,详细探讨了神经网络的构建和训练过程,包括反向传播算法和梯度下降方法。文中还列举了深度学习在图像识别、自然语言处理、医疗和金融等多个领域的应用实例,并讨论了当前面临的挑战,如数据依赖、计算资源需求、可解释性和对抗攻击等问题。最后,文章展望了未来的发展趋势,如与量子计算和区块链的融合,以及在更多领域的应用前景。 适合人群:对该领域有兴趣的技术人员、研究人员和学者,尤其适合那些希望深入了解深度学习原理和技术细节的读者。 使用场景及目标:①理解深度学习模型的基本原理和结构;②了解深度学习模型的具体应用案例;③掌握应对当前技术挑战的方向。 阅读建议:文章内容详尽丰富,读者应在阅读过程中注意理解各个关键技术的概念和原理,尤其是神经网络的构成及训练过程。同时也建议对比不同模型的特点及其在具体应用中的表现。
该文档提供了一个关于供应链管理系统开发的详细指南,重点介绍了项目安排、技术实现和框架搭建的相关内容。 文档分为以下几个关键部分: 项目安排:主要步骤包括搭建框架(1天),基础数据模块和权限管理(4天),以及应收应付和销售管理(5天)。 供应链概念:供应链系统的核心流程是通过采购商品放入仓库,并在销售时从仓库提取商品,涉及三个主要订单:采购订单、销售订单和调拨订单。 大数据的应用:介绍了数据挖掘、ETL(数据抽取)和BI(商业智能)在供应链管理中的应用。 技术实现:讲述了DAO(数据访问对象)的重用、服务层的重用、以及前端JS的继承机制、jQuery插件开发等技术细节。 系统框架搭建:包括Maven环境的配置、Web工程的创建、持久化类和映射文件的编写,以及Spring配置文件的实现。 DAO的需求和功能:供应链管理系统的各个模块都涉及分页查询、条件查询、删除、增加、修改操作等需求。 泛型的应用:通过示例说明了在Java语言中如何使用泛型来实现模块化和可扩展性。 文档非常技术导向,适合开发人员参考,用于构建供应链管理系统的架构和功能模块。
这份长达104页的手册由清华大学新闻与传播学院新媒体研究中心元宇宙文化实验室的余梦珑博士后及其团队精心编撰,内容详尽,覆盖了从基础概念、技术原理到实战案例的全方位指导。它不仅适合初学者快速了解DeepSeek的基本操作,也为有经验的用户提供了高级技巧和优化策略。
主题说明: 1、将mxtheme目录放置根目录 | 将mxpro目录放置template文件夹中 2、苹果cms后台-系统-网站参数配置-网站模板-选择mxpro 模板目录填写html 3、网站模板选择好之后一定要先访问前台,然后再进入后台设置 4、主题后台地址: MXTU MAX图图主题,/admin.php/admin/mxpro/mxproset admin.php改成你登录后台的xxx.php 5、首页幻灯片设置视频推荐9,自行后台设置 6、追剧周表在视频数据中,节目周期添加周一至周日自行添加,格式:一,二,三,四,五,六,日
运行GUI版本,可二开