图片 16

Java中快如闪电的线程间通讯

这几个传说源自叁个很简短的主见:创制一个对开拓人士友好的、简单轻量的线程间通信框架,完全不用锁、同步器、实信号量、等待和通知,在Java里开采叁个轻量、无锁的线程内通讯框架;何况也尚未队列、音讯、事件或别的其余并发专项使用的术语或工具。

本篇文章是世襲解读Disruptor源码的导读,切合对Disruptor还不打听的同室。假诺风乐趣,还足以看下作者事头阵的Disruptor类别小说。
要差不离弄明白Disruptor是个如何玩意儿,能够先回答那多少个难题:
Disruptor是什么?
为啥要用Disruptor?
Disruptor为啥那么快?

二十四线程已经济体改为绝大比相当多开拓者的乐趣所在了。他们努力尝试想找寻最优攻略来消弭这一个标题。过去曾经有各样尝试去标准那几个方案。特别是随着大数据,实时深入分析等问题领域的起来,又存在着新的挑衅。在这里个趋向供给走的一步是“DougLea”的文章(一部巨作),以并发框架(JS卡宴 166)的花样提必要大家。

只用平时的老式Java接口达成POJO的电视发表。

Disruptor是什么?

自己也来个一句话计算:Disruptor是LMAX开源的、用于代替并发线程间数据沟通的有界队列的、可选无锁的、高性能的线程间通信框架。

听上去是或不是感觉高大上?是否感觉很诧异怎么个高品质?是还是不是还多少云里雾里咋交流数据?那也是我最初的感觉。
日益来,我们稳步分解这一个名词。
Disruptor:首先我们看看Disruptor这么些词。熟悉星际迷航(Star
Trek)的校友大概对这一个词会更熟识,Disruptor(裂解炮)和Phaser(相位枪)都是星际迷航中的火器,Phaser是阿拉伯联合共合国酋火器,而Disruptor是克林贡的等价物。看起来Disruptor和Phaser是雷同的,那Phaser又是啥呢?在jdk1.7中,DougLea大师为大家带给了Phaser,二个可采纳的一同屏障,效用相近CyclicBarrier和CountDownLatch,可是使用更灵敏。
LMAX团队因故为Disruptor起了那么些名字,一方面是因为Disruptor和Phaser在拍卖信赖图表(恐怕说是花费链、多阶段管理、流水生产线)时有相像的地点;其他方面,Disruptor这个词本人就有不一致、破坏的意趣,很合乎Disruptor中的设计观念对于金钱观并发编制程序观念的某种“争持”–并发编制程序总是在想怎么使用十二线程去具有越来越大的吞吐量、更加少的延期,而Disruptor却由此丰富多的性子测验发掘,在出现编制程序中幸而过多的线程间通信、争抢引致了质量瓶颈,而结尾选项了单线程的拍卖逻辑去完结作业管理。
LMAX:LMAX是英国一个交易总量一点都不小的金交所,也是开源Disruptor的公司。
开源:https://github.com/LMAX-Exchange/disruptor
并发线程间数据调换的有界队列
:在现身编制程序时,平日索要在线程间交换数据,如任务分发、事件传递、状态改换等,
当时平常供给多少个线程去做客三个线程安全的数据构造,日常接纳使用队列(Queue)达成。队列在体积上又分为有界和无界,使用无界队列平常都急需保证临蓐者分娩速度低于消费者花销速度,不然将出于内部存款和储蓄器耗尽以致不幸结果。为了防止这种场所,队列常常是有界的。Java中平日应用BlockingQueue作为并发线程使用的有界队列,使用put(卡塔尔、take(卡塔尔(قطر‎在队列满、空的景况下实行等待,特别契合八线程间的数据分享,达成方式平时有ArrayBlockingQueue和LinkedBlockingQueue。
线程间通信:等同于并发线程间的数据调换。
可选无锁:使用Disruptor独一恐怕遇到Java锁的时候,正是在消费者等待可用事件实行费用时。而Disruptor为这些等待历程,编写了包含使用锁和不接受锁的有余国策,可遵照不一样情况和急需开展分选。
高性能:单坐褥者+单消费者质量测量试验:
使用LinkedBlockingQueue(https://github.com/LMAX-Exchange/disruptor/blob/master/src/perftest/java/com/lmax/disruptor/queue/OneToOneQueueThroughputTest.java

Starting Queue tests
Run 0, BlockingQueue=3,687,315 ops/sec
Run 1, BlockingQueue=2,721,088 ops/sec
Run 2, BlockingQueue=3,471,017 ops/sec
Run 3, BlockingQueue=5,347,593 ops/sec
Run 4, BlockingQueue=5,316,321 ops/sec
Run 5, BlockingQueue=5,449,591 ops/sec
Run 6, BlockingQueue=5,173,305 ops/sec

应用常用的带Translator的Disruptor测量试验(https://github.com/LMAX-Exchange/disruptor/blob/master/src/perftest/java/com/lmax/disruptor/translator/OneToOneTranslatorThroughputTest.java)

Starting Disruptor tests
Run 0, Disruptor=20,222,446 ops/sec
Run 1, Disruptor=70,671,378 ops/sec
Run 2, Disruptor=37,878,787 ops/sec
Run 3, Disruptor=37,105,751 ops/sec
Run 4, Disruptor=38,986,354 ops/sec
Run 5, Disruptor=27,578,599 ops/sec
Run 6, Disruptor=34,281,796 ops/sec

综上的名词解释,聪明如你,想必已经清楚个大致了。假如照旧不知情,料定是本人讲的不佳,请留言。

现行反革命启幕分别并发和并行性。那么些只是不一致的战术,况且市情上有广大框架提供,都能帮大家完成相仿的目标。但在增选的时候假若能同有时间了然他们此中的完结细节对大家也是大有便宜的。本文就要研究JVM线程池和线程分享的一对安居有效的选项。当然,随着多核微电脑的大规模运用,新的标题也随之而来。开垦职员也最初研究利用高档硬件的“mechanical
sympathy”(译者注:表示底层硬件的运作方式甚至与硬件运市价势一并的软件编制程序)来坚实质量。

它大概跟Akka的品种化actor好像,但作为二个必得超轻量,何况要本着单台多核微处理器实行优化的新框架,那多少个恐怕有一点过了。

怎么要用Disruptor?

那为啥要用Disruptor呢?或然说Disruptor解决了怎样难点啊?
诚如景观下,新的发明成立都以陪伴着对旧有东西的痛点发生的。在并发线程间沟通数据那个题目上,使用古板的封堵队列有啥难题啊?
凭借LMAX团队在二〇一一年通告的论文,可粗略总结为以下多少个难点:

  1. 锁的花销:
    古板梗塞队列使用锁保证线程安全。而锁通过操作系统内核的上下文切换完成,会暂停线程去等待锁直到释放。推行那样的上下文切换,会甩掉以前封存的多寡和下令。鉴于消费者和劳动者之间的快慢差别,队列总是临近满可能空的气象。这种景色会形成高水准的写入争用。
  2. 伪分享难点变成的天性低下。
  3. 队列是污物的主要性来源,队列中的成分和用来存储成分的节点目的急需打开每每的重新分配。

舆论中付出的,循环5亿次64人计数操作使用时间比较:

图片 1

施行5亿次63个人计数操作使用时间比较

Disruptor通过美好的规划,最大限度消除了上述多个难点。
惊诧如您,难道就不想知道其中奥密呢?

私家以为,当研讨线程池时,目前遍布应用的尤为重要有下述机制:

当actor高出不一样JVM实例(在相仿台机械上,或布满在互连网上的不一样机器上)的进度边界时,Akka框架很擅长管理进度间的通讯。

Disruptor为何那么快?

简易的话,Disruptor在规划上有以下几点优势:

  1. 个中数据存款和储蓄使用环形缓冲(Ring
    Buffer),在运行时举行对象内部存储器分配,这些目的无须数据本身,而只是一个数据容器。那个容器由客户提供,在Disruptor运营时,临蓐者负担获得容器设置好数据,消费者再去可用的容器中取得数码产生成本。那样分配成对象内部存储器,将有十分大恐怕让那个指标内设有主存中一而再三番三次分配,进而协助了CPU缓存地点预测。不然每一次new叁个对象,很难通晓对象内部存款和储蓄器分配到哪了。那样做好有个实惠,环形缓冲在JVM生命周期中国和日本常是永生的,GC的下压力越来越小
  2. 尽或者选取无锁设计,合理利用CAS。举多少个例证。
    a.
    通过界别是不是同意并产坐褥者,细分单坐褥者格局和多临盆者形式,单坐褥者单线程更新游标,多生产者方式选用CAS更新游标地方。
    b.
    在梗塞型等待攻略中,将拭目以俟分两有些。第一有的,消费者等待生产者发表新数据,由于不分明等待时间,使用锁的规格等待。而第二片段,消费者等待上一组消费者(Disruptor扶植链式成本,相仿Phaser分阶段处理)
    完毕此数量费用时,由于已规定有可花费数量,且一旦经常的费用时间极短,所以利用自旋(忙循环)来防止上下文切换引致的质量源消成本。能不用锁就不要锁,就算要用锁,也要将采纳锁的粒度用的渺小。
  3. 优化数据构造,扫除伪分享难点。Java中经过填充缓存行,来肃清伪分享难题的思绪,以往可能早就是不适当时候宜,连Java第88中学都新添了sun.misc.Contended证明来制止伪分享难题。但在Disruptor刚出道那会儿,能够以“机械同情(mechanical
    sympathy)”的思考情势,来优化Java数据构造,这或许还很新潮。
  4. 优化别的落成细节。举多少个例子。
    a.
    如四个RingBuffer,体量bufferSize为2的幂。使用sequence表示事件序号,可透过sequence & (bufferSize - 1)一直成分的index,比日常的求余取模(%)要快得多。事情发生早先总计好bufferSize以2为底的对数indexShift,可因此sequence >>> indexShift快速总结出sequence/bufferSize的商flag(其实一定于近来sequence在环形跑道上跑了几圈,在数量分娩时要设置好flag,在数码花费时就可以通过比对flag剖断此职务的多寡是或不是本圈的多寡),比除法要快得多。
    b.
    客观利用Unsafe,落成越发急迅地内部存款和储蓄器管理和原子访问。Unsafe封装了部分相符C/C++中的指针操作,除了JDK,在Netty、Spring、卡夫卡、Storm等极度多的风靡开源项目中都使用了Unsafe。

简短讲到那,后续起始享受解读Disruptor源码的剧情。


参照他事他说加以考查资料
1.Java8利用@sun.misc.Contended防止伪共享 –
http://www.jianshu.com/p/c3c108c3dcfd
2.写Java也得询问CPU–CPU缓存
http://www.cnblogs.com/techyc/p/3607085.html
3.linux翻看CPU高速缓存(cache卡塔尔新闻
http://www.cnblogs.com/kekukele/p/3829369.html
4.理解 CPU Cache
http://wsfdl.com/linux/2016/06/11/%E7%90%86%E8%A7%A3CPU%E7%9A%84cache.html
5.有关CPU Cache — 技士供给精晓的那一个事
http://cenalulu.github.io/linux/all-about-cpu-cache/
6.Mechanical Sympathy – Memory Barriers/Fences
https://mechanical-sympathy.blogspot.tw/2011/07/memory-barriersfences.html
7.《Java高并发程序设计》- 4.4.3 Java中的指针:Unsafe类

  1. Executor框架提供的线程。
  2. LMAX的Ring Buffer概念 (译者注:Ring
    Buffer即环形缓冲,LMAX是一种新型零售金融交易平台,其框架能以低顺延发生大批量交易,LMAX塑造在JVM平台上。
  3. 基于Actor(事件)的实现。

但对此那种只供给线程间通信的迷你项目来讲,用Akka类型化actor大概部分像用牛刀杀鸡,但是类型化actor仍为一种卓越的贯彻情势。

并发框架下的线程池选项:

本身花了几天时间,用动态代理,梗塞队列和缓存线程池创设了三个设计方案。

首先,小编个人不赞同使用即时风行的线程池概念,而相应运用职业行列的概念。简单来说,在一个试行框架可供接收的各样分裂取舍都是依赖某种顺序数据布局,如数组或队列(拥塞或非拥塞)之类的,例如ConcurrentLinkedQueue(并发链式队列),ArrayBlockingQueue(数组窒碍队列),
LinkedBlockingQueue(链式窒碍队列)等等。文书档案申明,即使它们的使用情况各不相仿,但他们带有的原形/数据布局有相近的性质,如顺序插入和遍历。

图一是以此框架的高等级次序布局:

图片 2

图片 3
图一: 框架的高等级次序结构

优势:

SPSC队列是指单一临蓐者/单一购买者队列。MPSC队列是指多分娩者/单一消费者队列。

  1. 压缩线程创建招致的推迟
  2. 透过优化线程数量,能够搞定能源贫乏的主题材料。

派发线程担任接受Actor线程发送的新闻,并把它们派发到对应的SPSC队列中去。

那几个足以使应用程序和服务器应用响应更加快。使用线程池看似多个很准确的施工方案只是却有多少个根天性的老毛病:延续争用难点。这里是Java中关于部分现身框架下线程池选项的座谈。

吸收到新闻的Actor线程用个中的多寡调用相应的actor实例中的方法。借助任何actor的代理,actor实例能够将消息发送到MPSC队列中,然后音信会被发送给指标actor线程。

Disruptor(环形缓冲):

笔者创制了一个简约的例子来测量试验,正是下面那么些打乒球的程序:

(LMAX的七个基于环形缓冲区的高质量进度间音信库)LMAX的开辟人员使用贰个忧愁框架来撤销一而再争用难题,这些框架是依赖三个叫环形缓冲的数据构造。它可能是线程间发送音讯的最平价办法了。它是队列的一种代替完毕格局,但它又和SEDA和Actors(译者注:那三种都以和Disruptor相通的面世模型)有一对同步特征。向Disruptor中放入音信必要两步,第一步申请两个环形缓冲的槽位,槽位可为客户提供写对应数据的笔录。然后须求提交该条记录,为了能灵活利用内部存款和储蓄器,2步法是必需的。独有由此提交,那条音信手艺对消费者线程可以见到。下图描述了环状缓冲这么些数据构造(Disruptor的中坚):

public interface PlayerA (
  void pong(long ball); //发完就忘的方法调用 
}
public interface PlayerB {   
  void ping(PlayerA playerA, long ball); //发完就忘的方法调用 
}    
public class PlayerAImpl implements PlayerA {    
  @Override    
  public void pong(long ball) {    
  }    
}
public class PlayerBImpl implements PlayerB {   
  @Override    
  public void ping(PlayerA playerA, long ball) {    
    playerA.pong(ball);    
  }    
}
public class PingPongExample {   
  public void testPingPong() {
    // 管理器隐藏了线程间通讯的复杂性
    // 控制actor代理,actor实现和线程  
    ActorManager manager = new ActorManager();
    // 在管理器内注册actor实现 
    manager.registerImpl(PlayerAImpl.class);    
    manager.registerImpl(PlayerBImpl.class);
    //创建actor代理。代理会将方法调用转换成内部消息。 
    //会在线程间发给特定的actor实例。    
    PlayerA playerA = manager.createActor(PlayerA.class);    
    PlayerB playerB = manager.createActor(PlayerB.class);    
    for(int i = 0; i < 1000000; i++) {    
       playerB.ping(playerA, i);     
   }    
}

(译者注:LMAX的主导是多少个业务逻辑微型机,而该事务逻辑微电脑的宗旨是Disruptor,这是三个产出组件,能够在无锁的景色下跌成网络的Queue并发操作)

透过测验,速度大致在每秒500,000
次乒/乓左右;还不易啊。可是跟单线程的运转速度比起来,笔者倏然就以为没那么好了。在 单线程中运作的代码每秒速度能落得20亿
(2,681,850,373State of Qatar!

图片 4

以致差了5,000
多倍。太让笔者深负众望了。在超多处境下,单线程代码的效劳都比三十多线程代码越来越高速。

Disruptor在多核平台上能实现异常的低的延期同偶尔间又有高吞吐量,固然线程程间供给分享数据甚至传递音信。

自己起来找原因,想看看笔者的乒球选手们干什么那样慢。经过一番调研和测量检验,笔者开掘是阻塞队列的标题,小编用来在actor间传递音信的连串影响了品质。

它的特殊之处在于锁和免争用布局。它依旧不采用CAS或内部存款和储蓄器尊崇。若想了然关于它的越来越多细节,这里有一篇不错的文章和官网。使用Disruptor的二个弱点(事实上也算不得瑕玷)是,你要求提前告诉Disruptor应用程序实现职务所急需的线程数。

图片 5

听他们说事件:

图 2: 唯有一个劳动者和二个买主的SPSC队列

对此守旧线程池机制,一个有力的代表方案正是遵照事件模型。这种基于事件的线程轮询/线程池/线程调治机制在函数式编制程序中很宽泛。关于那些概念的二个杰出流行的落实是基于actor的系统(译者注:Scala的产出系统),Akka已化作其实际的正规。(译者注:Akka,一种专长管理进度间通讯的框架)

据此小编倡导了一场比赛,要将它换到Java里最快的行列。小编发掘了Nitsan
Wakart的 博客 。他发了几篇文章介绍单一生产者/单一消费者(SPSC卡塔尔无锁队列的落实。这么些小说受到了Martin汤普森的解说 终精品质的无锁算法的启发。

Actors是那二个轻量级的产出实体。它们接收一种事件驱动的吸取循环来异步管理音讯。音信形式相配能够很有益于地发挥叁个actor的行为。它们拉长了望梅止渴等级进而使写,测验,驾驭和掩护并发/布满式系统特别轻巧。令你注意于专门的学业流——新闻怎样流入系统——而不是低等级次序的基本概念如线程,锁以致套接字IO。二个线程能够分配七个或单个actor,并且二种模型都以依需求选拔的。

跟基于私有锁的队列相比较,无锁队列的属性更优。在根据锁的种类中,当三个线程得到锁时,其余线程将要等着锁被放出。而在无锁的算法中,有些临蓐者线程临盆音信时不会卡住其余生产者线程,消费者也不会被别的读取队列的顾客拥塞。

像Akka这种基于actor的系统的优势犹如下所列:

在Martin汤普森的阐述以至在Nitsan的博客中介绍的SPSC队列的性情俨然令人无法相信—— 超过了100M
ops/sec。比JDK的并发队列完成还要快10倍
(在4核的 AMD Core i7 上的天性大约在 8M ops/sec 左右卡塔尔。

  • 可封装
  • 可监督
  • 可配置执行
  • 职责透明
  • 重试机制

自家怀着超级大的期待,将具备actor上连接的链式堵塞队列都换到了无锁的SPSC队列。缺憾,在吞吐量上的性质测量检验并不曾像自身料想的那样现身小幅度进级。不过超快作者就认识到,瓶颈并不在SPSC队列上,而是在八个生产者/单一消费者(MPSC卡塔尔国这里。

注:调试叁个基于actor的类别是三个丰富不方便的事体。

用SPSC队列做MPSC队列的职分并不那么简单;在做put操作时,七个临盆者也许会覆盖掉彼此的值。SPSC
队列就从不决定七个分娩者put操作的代码。所以正是换到最快的SPSC队列,也解决不了小编的标题。

Disruptor使用叁个线程四个客户方式,不相同于Actors使用N个线程M个买主形式。比方,你能够享有自由多的actors,然后它们会被疏散到一些数目固定的线程中(平常是一个核一个线程),至于其余的某些,actor模型就和Disruptor模型差不离了;特别是用来批管理的时候。

为了管理多少个生产者/单一消费者的景观,笔者决定启用LMAX
Disruptor ——二个依照环形缓冲区的高质量进度间消息库。

本人最先在因特网络搜多到的答案也表明开源空间中关于鲜明JVM选项基准的孝敬依然有一点点的。个中三个筛选是ExecutorBenchmarkt。它是叁个并行职分的开源测量试验框架。它是用Scala编写的,可是足以用来Java和Scala负载。

图片 6

粗略,迅明锐飞的软硬件行当在表现新挑衅给大家的同一时候也提供了大量解决应用程序容错性和响应性的不二等秘书技。对于不可预感的微量线程,小编建议利用JDK并发框架中的线程池机制。对于大气范围平日的职分,个人建议接受Disruptor。Disruptor实在是有几许学学曲线,但在性质和扩充性方面包车型地铁获得远远值得投入的读书时光。在应用程序要求某种重试或管理机制,以至布满式任务时,提议采取Akka的Actor模型。就算结果还应该有恐怕被别的因素所影响,你依旧会筛选map
reduce或fork/join模型或是别的自定义达成二个布满式应用程序。

图3: 单一临蓐者和单一消费者的LMAX Disruptor

依赖于Disruptor,超级轻松实现低顺延、高吞吐量的线程间消息电视发表。它还为临蓐者和买主的比不上组合提供了不相同的用例。多少个线程能够互不拥塞地读取环形缓冲中的音讯:

图片 7

图 4: 单一坐蓐者和七个买主的LMAX Disruptor    

下边是有多少个生产者写入环形缓冲区,多个买主从当中读取音信的景况。

图片 8

图 5: 多少个生产者和三个买主的LMAX Disruptor

经过对品质测量检验的全速搜索,作者找到了 多个公布者和二个主顾的吞吐量测量检验。
这么些真是正合作者意,它交给了上面这一个结果:

LinkedBlockingQueue Disruptor
Run 0 4,550,625 ops/sec 11,487,650 ops/sec
Run 1 4,651,162 ops/sec 11,049,723 ops/sec
Run 2 4,404,316 ops/sec 11,142,061 ops/sec

在3 个临蓐者/1个 购买者场景下,
Disruptor要比LinkedBlockingQueue快两倍多。可是那跟自个儿所期望的习性上升高10倍仍然有极大差别。

这让自家感觉很消极,並且小编的大脑一直在搜寻建设方案。就疑似真命天子相近,小编近来不在跟人拼车里下班,而是改乘地铁了。顿然灵光一闪,小编的大脑起始将车站跟生产者消费者对应起来。在一个车站里,既有分娩者(车和下车的人),也可以有消费者(同一辆车和上车的人)。

本身创立了
Railway类,并用AtomicLong追踪从一站到下一站的高铁。笔者先从简单的风貌开端,独有一辆车的铁轨。

public class RailWay {  
 private final Train train = new Train();  
 // stationNo追踪列车并定义哪个车站接收到了列车
 private final AtomicInteger stationIndex = new AtomicInteger();
// 会有多个线程访问这个方法,并等待特定车站上的列车 
public Train waitTrainOnStation(final int stationNo) {

   while (stationIndex.get() % stationCount != stationNo) {
    Thread.yield(); // 为保证高吞吐量的消息传递,这个是必须的。
                   //但在等待列车时它会消耗CPU周期 
   }  
   // 只有站号等于stationIndex.get() % stationCount时,这个忙循环才会返回

   return train;
 }
// 这个方法通过增加列车的站点索引将这辆列车移到下一站
  public void sendTrain() {
    stationIndex.getAndIncrement();
   }
  }

为了测量检验,作者用的原则跟在Disruptor质量测量试验中用的大同小异,况兼也是测的SPSC队列——测量试验在线程间传递long值。小编创立了上面这几个Train类,个中蕴藏了贰个long数组:

public class Train {   
  //   
  public static int CAPACITY = 2*1024;
  private final long[] goodsArray; // 传输运输货物的数组

  private int index;

  public Train() {   
      goodsArray = new long[CAPACITY];     
 }

 public int goodsCount() { //返回货物数量    
  return index;    
 }    
 public void addGoods(long i) { // 向列车中添加条目    
  goodsArray[index++] = i;    
 }    
 public long getGoods(int i) { //从列车中移走条目    
  index--;    
  return goodsArray[i];    
 }    
}

然后本人写了三个简单的测量试验 :八个线程通过列车相互传送long值。

图片 9

图 6: 使用单辆列车的单一生产者和单一消费者Railway

public void testRailWay() {   
  final Railway railway = new Railway();    
  final long n = 20000000000l;    
  //启动一个消费者进程 
  new Thread() {    
   long lastValue = 0;
   @Override   
   public void run() {    
    while (lastValue < n) {    
      Train train = railway.waitTrainOnStation(1); //在#1站等列车
      int count = train.goodsCount();    
      for (int i = 0; i < count; i++) {    
        lastValue = train.getGoods(i); // 卸货   
      }    
      railway.sendTrain(); //将当前列车送到第一站 
     }    
   }    
 }.start();

final long start = System.nanoTime();
long i = 0;   
while (i < n) {    
 Train train = railway.waitTrainOnStation(0); // 在#0站等列车    
 int capacity = train.getCapacity();    
 for (int j = 0; j < capacity; j++) {    
   train.addGoods((int)i++); // 将货物装到列车上 
 }    
 railway.sendTrain();
 if (i % 100000000 == 0) { //每隔100M个条目测量一次性能 
    final long duration = System.nanoTime() - start;    
    final long ops = (i * 1000L * 1000L * 1000L) / duration;    
    System.out.format("ops/sec = %,dn", ops);    
    System.out.format("trains/sec = %,dn", ops / Train.CAPACITY);    
    System.out.format("latency nanos = %.3f%nn", 
    duration / (float)(i) * (float)Train.CAPACITY);    
  }    
 }    
}

在区别的列车体积下运维那几个测量试验,结果惊着自己了:

容量 吞吐量: ops/sec 延迟: ns
1 5,190,883 192.6
2 10,282,820 194.5
32 104,878,614 305.1
256 344,614,640 742. 9
2048 608,112,493 3,367.8
32768 767,028,751 42,720.7

在高铁容积达到32,768时,多个线程传送新闻的吞吐量达到了767,028,751
ops/sec。比Nitsan博客中的SPSC队列快了好多倍。

继续按铁路高铁那几个思路思量,作者想知道倘若有两辆动车会怎么着?作者以为应该能增高吞吐量,同一时候还是能下落延迟。各样车站都会有它自个儿的列车。当一辆高铁在率先个车站装货时,第二辆列车会在其次个车站卸货,反之亦然。

图片 10

图 7: 使用两辆火车的单纯分娩者和单纯消费者Railway

上边是吞吐量的结果:

容量 吞吐量: ops/sec 延时: ns
1 7,492,684 133.5
2 14,754,786 135.5
32 174,227,656 183.7
256 613,555,475 417.2
2048 940,144,900 2,178.4
32768 797,806,764 41,072.6

结果是摄人心魄的;比单辆列车的结果快了1.4倍多。列车体积为有的时候,延迟从192.6纳秒降到133.5皮秒;那明摆着是七个激动人心的迹象。

就此小编的实验还未得了。列车容积为2048的多个线程传递音信的延期为2,178.4
飞秒,那太高了。作者在想什么减少它,创立三个有好多辆火车 的例证:

图片 11

图 8: 使用多辆动车的单一临盆者和单纯消费者Railway 

自己还把列车容积降低到了1个long值,开头玩起了列车数量。上边是测量试验结果:

列车数量 吞吐量: ops/sec 延迟: ns
2 10,917,951 91.6
32 31,233,310 32.0
256 42,791,962 23.4
1024 53,220,057 18.8
32768 71,812,166 13.9

用32,768 列车在线程间发送贰个long值的延期降至了13.9
微秒。通过调解列车数量和高铁体量,当延时不那么高,吞吐量不那么低时,吞吐量和延时就完成了极品平衡。

对于单一临蓐者和单一消费者(SPSC卡塔尔国来说,那个数值很棒;但大家怎么让它在有多少个临盆者和客户时也能见到效果呢?答案很简短,增多越多的车站!

图片 12

图 9:二个劳动者和三个客户的Railway

每一个线程都等着下一趟高铁,装货/卸货,然后把列车送到下一站。在劳动者往列车的里面装货时,消费者在从高铁上卸货。列车生生不息地从三个车站转到另一个车站。

为了测验单一临蓐者/多顾客(SPMCState of Qatar的状态,作者创设了二个有8个车站的Railway测验。
三个车站归属贰个劳动者,而除此以外7个车站归于消费者。结果是:

火车数量 = 256 ,列车体量 = 32:

 ops/sec = 116,604,397     延迟(纳秒) = 274.4

火车数量= 32,列车体量= 256:

 ops/sec = 432,055,469     延迟(纳秒) = 592.5

如您所见,就算有8个办事线程,测验给出的结果也一定好–
32辆体积为259个long的列车吞吐量为432,055,469
ops/sec。在测量试验时期,全体CPU内核的负载都是百分之百。

图片 13

图 10:在测量试验有8个车站的Railway 时期的CPU 使用意况

在玩这些Railway算法时,作者差十分少忘了自己开始时期的指标:提升多临蓐者/单消费者意况下的个性。

图片 14

图 11:七个坐褥者和叁个买主的 Railway 

本身创制了3个坐蓐者和1个客商的新测量检验。每辆列车一站一站地转圈,而各类分娩者只给每辆车装四分之一容积的货。消费者收取每辆车的里面四个生产者给出的方方面面三项物品。品质测验给出的平分结果如下所示:

 ops/sec = 162,597,109  列车/秒 = 54,199,036     延迟(纳秒) = 18.5

结果相当的屌。分娩者和买主专门的学业的进程当先了160M ops/sec。

为了增加补充足收入的差额异,下边给出相似境况下的Disruptor结果- 3个分娩者和1个客商:

Run 0, Disruptor=11,467,889 ops/sec
Run 1, Disruptor=11,280,315 ops/sec
Run 2, Disruptor=11,286,681 ops/sec
Run 3, Disruptor=11,254,924 ops/sec

上边是另三个批量消息的Disruptor 3P:1C 测验 (10 条音讯每批State of Qatar:

Run 0, Disruptor=116,009,280 ops/sec
Run 1, Disruptor=128,205,128 ops/sec
Run 2, Disruptor=101,317,122 ops/sec
Run 3, Disruptor=98,716,683 ops/sec;

终极是用带LinkedBlockingQueue 达成的Disruptor 在3P:1C风貌下的测量检验结果:

Run 0, BlockingQueue=4,546,281 ops/sec
Run 1, BlockingQueue=4,508,769 ops/sec
Run 2, BlockingQueue=4,101,386 ops/sec
Run 3, BlockingQueue=4,124,561 ops/sec

如你所见,Railway情势的平均吞吐量是162,597,109
ops/sec,而Disruptor在相通的事态下的最佳结果独有128,205,128
ops/sec。至于 LinkedBlockingQueue,最佳的结果唯有4,546,281 ops/sec。

Railway算法为事件批处理提供了一种能够鲜明增添吞吐量的简短方法。通过调解列车体积或列车数量,比较轻易落成想要的吞吐量/延迟。

除此以外,
当同一个线程能够用来开支新闻,管理它们并向环中重临结果时,通过混合临蓐者和买主,Railway也能用来管理百废待举的情状:

图片 15

图 12: 混合坐褥者和消费者的Railway

末段,我会提供二个因而优化的相当高吞吐量 单分娩者/单消费者测验:

图片 16

图 13:单个临盆者和单个消费者的Railway

它的平均结果为:吞吐量超过每秒15亿 (1,569,884,271卡塔尔国次操作,延迟为1.3
飞秒。如你所见,本文开首描述的要命规模相似的单线程测量试验的结果是每秒2,681,850,373。

您本人研商结论是如何啊。

自己盼望现在再写一篇作品,声明怎么着用Queue和
BlockingQueue接口辅助Railway算法,用来拍卖区别的劳动者和客户组合。敬请关切。

发表评论

电子邮件地址不会被公开。 必填项已用*标注