Disruptor筆記(一)-預備知識

Memory Barrier 內存障

.它是一個CPU指令。是的,再一次,我們在思考CPU級的東西以便得到我們需要的性能(Martin著名的MechanicalSympathy)。基本上它是一個指令,爲了a)確保特定運算的執行順序和b)影響一些數據(可能是一些指令的執行結果)的可見性。


.編譯器和CPU能對指令重新排序,來嘗試優化性能,最終執行結果是一樣的。插入一個內存障會告訴CPU和編譯器在那個命令之前執行的需要呆在那個命令之前,在那個命令之後執行的需要呆在那之後。就像一次去拉斯維加斯的旅遊全在你腦子裏一樣。

.內存障做的另一件事是強制各種CPU緩存的更新-比如,一個寫障會把在這個障之前寫到緩存的數據全刷新,於是其他任何線程去讀那個數據都會拿到最新的版本,不管它是由哪個內核或socket執行的。


.在java中,這裏神奇的咒語是單詞"volatile"(一個我覺得在Java認證中從沒明確地解釋過的東西)。如果你的字段是volatile的,Java內存模型會在你對它寫入之後插入一個寫障指令,並且在你對它讀取之前插入一個讀障指令。

這意味着如果你對一個volatile字段寫入,你知道的:

  .任何在你對這個字段寫入之後訪問它的線程都會得到更新後的值。

  .任何你在對這個字段寫入之前做的事都被確保發生過了,而任何更新過的數據值都會變得可見,因爲內存障把所有早先對緩存的寫入都刷新了。


cache line padding 高速緩存行 補齊

內存緩存系統中基本單元是高速緩存行(Cache lines). cpu會把數據從內存加載到高速緩存中 ,這樣可以獲得更好的性能,高速緩存默認大小是64 Byte爲一個區域,一個區域在一個時間點只允許一個核心操作,也就是說不能有多個核心同時操作一個緩存區域。


因爲高速緩存是64字節,而Hotspot JVM的對象頭是兩個部分組成,第一部分是由24字節的hash code和8字節的鎖等狀態標識組成,第二部分是指向該對象類的引用。基本類型字節如下:
doubles (8) and longs (8)
ints (4) and floats (4)
shorts (2) and chars (2)
booleans (1) and bytes (1)
references (4/8)


False Share   僞內存

例子:

設想你的long數據不是數組的一部分。設想它只是單獨的一個變量。讓我們稱它爲“頭”,沒什麼理由。然後再設想在你的類中有另一個變量緊挨着它。讓我們直接稱它爲“尾”。現在,當你加載"頭"到高速緩存的時候,你免費加載了“尾”。




直到你意識到“尾”正在被你的生產者寫入,而“頭”正在被你的消費者寫入。這兩個變量實際上並不是密切相關的,而且事實上是要被兩個可能在兩個不同的內核中運行的不同線程使用的。


設想你的消費者更新了“頭”。緩存中的值被更新了,內存中的值被更新了,而其他任何緩存塊中存在的“頭”都失效了因爲其他緩存不會有嶄新的值。記住我們是在整個塊級處理的,我們沒法只把“頭”標記爲無效。



現在如果一些進程正在另一個內核上運行,只是想讀“尾”的值,整個緩存塊需要被重新從主內存讀取。那麼一個和你的消費者無關的線程讀一個和“頭”無關的值,它被高速緩存未命中給拖慢了。

當然如果兩個獨立的線程同時對那兩個值寫入會更糟。每次當另一個線程對高速緩存塊做了寫操作的時候,每個內核都要把另一個內核上的高速緩存塊失效掉並重新讀取裏面的數據。你基本上是遇到兩個線程之間的寫衝突了儘管它們寫入的是不同的變量。

這叫作“僞共享”,因爲每次你訪問“頭”你都會得到“尾”,而且每次你訪問“尾”,你同樣會得到“頭”。這一切都在後臺發生,沒有任何編譯警告會告訴你你正在寫一個併發訪問效率很低的代碼。


參考:

http://www.cnblogs.com/adaikiss/tag/disruptor/

發佈了73 篇原創文章 · 獲贊 60 · 訪問量 32萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章