2014-02-27 87 views
3

在作爲JSR166的一部分引入的類中,作者使用所謂的填充來填充Striped64.Cell類的單個值字段。爲什麼jsr166e.Striped64.Cell類中值域的額外填充值?

這裏是一個類的摘錄:

/** 
* Padded variant of AtomicLong supporting only raw accesses plus CAS. The value field is placed 
* between pads, hoping that the JVM doesn't reorder them. 
* <p/> 
* JVM intrinsics note: It would be possible to use a release-only form of CAS here, if it were 
* provided. 
*/ 
static final class Cell { 
    volatile long p0, p1, p2, p3, p4, p5, p6; 
    volatile long value; 
    volatile long q0, q1, q2, q3, q4, q5, q6; 

    ... 

筆者然後使用CAS原子地修改值。

在Striped64類中,作者還使用不安全來訪問兩個其他字段,但不應用任何此類填充。

我的問題是:爲什麼需要做這樣的事情,引入14個冗餘字段填充單個值字段?

回答

4

填充是爲了防止共享value字段的高速緩存行 - 否則,可能必須從內存中重新獲取該值,因爲高速緩存行上的其他內容需要使整行無效。所以目標是提高性能。爲了使事情變得簡單,Java 8引入了@Contended annotation,它在底層執行相同的操作,不同之處在於它由JVM本身處理。

+0

感謝您的回答。我認爲這是正確的,但爲什麼額外的q0 ... q6作爲p0..p6 +值的總體大小已經是64字節,這在我的理解中完全適合單個緩存行?是否容納實現更長緩存行的其他體系結構? – silkentrance

+2

@silkentrance它將使用2個緩存行。如果你只有p0,p1,p6的值,那麼你最終可能會在一個高速緩存行上使用p0-p6,其他數據和另一行上的值以及其他數據。 – assylias

4

儘管我同意assylias的回答,但我認爲這需要一些解釋。

爲什麼緩存缺失重要?

因爲從主內存中讀取比緩存中的要慢得多。如果你有一個需要經常使用的變量,讓它緩存很重要。此外,如果此變量與其他變量共享相同的緩存,則可能會導致整個緩存行失效。

考慮變量1與變量2位於同一個緩存時的例子。變量1由thread1使用,變量2由thread2使用。因爲他們生活在同一個緩存行中,所以如果有更新的變量2,並且thread1需要使用變量1,則需要刪除緩存行(即使它不使用此變量,也需要刪除)並從主內存中讀取。這被稱爲虛假分享

爲什麼實際上有7個多長?

如果只有JVM沒有決定重新對內存進行重新排序,那麼從哪裏開始讀取這個變量並不重要(可以從第3行讀取它,在8個「緩存行「) - 你仍然會在緩存行中得到一個單一的值。所以,無論你開始閱讀,只有一個對你很重要的值將在緩存行中,因此不可能存在「錯誤共享」的「緩存未命中」。

P.S.這就是爲什麼Java對象的大小可以被8整除。