1. 程式人生 > >有助於減少偽共享的@Contended註解

有助於減少偽共享的@Contended註解

原文連結 作者:Dave 譯者:卓二妹 校對:丁一

詳細描述看Aleksey Shipilev這封郵件 —— 我們期待@Contended已久。JVM會自動為物件欄位進行記憶體佈局。通常JVM會這樣做:(a)將物件的域按從大到小的順序排列,以優化佔用的空間;(b)打包引用型別的欄位,以便垃圾收集器在追蹤的時候能夠處理相連的引用型別的欄位。@Contended讓程式能夠更明確地控制併發和偽共享。通過該功能我們能夠把那些頻繁進行寫操作的共享欄位,從其它幾乎是只讀或只有少許寫操作的欄位中分離開來。原則很簡單:對共享內容進行讀操作的代價小,對共享內容的寫操作代價非常高。我們也可以將那些可能會被同一個執行緒幾乎同時寫的欄位打包到一起。

一般而言,我們試圖改變相關欄位的位置,以使得coherency misses的發生次數降到最低。在簡單的單執行緒環境中,那些在時間上緊挨在一起被訪問的欄位,空間上應該放置在相鄰的位置以提升快取區域性性[1]。也就是,時間區域性性應該決定著空間區域性性。時間上在一起訪問的欄位應該放到空間上相鄰的位置。我們說過,當多執行緒併發訪問我們的欄位的時候,我們必須小心翼翼地以避免偽共享和因coherence traffic導致過多的快取失效。同樣地,對於那些原本是分開的欄位,但可能會被同一個執行緒同時寫入相同的快取行,我們會試圖將這些欄位聚集到一起。注意:如果我們想極度地降低單執行緒下的容量缺失[2]

,那麼在併發環境中可能出現大量的coherency misses。在原生C/C++程式碼中,程式設計師都在使用能感知併發的(concurrency-aware)結構佈局。@Contended也應在JAVA中提供同樣的功能,雖然在原生代碼中,繫結欄位到偏移量發生在編譯階段,但在JAVA中發生在裝載階段。值得指出的是,在一般情況下,找不到一個同時適用於單執行緒和多執行緒環境的最優佈局。理想佈局問題本身就是一個NP難題。

理想情況下,JAVA虛擬機器應該使用硬體的監控設施來檢測出共享行為,並在程式執行中改變佈局。這是有一定困難的,因為我們還沒有好的辦法為JVM提供有效的資訊。提示:我們需要取消作業系統和管理程式的中間層。另外一個挑戰是,原始欄位的偏移量有在unsafe

工具中使用,因此我們需要解決這個問題,可能是使用一個額外的中間層。

最後,眾所周知final欄位是隻讀的,因此,我還希望能夠將它們打包到一起。

校注

[1]:早在1968年,Denning.P就曾提出區域性性原理:程式在執行時將呈現出區域性性規律,即在一較短的時間內,程式的執行僅侷限於某個部分;相應地,它所訪問的儲存空間也侷限於某個區域。

[2]:根據快取缺失的原因,可以分為以下幾個型別:

  • 強制缺失(Compulsory Miss):任何資料在未被載入快取前都會造成快取缺失,必須先把資料從記憶體/上一級快取載入當前快取,才能繼續工作。由此帶來的缺失,稱為強制缺失。
  • 容量缺失(Capacity Miss):由於快取容量小於記憶體/上一級快取,使得資料不能全部載入快取,由此帶來的缺失,叫做容量缺失。
  • 衝突缺失(Conflict Miss):快取中,每個cache line(快取中資料訪問的最小單位)會對應多處記憶體。之前的記憶體訪問重新整理了cache line,由此導致的缺失,稱為衝突缺失。