`
wbj0110
  • 浏览: 1603032 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

剖析Disruptor:为什么会这么快?(一)Ringbuffer的特别之处(转)

阅读更多

最近,我们开源了LMAX Disruptor,它是我们的交易系统吞吐量快(LMAX是一个新型的交易平台,号称能够单线程每秒处理数百万的订单)的关键原因。为什么我们要将其开源?我们意识到对高性能编程领域的一些传统观点,有点不对劲。我们找到了一种更好、更快地在线程间共享数据的方法,如果不公开于业界共享的话,那未免太自私了。同时开源也让我们觉得看起来更酷。

从这个站点,你可以下载到一篇解释什么是Disruptor及它为什么如此高性能的文档。这篇文档的编写过程,我并没有参与太多,只是简单地插入了一些标点符号和重组了一些我不懂的句子,但是非常高兴的是,我仍然从中提升了自己的写作水平。

我发现要把所有的事情一下子全部解释清楚还是有点困难的,所有我准备一部分一部分地解释它们,以适合我的NADD听众。

首先介绍ringbuffer。我对Disruptor的最初印象就是ringbuffer。但是后来我意识到尽管ringbuffer是整个模式(Disruptor)的核心,但是Disruptor对ringbuffer的访问控制策略才是真正的关键点所在。

 

ringbuffer到底是什么?

嗯,正如名字所说的一样,它是一个环(首尾相接的环),你可以把它用做在不同上下文(线程)间传递数据的buffer。

RingBuffer.png

(好吧,这是我通过画图板手画的,我试着画草图,希望我的强迫症不会让我画完美的圆和直线)

基本来说,ringbuffer拥有一个序号,这个序号指向数组中下一个可用的元素。(校对注:如下图右边的图片表示序号,这个序号指向数组的索引4的位置。)

RingBufferInitial.png

随着你不停地填充这个buffer(可能也会有相应的读取),这个序号会一直增长,直到绕过这个环。

要找到数组中当前序号指向的元素,可以通过mod操作:

               sequence mod array length = array index

以上面的ringbuffer为例(java的mod语法):12 % 10 = 2。很简单吧。

事实上,上图中的ringbuffer只有10个槽完全是个意外。如果槽的个数是2的N次方更有利于基于二进制的计算机进行计算。

(校对注:2的N次方换成二进制就是1000,100,10,1这样的数字, sequence & (array length-1) = array index,比如一共有8槽,3&(8-1)=3,HashMap就是用这个方式来定位数组元素的,这种方式比取模的速度更快。)

那又怎么样?

如果你看了维基百科里面的关于环形buffer的词条,你就会发现,我们的实现方式,与其最大的区别在于:没有尾指针。我们只维护了一个指向下一个可用位置的序号。这种实现是经过深思熟虑的—我们选择用环形buffer的最初原因就是想要提供可靠的消息传递。我们需要将已经被服务发送过的消息保存起来,这样当另外一个服务通过nak (校对注:拒绝应答信号)告诉我们没有成功收到消息时,我们能够重新发送给他们。

听起来,环形buffer非常适合这个场景。它维护了一个指向尾部的序号,当收到nak(校对注:拒绝应答信号)请求,可以重发从那一点到当前序号之间的所有消息:

RingBufferReplay.png

我们实现的ring buffer和大家常用的队列之间的区别是,我们不删除buffer中的数据,也就是说这些数据一直存放在buffer中,直到新的数据覆盖他们。这就是和维基百科版本相比,我们不需要尾指针的原因。ringbuffer本身并不控制是否需要重叠(决定是否重叠是生产者-消费者行为模式的一部分–如果你等不急我写blog来说明它们,那么可以自行检出Disruptor项目)。

它为什么如此优秀?

之所以ringbuffer采用这种数据结构,是因为它在可靠消息传递方面有很好的性能。这就够了,不过它还有一些其他的优点。

首先,因为它是数组,所以要比链表快,而且有一个容易预测的访问模式。(译者注:数组内元素的内存地址的连续性存储的)。这是对CPU缓存友好的—也就是说,在硬件级别,数组中的元素是会被预加载的,因此在ringbuffer当中,cpu无需时不时去主存加载数组中的下一个元素。(校对注:因为只要一个元素被加载到缓存行,其他相邻的几个元素也会被加载进同一个缓存行)

其次,你可以为数组预先分配内存,使得数组对象一直存在(除非程序终止)。这就意味着不需要花大量的时间用于垃圾回收。此外,不像链表那样,需要为每一个添加到其上面的对象创造节点对象—对应的,当删除节点时,需要执行相应的内存清理操作。

缺少的部分

我并没有在本文中介绍如何避免ringbuffer产生重叠,以及如何对ringbuffer进行读写操作。你可能注意到了我将ringbuffer和链表那样的数据结构进行比较,因为我并认为链表是实际问题的标准答案。

当你将Disruptor和基于 队列之类的实现进行比较时,事情将变得很有趣。队列通常注重维护队列的头尾元素,添加和删除元素等。所有的这些我都没有在ringbuffer里提到,这是因为ringbuffer不负责这些事情,我们把这些操作都移到了数据结构(ringbuffer)的外部

分享到:
评论

相关推荐

    LMAX disruptor jar包+Demo+Api+src源码 disruptor-3.0.1.jar

    - 初始化Ring Buffer:根据预期的最大并发度和事件大小计算Ring Buffer大小。 - 创建Producer:选择适合的策略,如SingleProducerSequencer或MultiProducerSequencer。 - 设置Consumer:实现EventHandler接口,...

    Disruptor并发框架中文参考文档

    ##### 2.1 Ring Buffer的特别之处 Ring Buffer是Disruptor的核心组件,它是一个固定大小的循环数组。Ring Buffer的设计使得生产者和消费者可以在不使用锁的情况下进行通信。Ring Buffer使用两个指针(头指针和尾...

    Disruptor:一种高性能的、在并发线程间数据交换领域用于替换有界限队列的方案

    Disruptor的核心是其环形缓冲区(Ring Buffer)设计,这是一个固定大小的数组,生产者和消费者通过序列号进行同步,而无需锁或其他同步机制。这种设计消除了传统队列中的锁和条件变量,极大地提高了并发性能。 总结...

    disruptor 代码分析

    本文将深入分析Disruptor的代码,特别聚焦于`setGatingSequences`方法的作用以及如何确定每个消费者可处理的最大事件序号,同时探讨`YieldingWaitStrategy`与`SingleThreadedClaimStrategy`的组合使用。 #### ...

    Java工具:高性能并发工具Disruptor简单使用

    Disruptor,由LMAX公司开源的一款并发框架,为处理高并发场景提供了一种新颖且高效的解决方案。它通过消除锁和线程间通信的开销,实现了微秒级的延迟和极高的吞吐量,尤其适用于金融交易、实时分析等对性能有苛刻...

    Disruptor报错FatalExceptionHandler的解决办法,看网上这种解决办法挺少,整理了一下

    Disruptor是一款高性能的并发框架,它通过使用Ring Buffer和基于事件的处理方式来消除锁竞争,提升系统性能。在使用Disruptor过程中,开发者可能会遇到`FatalExceptionHandler`的错误,这通常是由于处理流程中的异常...

    disruptor技术培训

    5. **启动Disruptor**:获取RingBuffer实例,并通过它来生产数据。 6. **处理数据**:数据被自动发布到RingBuffer,并由相应的消费者进行处理。 通过以上步骤,我们就可以构建一个基于Disruptor的高性能并发处理...

    disruptorqueue:基于干扰器 RingBuffer 的阻塞队列实现

    Disruptor的另一个创新之处在于其“中断器”(Disruptor)角色。中断器是一个全局的Sequence,负责协调所有生产者和消费者的处理进度。当某个消费者处理速度较慢时,中断器可以阻止其他生产者继续填充RingBuffer,...

    Disruptor应用实例

    首先,我们要理解Disruptor的核心概念——环形缓冲区(Ring Buffer)。环形缓冲区是一种高效的数据结构,其设计灵感来源于计算机硬件中的缓存设计。它采用固定大小的数组,避免了内存分配和释放带来的性能开销。当...

    Disruptor进阶.docx

    它的核心是基于Lock-free算法的Ringbuffer,相比于传统的BlockingQueue,Disruptor能提供显著的性能提升。 1. **Lock vs CAS** Disruptor摒弃了传统的锁机制,转而采用CPU级别的Compare and Swap (CAS)原语。CAS...

    Disruptor 极速体验.docx

    通过这些核心组件,Disruptor 构建了一个高效的消息传递机制,特别适合于金融交易、实时数据分析等对性能要求极高的场景。使用Disruptor,开发者能够构建出响应速度快、并发性能强的系统,同时降低了系统的复杂性和...

    disruptor-3.2.1.zip

    Disruptor-3.2.1是由LMAX公司开发并开源的一款高性能的并发框架,其核心是一个环形缓冲区(Ring Buffer)。这个框架的设计理念是消除多线程之间的锁竞争,从而大幅度提高系统的处理速度。在3.2.1版本中,Disruptor...

    无锁队列Disruptor超详细教程

    无锁队列Disruptor是LMAX公司为解决内存队列延迟问题而设计的一种高性能队列,它在Java领域被誉为最高性能的队列。与Kafka等服务间消息队列不同,Disruptor主要用于线程间的消息传递。由于其卓越的性能,基于...

    Disruptor并行框架面试题收录

    - **定义**:Disruptor使用RingBuffer作为存储事件的主要数据结构。它是一个固定大小的循环数组,用于存储由生产者产生的事件。 - **特点**: - **固定大小**:初始化时确定大小,通常为2的幂次方,以便利用位...

    share-disruptor.zip

    1. **环形缓冲区(Ring Buffer)**:Disruptor的核心是环形缓冲区,它是一个固定大小的数组,用于存储待处理的事件。这个设计避免了在内存中频繁分配和释放空间,从而减少了垃圾回收的压力。环形缓冲区的索引使用模...

    54丨算法实战(三):剖析高性能队列Disruptor背后的数据结构和算法1

    Disruptor不仅仅是一个简单的循环队列,它还引入了更高级的并发控制策略,如使用了环形缓冲区(Ring Buffer)。环形缓冲区是一种特殊的循环队列,其大小固定,通过预分配内存空间和利用位运算优化索引访问,进一步提高...

    Disruptor并发框架

    Disruptor是一个基于环形缓冲区(Ring Buffer)的并发工具,其主要目标是解决多线程间的数据共享问题,提高并发性能。与传统的锁机制相比,Disruptor采用无锁算法和事件发布/消费模式,大大减少了内存争用和上下文...

    disrupter的使用简单demo

    在这个例子中,`EventProducer`是生产者的实现,它使用Disruptor提供的`ringBuffer.next()`和`ringBuffer.publish()`方法来发布事件。`next()`方法返回一个可用于写入事件的序列号,而`publish()`方法则确认事件已经...

Global site tag (gtag.js) - Google Analytics