有助于减少伪共享的@Contended注解
原文链接 作者:Dave 译者:卓二妹 校对:丁一
详细描述看Aleksey Shipilev这封邮件 —— 我们期待@Contended已久。JVM会自动为对象字段进行内存布局。通常JVM会这样做:(a)将对象的域按从大到小的顺序排列,以优化占用的空间;(b)打包引用类型的字段,以便垃圾收集器在追踪的时候能够处理相连的引用类型的字段。@Contended让程序能够更明确地控制并发和伪共享。通过该功能我们能够把那些频繁进行写操作的共享字段,从其它几乎是只读或只有少许写操作的字段中分离开来。原则很简单:对共享内容进行读操作的代价小,对共享内容的写操作代价非常高。我们也可以将那些可能会被同一个线程几乎同时写的字段打包到一起。
一般而言,我们试图改变相关字段的位置,以使得coherency misses的发生次数降到最低。在简单的单线程环境中,那些在时间上紧挨在一起被访问的字段,空间上应该放置在相邻的位置以提升缓存局部性[1]。也就是,时间局部性应该决定着空间局部性。时间上在一起访问的字段应该放到空间上相邻的位置。我们说过,当多线程并发访问我们的字段的时候,我们必须小心翼翼地以避免伪共享和因coherence traffic导致过多的缓存失效。同样地,对于那些原本是分开的字段,但可能会被同一个线程同时写入相同的缓存行,我们会试图将这些字段聚集到一起。注意:如果我们想极度地降低单线程下的容量缺失[2],那么在并发环境中可能出现大量的coherency misses。在原生C/C++代码中,程序员都在使用能感知并发的(concurrency-aware)结构布局。@Contended也应在JAVA中提供同样的功能,虽然在本地代码中,绑定字段到偏移量发生在编译阶段,但在JAVA中发生在装载阶段。值得指出的是,在一般情况下,找不到一个同时适用于单线程和多线程环境的最优布局。理想布局问题本身就是一个NP难题。
理想情况下,JAVA虚拟机应该使用硬件的监控设施来检测出共享行为,并在程序运行中改变布局。这是有一定困难的,因为我们还没有好的办法为JVM提供有效的信息。提示:我们需要取消操作系统和管理程序的中间层。另外一个挑战是,原始字段的偏移量有在unsafe工具中使用,因此我们需要解决这个问题,可能是使用一个额外的中间层。
最后,众所周知final字段是只读的,因此,我还希望能够将它们打包到一起。
校注
[1]:早在1968年,Denning.P就曾提出局部性原理:程序在执行时将呈现出局部性规律,即在一较短的时间内,程序的执行仅局限于某个部分;相应地,它所访问的存储空间也局限于某个区域。
[2]:根据缓存缺失的原因,可以分为以下几个类型:
- 强制缺失(Compulsory Miss):任何数据在未被载入缓存前都会造成缓存缺失,必须先把数据从内存/上一级缓存载入当前缓存,才能继续工作。由此带来的缺失,称为强制缺失。
- 容量缺失(Capacity Miss):由于缓存容量小于内存/上一级缓存,使得数据不能全部载入缓存,由此带来的缺失,叫做容量缺失。
- 冲突缺失(Conflict Miss):缓存中,每个cache line(缓存中数据访问的最小单位)会对应多处内存。之前的内存访问刷新了cache line,由此导致的缺失,称为冲突缺失。
- https://github.com/monnand/blogs/blob/master/hashtable-bench.markdown#-3
- http://en.wikipedia.org/wiki/CPU_cache#Cache_miss
原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: 有助于减少伪共享的@Contended注解
暂无评论