Java8 性能提升: LongAdder vs AtomicLong
即將到來的Java8給在Java虛擬機上最廣泛使用的語言帶來了大量的新特性。或許最多提到的是Lambdas表達式,Scala和JRuby的愛好者們對此發出了終于來了的感嘆。但是對于多線程應用更為重要的類是新增的LongAdder和DoubleAdder,在多線程下,原子實現比 AtomicInteger和AtomicLong提供了更優越的性能。
一些簡單的指標闡述了AtomicLong和LongAdder之間的性能不同點——測試下面這些指標,我們使用了可以訪問Intel Xeon E5-2670所有8個核的m3.2xlarge EC2實例。
在單線程下,新的LongAdder慢了1/3,但是當多個線程在爭著增加字段,LongAdder則顯示出了自己的價值。注意到,每個線程所做的唯一事情就是試圖增加計數——這是一個最極端形式的綜合標準。這里的競爭比你在實際應用程序中可能看到的要高的多,但是有時候你確實需要這種共享計數器,而 LongAdder帶來了很大的幫助。
你可以在我們的 java-8-benchmarks 倉庫中找到這些基準測試程序數值的代碼。它使用了JMH來完成實際工作,并以Marshall的gradle-jmh-demo為基礎。JMH能夠為程序員完成對精度要求很高的工作,并有效降低編程的難度,保證了結果數據能夠反應目前在基于JVM性能測試精度的較高水準。JMH并非一定在perf下運行,所以我們還做了一些單例測試。
Perf-stat下更多的細節
通過單例測試,我們可以在perf-sata下對測試程序有更多的控制力度,以及獲取程序運行中更多的細節。最基本的指標是每一個標準運行占用的時間。這些基準都在運行在英特爾I7-2600k內核上(實際硬件,不是虛擬的)。
在單線程的情況下,AtomicLong稍微快一點,在2個線程的時候,AtomicLong比LongAdder慢近4倍,而在線程數和機器CPU核數一致的情況下,AtomicLong比LongAdder慢近5倍。
讓人印象更深刻的是,直到線程數超過CPU的物理核數(在這個例子中是4)之前,LongAdder的性能一直是個常量。
一個周期內的指令
周期內的指令衡量標準是:”CPU運行指令的時間” 對比 “CPU等待加載內存或者處理高速緩存加載或匹配數據的時間”。在這個例子中,我們看到Atomic在多線程的情況下IPC(周期內的指令)指標非常的差,而LongAdder維持著一個更健康的IPC(周期內的指令)指標. 從4線程到8線程之間的下降可能是因為CPU有4個核,每個核中有2個硬件線程, 而硬件線程在這個情況下沒有實際上的幫助。
空閑時間
處理器上的執行流水線主要分為2個部分:負責獲取和解碼操作的前端,和負責執行指令的后端。獲取的操作沒有什么值得討論的,所以我們跳過前端。
后端揭示了更多幕后的情況,AtomicLong的實現在后端留下了比LongAdder幾乎一倍的周期閑置。AtomicLong的IPC較低和它的高閑置時間是直接相關的:CPU內核花費了大量時間來決定它們中間的誰去控制包含AtomicLong的緩存線。
參考閱讀
原文鏈接: palominolabs 翻譯: ImportNew.com - 吳功偉
譯文鏈接: http://www.importnew.com/9560.html