首先介紹ringbuffer。我對Disruptor的最初印象就是ringbuffer。可是後來我意識到儘管ringbuffer是整個模式(Disruptor)的核心,可是Disruptor對ringbuffer的訪問控制策略纔是真正的關鍵點所在。java
最近,咱們開源了LMAX Disruptor, 它是咱們的交易系統吞吐量快(LMAX是一個新型的交易平臺,號稱可以單線程每秒處理數百萬的訂單)的關鍵緣由。爲何咱們要將其開源?咱們意識到對高性 能編程領域的一些傳統觀點,有點不對勁。咱們找到了一種更好、更快地在線程間共享數據的方法,若是不公開於業界共享的話,那未免太自私了。同時開源也讓我 們以爲看起來更酷。編程
從這個站點,你能夠下載到一篇解釋什麼是Disruptor及它爲何如此高性能的文檔。這篇文檔的編寫過程,我並無參與太多,只是簡單地插入了一些標點符號和重組了一些我不懂的句子,可是很是高興的是,我仍然從中提高了本身的寫做水平。數組
我發現要把全部的事情一會兒所有解釋清楚仍是有點困難的,全部我準備一部分一部分地解釋它們,以適合個人NADD聽衆。緩存
首先介紹ringbuffer。我對Disruptor的最初印象就是ringbuffer。可是後來我意識到儘管ringbuffer是整個模式(Disruptor)的核心,可是Disruptor對ringbuffer的訪問控制策略纔是真正的關鍵點所在。數據結構
嗯,正如名字所說的同樣,它是一個環(首尾相接的環),你能夠把它用作在不一樣上下文(線程)間傳遞數據的buffer。ide
(好吧,這是我經過畫圖板手畫的,我試着畫草圖,但願個人強迫症不會讓我畫完美的圓和直線)性能
基原本說,ringbuffer擁有一個序號,這個序號指向數組中下一個可用的元素。(校對注:以下圖右邊的圖片表示序號,這個序號指向數組的索引4的位置。)google
隨着你不停地填充這個buffer(可能也會有相應的讀取),這個序號會一直增加,直到繞過這個環。spa
要找到數組中當前序號指向的元素,能夠經過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(校對注:拒絕應答信號)請求,能夠重發從那一點到當前序號之間的全部消息:
咱們實現的ring buffer和你們經常使用的隊列之間的區別是,咱們不刪除buffer中的數據,也就是說這些數據一直存放在buffer中,直到新的數據覆蓋他們。這就是 和維基百科版本相比,咱們不須要尾指針的緣由。ringbuffer自己並不控制是否須要重疊(決定是否重疊是生產者-消費者行爲模式的一部分–若是你等 不急我寫blog來講明它們,那麼能夠自行檢出Disruptor項目)。
之因此ringbuffer採用這種數據結構,是由於它在可靠消息傳遞方面有很好的性能。這就夠了,不過它還有一些其餘的優勢。
首先,由於它是數組,因此要比鏈表快,並且有一個容易預測的訪問模式。(譯者注:數組內元素的內存地址的連續性存儲的)。這是對CPU緩存友好的—也就是說,在硬件級別,數組中的元素是會被預加載的,所以在ringbuffer當中,cpu無需時不時去主存加載數組中的下一個元素。(校對注:由於只要一個元素被加載到緩存行,其餘相鄰的幾個元素也會被加載進同一個緩存行)
其次,你能夠爲數組預先分配內存,使得數組對象一直存在(除非程序終止)。這就意味着不須要花大量的時間用於垃圾回收。此外,不像鏈表那樣,須要爲每個添加到其上面的對象創造節點對象—對應的,當刪除節點時,須要執行相應的內存清理操做。
我並無在本文中介紹如何避免ringbuffer產生重疊,以及如何對ringbuffer進行讀寫操做。你可能注意到了我將ringbuffer和鏈表那樣的數據結構進行比較,由於我並認爲鏈表是實際問題的標準答案。
當你將Disruptor和基於 隊列之類的實現進行比較時,事情將變得頗有趣。隊列一般注重維護隊列的頭尾元素,添加和刪除元素等。全部的這些我都沒有在ringbuffer裏提到,這 是由於ringbuffer不負責這些事情,咱們把這些操做都移到了數據結構(ringbuffer)的外部
原文連接:http://ifeve.com/ringbuffer/
譯文連接:http://ifeve.com/dissecting-disruptor-whats-so-special/
轉載來源:http://developer.51cto.com/art/201306/399341.htm