麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

并發(fā)框架Disruptor淺析

2019-11-14 21:15:41
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友
并發(fā)框架Disruptor淺析

1、引言

  Disruptor是一個(gè)開(kāi)源的java框架,它被設(shè)計(jì)用于在生產(chǎn)者—消費(fèi)者(PRoducer-consumer problem,簡(jiǎn)稱PCP)問(wèn)題上獲得盡量高的吞吐量(TPS)和盡量低的延遲。Disruptor是LMAX在線交易平臺(tái)的關(guān)鍵組成部分,LMAX平臺(tái)使用該框架對(duì)訂單處理速度能達(dá)到600萬(wàn)TPS,除金融領(lǐng)域之外,其他一般的應(yīng)用中都可以用到Disruptor,它可以帶來(lái)顯著的性能提升。其實(shí)Disruptor與其說(shuō)是一個(gè)框架,不如說(shuō)是一種設(shè)計(jì)思路,這個(gè)設(shè)計(jì)思路對(duì)于存在“并發(fā)、緩沖區(qū)、生產(chǎn)者—消費(fèi)者模型、事務(wù)處理”這些元素的程序來(lái)說(shuō),Disruptor提出了一種大幅提升性能(TPS)的方案。

  現(xiàn)在有很多人寫過(guò)關(guān)于Disruptor文章,但是我還是想寫這篇淺析,畢竟不同人的理解是不同的,希望沒(méi)接觸過(guò)它的人能通過(guò)本文對(duì)Disruptor有個(gè)初步的了解,本文后面給出了一些相關(guān)鏈接供參考。

2、什么是Disruptor?為什么速度更快?

  簡(jiǎn)單的說(shuō),Disruptor是一個(gè)高性能的Buffer,并提供了使用這個(gè)Buffer的框架。為什么說(shuō)是它性能更好呢?這得從PCP和傳統(tǒng)解決辦法的缺點(diǎn)開(kāi)始說(shuō)起。

  我們知道,PCP又稱Bounded-Buffer問(wèn)題,其核心就是保證對(duì)一個(gè)Buffer的存取操作在多線程環(huán)境下不會(huì)出錯(cuò)。使用Java中的ArrayBlockingQueue和LinkedBlockingQueue類能輕松的完成PCP模型,這對(duì)于一般程序已經(jīng)沒(méi)問(wèn)題了,但是對(duì)于并發(fā)度高、TPS要求較大的系統(tǒng)則不然。

  *BlockingQueue使用的是packagejava.util.concurrent.locks中實(shí)現(xiàn)的鎖,當(dāng)多個(gè)線程(例如生產(chǎn)者)同時(shí)寫入Queue時(shí),鎖的爭(zhēng)搶會(huì)導(dǎo)致只有一個(gè)生產(chǎn)者可以執(zhí)行,其他線程都中斷了,也就是線程的狀態(tài)從RUNNING切換到BLOCKED,直到某個(gè)生產(chǎn)者線程使用完Buffer后釋放鎖,其他線程狀態(tài)才從BLOCKED切換到RUNNABLE,然后時(shí)間片到其他線程后再進(jìn)行鎖的爭(zhēng)搶。上述過(guò)程中,一般來(lái)說(shuō)生產(chǎn)者存放一個(gè)數(shù)據(jù)到Buffer中所需時(shí)間是非常短的,操作系統(tǒng)切換線程上下文的速度也是非??斓?,但是當(dāng)線程數(shù)量增多后,OS切換線程所帶來(lái)的開(kāi)銷逐漸增多,鎖的反復(fù)申請(qǐng)和釋放成為性能瓶頸。*BlockingQueue除了使用鎖帶來(lái)的性能損失外,還可能因?yàn)榫€程爭(zhēng)搶的順序問(wèn)題造成性能再次損失:實(shí)際使用中發(fā)現(xiàn)線程的調(diào)度順序并不理想,可能出現(xiàn)短時(shí)間內(nèi)OS頻繁調(diào)度出生產(chǎn)者或消費(fèi)者的情況,這樣造成緩沖區(qū)可能短時(shí)間內(nèi)被填滿或被清空的極端情況。(理想情況應(yīng)該是緩沖區(qū)長(zhǎng)度適中,生產(chǎn)和消費(fèi)速度基本一致)

  對(duì)于上面的問(wèn)題Disruptor的解決方案是:不用鎖。

Ring Buffer示意圖

  Disruptor使用一個(gè)Ring Buffer存放生產(chǎn)者的“產(chǎn)品”,環(huán)形緩沖區(qū)實(shí)際上還是一段連續(xù)內(nèi)存,之所以稱作環(huán)形是因?yàn)樗鼘?duì)數(shù)據(jù)存放位置的處理,生產(chǎn)者和消費(fèi)者各有一個(gè)指針(數(shù)組下標(biāo)),消費(fèi)者的指針指向下一個(gè)要讀取的Slot,生產(chǎn)者指針指向下一個(gè)要放入的Slot,消費(fèi)或生產(chǎn)后,各自的指針值p = (p +1) % n,n是緩沖區(qū)長(zhǎng)度,這樣指針在緩沖區(qū)上反復(fù)游走,故可以將緩沖區(qū)看成環(huán)狀。(如右圖)(Ring Buffer并非Disruptor原創(chuàng),linux內(nèi)核中就有環(huán)形緩沖區(qū)的實(shí)現(xiàn)。)使用Ring Buffer時(shí):

當(dāng)生產(chǎn)者和消費(fèi)者都只有一個(gè)時(shí),由于兩個(gè)線程分別操作不同的指針,所以不需要鎖。

當(dāng)有多個(gè)消費(fèi)者時(shí),(按Disruptor的設(shè)計(jì))每個(gè)消費(fèi)者各自控制自己的指針,依次讀取每個(gè)Slot(也就是每個(gè)消費(fèi)者都會(huì)讀取到所有的產(chǎn)品),這時(shí)只需要保證生產(chǎn)者指針不會(huì)超過(guò)最慢的消費(fèi)者(超過(guò)最后一個(gè)消費(fèi)者“一圈”)即可,也不需要鎖。

當(dāng)有多個(gè)生產(chǎn)者時(shí),多個(gè)線程共用一個(gè)寫指針,此處需要考慮多線程問(wèn)題,例如兩個(gè)生產(chǎn)者線程同時(shí)寫數(shù)據(jù),當(dāng)前寫指針=0,運(yùn)行后其中一個(gè)線程應(yīng)獲得緩沖區(qū)0號(hào)Slot,另一個(gè)應(yīng)該獲得1號(hào),寫指針=2。對(duì)于這種情況,Disruptor使用CAS來(lái)保證多線程安全。

  CAS(Compare and Swap/Set)是現(xiàn)在CPU普遍支持的一種指令(例如cmpxchg系類指令),CAS操作包含3個(gè)操作數(shù):CAS(A,B,C),其功能是:取地址A的值與B比較,如果相同,則將C賦值到地址A。CAS特點(diǎn)是它是由硬件實(shí)現(xiàn)的極輕量級(jí)指令,同時(shí)CPU也保證此操作的原子性。在考慮線程間同步問(wèn)題時(shí),可以使用Unsafe類的boolean compareAndSwapInt(java.lang.Object arg0, long arg1, int arg2, int arg3);系列方法,對(duì)于一個(gè)int變量(例如,Ring Buffer的寫指針),使用CAS可以避免多線程訪問(wèn)帶來(lái)的混亂,當(dāng)compareAndSwap方法true時(shí)表明CAS操作成功賦值,返回false則表明地址A處的值并不等于B,此時(shí)重新試一遍即可,使用CAS移動(dòng)寫指針的邏輯如下:  

 1 //寫指針向后移動(dòng)n 2 public long next(int n) 3 { 4     //...... 5     long current,next; 6     do 7     { 8         //此處先將寫指針的當(dāng)前值備份一下 9         current = pointer.get();10         //預(yù)計(jì)寫指針將要移動(dòng)到的位置11         next = current + n;12         //......省略:確保從current到current+n的Slot已經(jīng)被消費(fèi)者讀完......13         //*原子操作*如果當(dāng)前寫指針和剛才一樣(說(shuō)明9-12行的計(jì)算有效),那么移動(dòng)寫指針14         if ( pointer.comapreAndSet(current,next) )15             break;  16     }while ( true )//如果CAS失敗或者還不能移動(dòng)寫指針,則不斷嘗試17     return next;18 }

  OK,我們現(xiàn)在有了一個(gè)使用CAS的Ring Buffer,這比用鎖快上不少,但CAS的效率并沒(méi)有想象的那么快,根據(jù)鏈接[2]pdf中評(píng)測(cè):和單一線程無(wú)鎖執(zhí)行某簡(jiǎn)單任務(wù)相比,使用鎖的時(shí)間比無(wú)鎖高出2個(gè)數(shù)量級(jí),CAS也高出了一個(gè)數(shù)量級(jí)。那么Disruptor還有什么提高性能的地方呢?下面列舉一下除了無(wú)鎖編程外的其他性能優(yōu)化點(diǎn)。

  緩存行填充(Cache Line Padding):CPU緩存常以64bytes作為一個(gè)緩存行大小,緩存由若干個(gè)緩存行組成,緩存寫回主存或主存寫入緩存均是以行為單位,此外每個(gè)CPU核心都有自己的緩存(但是若某個(gè)核心對(duì)某緩存行做出修改,其他擁有同樣緩存的核心需要進(jìn)行同步),生產(chǎn)者和消費(fèi)者的指針用long型表示,假設(shè)現(xiàn)在只有一個(gè)生產(chǎn)者和一個(gè)消費(fèi)者,那么雙方的指針間沒(méi)有什么直接聯(lián)系,只要不“挨著”,應(yīng)該可以各改各的指針。OK前面說(shuō)有點(diǎn)亂,下面問(wèn)題來(lái)了:如果生產(chǎn)者和消費(fèi)者的指針(加起來(lái)共16bytes)出現(xiàn)在同一個(gè)緩存行中會(huì)怎么樣?例如CPU核心A運(yùn)行的消費(fèi)者修改了一下自己的指針值(P1),那么其他核心中所有緩存了P1的緩存行都將失效,并從主存重新調(diào)配。這樣做的缺點(diǎn)顯而易見(jiàn),但是CPU和編譯器并未聰明到避免這個(gè)問(wèn)題,所以需要緩存行填充。雖然問(wèn)題產(chǎn)生的原因很繞,但是解決方案卻非常簡(jiǎn)單:對(duì)于一個(gè)long型的緩沖區(qū)指針,用一個(gè)長(zhǎng)度為8的long型數(shù)組代替。如此一來(lái),一個(gè)緩存行被這個(gè)數(shù)組填充滿,線程對(duì)各自指針的修改不會(huì)干擾到他人。

  避免GC:寫Java程序的時(shí)候,很多人習(xí)慣隨手new各種對(duì)象,雖然Java的GC會(huì)負(fù)責(zé)回收,但是系統(tǒng)在高壓力情況下頻繁的new必定導(dǎo)致更頻繁的GC,Disruptor避免這個(gè)問(wèn)題的策略是:提前分配。在創(chuàng)建RingBuffer實(shí)例時(shí),參數(shù)中要求給出緩沖區(qū)元素類型的Factory,創(chuàng)建實(shí)例時(shí),Ring Buffer會(huì)首先將整個(gè)緩沖區(qū)填滿為Factory所產(chǎn)生的實(shí)例,后面生產(chǎn)者生產(chǎn)時(shí),不再用傳統(tǒng)做法(順手new一個(gè)實(shí)例出來(lái)然后add到buffer中),而是獲得之前已經(jīng)new好的實(shí)例,然后設(shè)置其中的值。舉個(gè)形象的例子就是,若緩沖區(qū)是個(gè)放很多紙片的地方,紙片上記錄著信息,以前的做法是:每次加入緩沖區(qū)時(shí),都從系統(tǒng)那現(xiàn)準(zhǔn)備一張紙片,然后再寫好紙片放進(jìn)緩沖區(qū),消費(fèi)完就隨手扔掉?,F(xiàn)在的做法是:實(shí)現(xiàn)準(zhǔn)備好所有的紙片,想放入時(shí)只需要擦掉原來(lái)的信息寫上新的即可。

  成批操作(Batch):Ring Buffer的核心操作是生產(chǎn)和消費(fèi),如果能減少這兩個(gè)操作的次數(shù),性能必然相應(yīng)地提高。Disruptor中使用成批操作來(lái)減少生產(chǎn)和消費(fèi)的次數(shù),下面具體說(shuō)一下Disruptor的生產(chǎn)和消費(fèi)過(guò)程中如何體現(xiàn)Batch的。向RingBuffer生產(chǎn)東西的時(shí)候,需要經(jīng)過(guò)2個(gè)階段:階段一為申請(qǐng)空間,申請(qǐng)后生產(chǎn)者獲得了一個(gè)指針?lè)秶鶾low,high],然后再對(duì)緩沖區(qū)中[low,high]這段的所有對(duì)象進(jìn)行setValue(見(jiàn)優(yōu)化點(diǎn)②),階段2為發(fā)布(像這樣ringBuffer.publish(low,high);)。階段1結(jié)束后,其他生產(chǎn)者再申請(qǐng)的話,會(huì)得到另一段緩沖區(qū)。階段2結(jié)束后,之前申請(qǐng)的這一段數(shù)據(jù)就可以被消費(fèi)者讀到。Disruptor推薦成批生產(chǎn)、成批發(fā)布,減少生產(chǎn)時(shí)的同步帶來(lái)的性能損失。從RingBuffer消費(fèi)東西的時(shí)候也需要兩個(gè)階段,階段一為等待生產(chǎn)者的(寫)指針值超過(guò)指定值(N,即N之前的數(shù)據(jù)已經(jīng)生產(chǎn)過(guò)了),階段一執(zhí)行完后,消費(fèi)者會(huì)得到一個(gè)指針值(R),表示Ring Buffer中下標(biāo)R之前的值是可以讀的。階段2就是具體讀?。裕?。階段一返回值R很有可能大于N,此時(shí)消費(fèi)者應(yīng)該進(jìn)行成批讀取操作,將[R,N]范圍內(nèi)的數(shù)據(jù)全部處理。

  LMAX架構(gòu):(注:指的是LMAX公司在做他們的交易平臺(tái)時(shí)使用的一些設(shè)計(jì)思想的集合,嚴(yán)格講是LMAX架構(gòu)包含Disruptor,并非其中的一部分,但是Disruptor的設(shè)計(jì)中或多或少體現(xiàn)了這些思想,所以在這還是要提一下,關(guān)于LMAX架構(gòu)應(yīng)該可以寫很多,但限于個(gè)人水平,在這只能簡(jiǎn)單說(shuō)說(shuō)。另外,這個(gè)架構(gòu)是以及極端追求性能的產(chǎn)物,不一定適合大眾。)如下圖所示LMAX架構(gòu)分為三個(gè)部分,輸入/輸出Disruptor,和中間核心的業(yè)務(wù)邏輯處理器。所有的信息輸入進(jìn)入InputDisruptor,被業(yè)務(wù)邏輯處理器讀取后送入OutputDisruptor,最后輸出到其他地方。

LMAX架構(gòu)

  對(duì)于一般由表現(xiàn)層+業(yè)務(wù)層+持久層組成的Web系統(tǒng),LMAX架構(gòu)指的是業(yè)務(wù)層,它有如下幾個(gè)特點(diǎn):

    a)業(yè)務(wù)邏輯處理器(簡(jiǎn)稱BLP)完全的In-Memory:如上圖,業(yè)務(wù)邏輯處理器是處理所有業(yè)務(wù)邏輯的地方,Input Disruptor把輸入(例如訂單數(shù)據(jù)、用戶操作)以消息的形式(稱作Message或者Event都可以)發(fā)到BLP,BLP進(jìn)行響應(yīng)。一般系統(tǒng)中我們可能會(huì)多線程執(zhí)行一些業(yè)務(wù)邏輯代碼,然后這些代碼最終生成一些SQL語(yǔ)句,然后這些語(yǔ)句再去查數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)可能在其他主機(jī),數(shù)據(jù)庫(kù)查詢結(jié)果可能直接用了內(nèi)存中的緩存,最壞情況是數(shù)據(jù)庫(kù)從磁盤中讀取了想要的數(shù)據(jù),最后再返回給業(yè)務(wù)邏輯代碼。這個(gè)過(guò)程有很多的時(shí)間浪費(fèi):首先,多線程訪問(wèn)持久層會(huì)涉及到同步問(wèn)題(鎖,有是這貨)。其次,生成*QL語(yǔ)句、查詢數(shù)據(jù)庫(kù)的耗時(shí)也是非常大的。最后,最壞情況下還要進(jìn)行一大串磁盤IO和內(nèi)存IO才能取到數(shù)據(jù)。LMAX對(duì)此的解決方案是:把能用到的所有數(shù)據(jù)全部裝入內(nèi)存,只有到極少數(shù)或周期性需要同步、持久化的時(shí)候再訪問(wèn)數(shù)據(jù)庫(kù)。(這聽(tīng)起來(lái)有點(diǎn)瘋狂,但是仔細(xì)想想這些業(yè)務(wù)真的需要那么大空間嗎?)這么做的好處也是顯而易見(jiàn),減少了網(wǎng)絡(luò)、磁盤的IO后In-Memory系統(tǒng)上的大部分業(yè)務(wù)邏輯全都變成一些加減乘除運(yùn)算了。

    b)異步-事件驅(qū)動(dòng):經(jīng)過(guò)a)的修改,如果還存在一些業(yè)務(wù)邏輯處理過(guò)程是需要長(zhǎng)時(shí)間才能完成的,那么就把它作為一個(gè)事件,再拋給其他組件(可能還是Disruptor)等待。業(yè)務(wù)邏輯處理器需要時(shí)刻保持最快速度、最高效率,它不能等待任何事情。

    c)每個(gè)業(yè)務(wù)邏輯處理器是單線程的:你沒(méi)有聽(tīng)錯(cuò)。其實(shí)有了a)b)作為前提,會(huì)發(fā)現(xiàn)多線程所帶來(lái)的業(yè)務(wù)層面同步問(wèn)題將會(huì)極大限制BLP效率、增大BLP的復(fù)雜度,和BLP的設(shè)計(jì)(Keep it simple, stupid.)相悖,如果實(shí)在想多線程,可以參照d)。

    d)使用多級(jí)業(yè)務(wù)邏輯處理器:有些像管道模式,上圖的3塊結(jié)構(gòu)可以以多種方式組合,一個(gè)BLP可以將輸出送往多個(gè)Output Disruptor,而這些Disruptor可能是另一些3塊結(jié)構(gòu)的InputDisruptor,即有些BLP是起到分發(fā)作用的,另一些是進(jìn)行具體業(yè)務(wù)邏輯計(jì)算的。每個(gè)BLP對(duì)應(yīng)一個(gè)線程,整個(gè)架構(gòu)可能比上圖復(fù)雜很多。

3、Hello Disruptor

  Disruptor最初是由Java實(shí)現(xiàn)的,現(xiàn)在也有C/Cpp和.Net版本,Java版最全更新最快,代碼注釋較多比較好懂。說(shuō)了這么多,本節(jié)先給出一個(gè)測(cè)試?yán)?,展示Disruptor的基本用法,例子中用LinkedBlockingQueue和Disruptor分別實(shí)現(xiàn)了單一生產(chǎn)者+單一消費(fèi)者存取簡(jiǎn)單對(duì)象的測(cè)試,統(tǒng)計(jì)了一下雙方消耗的時(shí)間,僅供參考。

  例子中使用Disruptor 3.2.1。不同版本間的Disruptor一些術(shù)語(yǔ)可能有變化,在該版本中,緩沖區(qū)里的元素被稱作Event,指針(緩沖區(qū)的下標(biāo))被稱作Sequence,生產(chǎn)者的指針為RingBuffer.sequencer(private成員),消費(fèi)者的指針通過(guò)ringBufferInstance.newBarrier()得到。
//簡(jiǎn)單對(duì)象:緩沖區(qū)中的元素,里面只有一個(gè)value,提供setValueprivate class TestObj {        public long value;        public TestObj(long value)    {        this.value = value;    }        public void setValue(long value)    {        this.value = value;    }    }public class Test {    //待生產(chǎn)的對(duì)象個(gè)數(shù)    final long objCount = 1000000;    final long bufSize;//緩沖區(qū)大小    {        bufSize = getRingBufferSize(objCount);    }        //獲取RingBuffer的緩沖區(qū)大小(2的冪次!加速計(jì)算)    static long getRingBufferSize(long num)    {        long s = 2;        while ( s < num )        {            s <<= 1;        }        return s;    }        //使用LinkedBlockingQueue測(cè)試    public void testBlocingQueue() throws Exception    {        final LinkedBlockingQueue<TestObj> queue = new LinkedBlockingQueue<TestObj>();        Thread producer = new Thread(new Runnable() {//生產(chǎn)者            @Override            public void run() {                try{                    for ( long i=1;i<=objCount;i++ )                    {                        queue.put(new TestObj(i));//生產(chǎn)                    }                }catch ( InterruptedException e ){                }            }        });        Thread consumer = new Thread(new Runnable() {//消費(fèi)者            @Override            public void run() {                try{                    TestObj readObj = null;                    for ( long i=1;i<=objCount;i++ )                    {                        readObj = queue.take();//消費(fèi)                        //DoSomethingAbout(readObj);                    }                }catch ( InterruptedException e ){                }            }        });                long timeStart = System.currentTimeMillis();//統(tǒng)計(jì)時(shí)間        producer.start();        consumer.start();        consumer.join();        producer.join();        long timeEnd = System.currentTimeMillis();        DecimalFormat df = (DecimalFormat) DecimalFormat.getInstance();        System.out.println((timeEnd - timeStart) + "/" + df.format(objCount) +                " = " + df.format(objCount/(timeEnd - timeStart)*1000) );    }        //使用RingBuffer測(cè)試    public void testRingBuffer() throws Exception    {        //創(chuàng)建一個(gè)單生產(chǎn)者的RingBuffer,EventFactory是填充緩沖區(qū)的對(duì)象工廠        //            YieldingWaitStrategy等"等待策略"指出消費(fèi)者等待數(shù)據(jù)變得可用前的策略        final RingBuffer<TestObj> ringBuffer = RingBuffer.createSingleProducer(new EventFactory<TestObj>() {            @Override            public TestObj newInstance() {                return new TestObj(0);            }        } , (int)bufSize, new YieldingWaitStrategy());        //創(chuàng)建消費(fèi)者指針        final SequenceBarrier barrier = ringBuffer.newBarrier();                Thread producer = new Thread(new Runnable() {//生產(chǎn)者            @Override            public void run() {                for ( long i=1;i<=objCount;i++ )                {                    long index = ringBuffer.next();//申請(qǐng)下一個(gè)緩沖區(qū)Slot                    ringBuffer.get(index).setValue(i);//對(duì)申請(qǐng)到的Slot賦值                    ringBuffer.publish(index);//發(fā)布,然后消費(fèi)者可以讀到                }            }        });        Thread consumer = new Thread(new Runnable() {//消費(fèi)者            @Override            public void run() {                TestObj readObj = null;                int readCount = 0;                long readIndex = Sequencer.INITIAL_CURSOR_VALUE;                while ( readCount < objCount )//讀取objCount個(gè)元素后結(jié)束                {                    try{                        long nextIndex = readIndex + 1;//當(dāng)前讀取到的指針+1,即下一個(gè)該讀的位置                        long availableIndex = barrier.waitFor(nextIndex);//等待直到上面的位置可讀取                        while ( nextIndex <= availableIndex )//從下一個(gè)可讀位置到目前能讀到的位置(Batch!)                        {                            readObj = ringBuffer.get(nextIndex);//獲得Buffer中的對(duì)象                            //DoSomethingAbout(readObj);                            readCount++;                            nextIndex ++;                        }                        readIndex = availableIndex;//刷新當(dāng)前讀取到的位置                    }catch ( Exception ex)                    {                        ex.printStackTrace();                    }                }            }        });                long timeStart = System.currentTimeMillis();//統(tǒng)計(jì)時(shí)間        producer.start();        consumer.start();        consumer.join();        producer.join();        long timeEnd = System.currentTimeMillis();        DecimalFormat df = (DecimalFormat) DecimalFormat.getInstance();        System.out.println((timeEnd - timeStart) + "/" + df.format(objCount) +                " = " + df.format(objCount/(timeEnd - timeStart)*1000) );            }        public static void main(String[] args) throws Exception {        Test ins = new Test();        //執(zhí)行測(cè)試        ins.testBlocingQueue();        ins.testRingBuffer();    }}
測(cè)試代碼

測(cè)試結(jié)果:

319/1,000,000 = 3,134,000 //使用LinkedBlockingQueue在319毫秒內(nèi)存取100萬(wàn)個(gè)簡(jiǎn)單對(duì)象,每秒鐘能執(zhí)行313萬(wàn)個(gè)

46/1,000,000 = 21,739,000 //使用Disruptor在46毫秒內(nèi)存取100萬(wàn)個(gè)簡(jiǎn)單對(duì)象,每秒鐘能執(zhí)行2173萬(wàn)個(gè)

平均下來(lái)使用Disruptor速度能提高7倍。(不同電腦、應(yīng)用環(huán)境下結(jié)果可能不一致)

4、隨想:Disruptor、完成端口與Mechanical Sympathy

When pushing performance like this, it starts to become important to take account of the way modern hardware is constructed.

                      —The LMAX Architecture

  “當(dāng)對(duì)性能的追求達(dá)到這樣的程度,以致對(duì)現(xiàn)代硬件構(gòu)成的理解變得越來(lái)越重要。”這句話恰當(dāng)?shù)匦稳萘薉isruptor/LMAX在對(duì)性能方面的追求和失敗。咦,失???為什么會(huì)這么說(shuō)呢?Disruptor當(dāng)然是一個(gè)優(yōu)秀的框架,我說(shuō)的失敗指的是在開(kāi)發(fā)它的過(guò)程中,LMAX曽試圖提高并發(fā)程序效率,優(yōu)化、使用鎖或借助其他模型,但是這些嘗試最終失敗了——然后他們構(gòu)建了Disruptor。再提問(wèn):一個(gè)Java程序員在嘗試提高他的程序性能的時(shí)候,需要了解很多硬件知識(shí)嗎?我想很多人都會(huì)回答“不需要”,構(gòu)建Disruptor的過(guò)程中,最初開(kāi)發(fā)人員對(duì)這個(gè)問(wèn)題的回答可能也是“不需要”,但是嘗試失敗后他們決定另辟蹊徑??偟目聪翫isruptor的設(shè)計(jì):鎖到CAS、緩沖行填充、避免GC等,我感覺(jué)這些設(shè)計(jì)都在刻意“遷就”或者“依賴”硬件設(shè)計(jì),這些設(shè)計(jì)更像是一種“(ugly)hack”(毫無(wú)疑問(wèn),Disruptor還是目前最優(yōu)秀的方案之一)。

  Disruptor我想到了完成端口,完成端口據(jù)說(shuō)能是Windows上最快的并發(fā)網(wǎng)絡(luò)“框架”:你只要通過(guò)API告訴Windows你想recv哪些socket,然后各個(gè)recv操作在內(nèi)核層面上執(zhí)行并加入到某個(gè)隊(duì)列中,最后再使用Worker線程進(jìn)行處理,大部分工作Windows都為你做好了,不使用鎖也沒(méi)有上下文切換和大量線程,是不是和Disruptor異曲同工呢?完成端口和Disruptor在追求性能時(shí),都避免使用并行、鎖、多線程等概念,這些概念的出現(xiàn)自有它們的原因,這里不用多說(shuō),但是為了性能(考慮到硬件)卻不能充分使用它們,說(shuō)明在處理并發(fā)、并行問(wèn)題上,硬件和軟件的發(fā)展存在不協(xié)調(diào),可能馮氏計(jì)算機(jī)還是適合單“線程”順序處理信息吧。關(guān)于這種不協(xié)調(diào),我認(rèn)為應(yīng)該是硬件應(yīng)該會(huì)逐步適應(yīng)軟件,但也有人提出了有意思的Mechanical Sympathy(鏈接[6]),至于未來(lái)會(huì)如何發(fā)展就不是這篇blog能討論的了:) 。

(完)

鏈接:

[1]Disruptor介紹譯文:http://ifeve.com/disruptor/

原文https://code.google.com/p/disruptor/wiki/BlogsAndArticles

[2]Disruptor的GitHub:http://lmax-exchange.github.io/disruptor/

其中http://lmax-exchange.github.io/disruptor/files/Disruptor-1.0.pdf這篇PDF對(duì)Disruptor作了很好的闡述。

[3]完成端口:http://blog.csdn.net/piggyxp/article/details/6922277

[4]致敬disruptor:CAS實(shí)現(xiàn)高效(偽)無(wú)鎖阻塞隊(duì)列實(shí)踐:http://www.majin163.com/2014/03/24/cas_queue/

[5]Disruptor 源碼分析:http://huangyunbin.VEvb.com/blog/1944232

[6]Mechanical Sympathy:http://mechanical-sympathy.blogspot.com/

----------------------------------(我是分割線)----------------------------------

PS1: 轉(zhuǎn)載請(qǐng)注明作者。

PS2: 下載:Disruptor介紹PPT

PS3:這是我第5個(gè)博客(不過(guò)前4個(gè)都不是技術(shù)blog,笑),以后我會(huì)盡量貼一些遇到的問(wèn)題和思考到這里,水平有限,歡迎各位指出不足!


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 情侣啪啪网站 | 草碰人人 | 久久久久久久久久久久网站 | 污片在线观看视频 | 亚洲第一成av人网站懂色 | 国产资源在线播放 | 国产精品久久久久久久久久久天堂 | 国产视频第一区 | 国内精品久久久久久2021浪潮 | 日韩三级伦理在线观看 | 毛片免费观看完整版 | 深夜影院一级毛片 | 精国品产一区二区三区有限公司 | 在线观看美女av | 92看片淫黄大片一级 | 国产精品自在线拍 | 国产午夜精品久久久久久免费视 | videos高潮| 国产污污视频 | 黄色片视频观看 | 在线播放视频一区二区 | 国产九色在线播放九色 | 精品国产一区二区三区在线观看 | 久草成人在线观看 | 91九色国产视频 | 日韩视频一区二区在线观看 | 亚洲第一页在线观看 | 欧美成人精品一区 | 久色亚洲 | 一边吃奶一边插下面 | 黄色特级片黄色特级片 | 性 毛片 | 国产九色在线播放九色 | 久久成人激情视频 | 久久久久久免费 | 密室逃脱第一季免费观看完整在线 | 久久精片 | 妇子乱av一区二区三区 | 久久一本日日摸夜夜添 | 717影院理论午夜伦八戒秦先生 | 久久草在线观看视频 |