Disruptor的伪共享解决方案

1.术语


术语


英文单词


描述


内存屏障


Memory Barriers


是一组处理器指令,用于实现对内存操作的顺序限制。

In the Java Memory Model a volatile field has a store barrier inserted after a write to it and a load barrier inserted before a read of it.


缓存行


Cache line


缓存中可以分配的最小存储单位。处理器填写缓存线时会加载整个缓存线,需要使用多个主内存读周期。


原子操作


Atomic operations


不可中断的一个或一系列操作。


缓存行填充


cache line fill


当处理器识别到从内存中读取操作数是可缓存的,处理器读取整个缓存行到适当的缓存(L1,L2,L3的或所有)


缓存命中


cache hit


如果进行高速缓存行填充操作的内存位置仍然是下次处理器访问的地址时,处理器从缓存中读取操作数,而不是从内存。


写命中


write hit


当处理器将操作数写回到一个内存缓存的区域时,它首先会检查这个缓存的内存地址是否在缓存行中,如果不存在一个有效的缓存行,则处理器将这个操作数写回到缓存,而不是写回到内存,这个操作被称为写命中。


写缺失


write misses the cache


一个有效的缓存行被写入到不存在的内存区域。

一、 什么是伪共享

在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己缓存的

值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器要对这个数据进行修改

操作的时候,会强制重新从系统内存里把数据读到处理器缓存里。

  缓存行的大小是64字节。这意味着,小于64字节的变量,是有可能存在于同一条缓存行的。例如变量X大小32字节,变量Y大小32字节,

那么他们有可能会存在于一缓存行上。

当core 1上的线程想更新X,而core 2上的线程想更新Y,而X变量和Y变量在同一个缓存行中时;每个线程都要去竞争缓存行的所有权来更新变量。

如果core 1获得所缓存行的所有权,那么缓存子系统将会使core 2中对应的缓存行失效,反之亦然。使x和y所在的缓存行来来回回的经过L3缓存,大大影响了

性能。这种情况,就像多个线程同时获取锁的所有权一样。如果互相竞争的core位于不同的插槽,就要额外横跨插槽连接,问题可能更加严重。

伪共享,就是多个线程同时修改共享在同一个缓存行里的独立变量,无意中影响了性能

二、缓存行填充

为什么追加64字节能够提高并发编程的效率呢

  因为对于英特尔酷睿i7,酷睿, Atom和NetBurst, Core Solo和Pentium M处理器的L1,L2或L3缓存的高速缓存行是64个字节宽,

不支持部分填充缓存行,这意味着如果队列的头节点和尾节点都不足64字节的话,可以发生伪共享,而队列的入队和出队操作是需要不停修

改头接点和尾节点,所以在多处理器的情况下将会严重影响到队列的入队和出队效率。

    /** head of the queue */
    private transient final PaddedAtomicReference < QNode > head;

    /** tail of the queue */
    private transient final PaddedAtomicReference < QNode > tail;

    static final class PaddedAtomicReference < T > extends AtomicReference < T > {
        // enough padding for 64bytes with 4byte refs 
        Object p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, pa, pb, pc, pd, pe;
        PaddedAtomicReference(T r) {
            super(r);
        }
    }

    public class AtomicReference < V > implements java.io.Serializable {

        private volatile V value;
    }

一个对象的引用占4个字节,它追加了15个变量共占60个字节,再加上父类的Value变量,一共64个字节。

三、理解Disruptor的RingBuffer

四、Log4j 2 使用Disruptor提高性能

五、参考

1.Disruptor

2.海阔凭鱼跃从缓存行出发理解volatile变量、伪共享False sharing、disruptor

3.美团:高性能队列——Disruptor

时间: 2024-12-28 12:23:43

Disruptor的伪共享解决方案的相关文章

从缓存行出发理解volatile变量、伪共享False sharing、disruptor

volatilekeyword 当变量被某个线程A改动值之后.其他线程比方B若读取此变量的话,立马能够看到原来线程A改动后的值 注:普通变量与volatile变量的差别是volatile的特殊规则保证了新值能马上同步到主内存,以及每次使用前能够马上从内存刷新,即一个线程改动了某个变量的值,其他线程读取的话肯定能看到新的值. 普通变量: 写命中:当处理器将操作数写回到一个内存缓存的区域时.它首先会检查这个缓存的内存地址是否在缓存行中,假设不存在一个有效的缓存行,则处理器将这个操作数写回到缓存,而不

伪共享 FalseSharing (CacheLine,MESI) 浅析以及Java里的解决方案

起因 在阅读百度的发号器 uid-generator 源码的过程中,发现了一段很奇怪的代码: /** * Represents a padded {@link AtomicLong} to prevent the FalseSharing problem<p> * * The CPU cache line commonly be 64 bytes, here is a sample of cache line after padding:<br> * 64 bytes = 8 by

伪共享(false sharing),并发编程无声的性能杀手

在并发编程过程中,我们大部分的焦点都放在如何控制共享变量的访问控制上(代码层面),但是很少人会关注系统硬件及 JVM 底层相关的影响因素.前段时间学习了一个牛X的高性能异步处理框架 Disruptor,它被誉为“最快的消息框架”,其 LMAX 架构能够在一个线程里每秒处理 6百万 订单!在讲到 Disruptor 为什么这么快时,接触到了一个概念——伪共享( false sharing ),其中提到:缓存行上的写竞争是运行在 SMP 系统中并行线程实现可伸缩性最重要的限制因素.由于从代码中很难看

java 伪共享

MESI协议及RFO请求典型的CPU微架构有3级缓存, 每个核都有自己私有的L1, L2缓存. 那么多线程编程时, 另外一个核的线程想要访问当前核内L1, L2 缓存行的数据, 该怎么办呢?有人说可以通过第2个核直接访问第1个核的缓存行. 这是可行的, 但这种方法不够快. 跨核访问需要通过Memory Controller(见上一篇的示意图), 典型的情况是第2个核经常访问第1个核的这条数据, 那么每次都有跨核的消耗. 更糟的情况是, 有可能第2个核与第1个核不在一个插槽内.况且Memory C

多线程中的volatile和伪共享

  伪共享 false sharing,顾名思义,“伪共享”就是“其实不是共享”.那什么是“共享”?多CPU同时访问同一块内存区域就是“共享”,就会产生冲突,需要控制协议来协调访问.会引起“共享”的最小内存区域大小就是一个cache line.因此,当两个以上CPU都要访问同一个cache line大小的内存区域时,就会引起冲突,这种情况就叫“共享”.但是,这种情况里面又包含了“其实不是共享”的“伪共享”情况.比如,两个处理器各要访问一个word,这两个word却存在于同一个cache line

伪共享和缓存行填充,从Java 6, Java 7 到Java 8

关于伪共享的文章已经很多了,对于多线程编程来说,特别是多线程处理列表和数组的时候,要非常注意伪共享的问题.否则不仅无法发挥多线程的优势,还可能比单线程性能还差.随着JAVA版本的更新,再各个版本上减少伪共享的做法都有区别,一不小心代码可能就失效了,要注意进行测试.这篇文章总结一下. 什么是伪共享 关于伪共享讲解最清楚的是这篇文章<剖析Disruptor:为什么会这么快?(三)伪共享>,我这里就直接摘抄其对伪共享的解释: 缓存系统中是以缓存行(cache line)为单位存储的.缓存行是2的整数

并发不得不说的伪共享

前言 可谓是一入并发深似海,看得越多,发现自己懂的越少,总感觉自己只是了解了其冰山一角.但是在研究的过程中越来越感受到一些框架的设计之美,很细腻的赶脚.同时也让我get到了新的知识点. CPU缓存 在正式进入正题之前,必须得先说说缓存这个概念.对于缓存这个概念相信大多数程序猿都不会很陌生,在大大小小项目中都会遇到.举个最简单的例子:数据一般都会存放到数据库之中.但在某些应用场景中不可能每次加载数据都去从数据库中加载(毕竟io操作是非常耗时和耗性能的),而是会用redis之类的缓存中间件去过渡,在

共享和伪共享

共享就是一个内存区域的数据被多个处理器访问,伪共享就是不是真的共享.这里的共享这个概念是基于逻辑层面的.实际上伪共享与共享在cache line 上实际都是共享的. CPU访问的数据都是从cache line 中读取的.如果cpu 在cache 中找不到需要的变量,则称缓存未命中. 未命中时,需要通过总线从内存中读取进cache 中.每次读取的内存大小就是一个cache line 的大小. 如果多个CPU访问的不同内存变量被装载到了同一个cache line 中,则从程序逻辑层上讲,并没有共享变

NO4 系统移植-项目技术资料共享解决方案

系统移植项目-技术资料共享解决方案 前言 本文只做解决方案分析,不做具体技术点的说明 一:为什么需要技术资料的共享 系统移植项目的移植点可以分:(1)操作系统移植,例如:由window移植到linux上,由Aix移植到window上等,(2)程序语言的移植,例如vb6升级到vb.net,由PHP移植到Java上等,(3)后台数据库移植,例如:DB2移植到sql server,由sql server移植到Oracle上等. 一般的项目都是包含多个移植点,很少能有一个移植点的项目. 在项目过程中,操