2015-11-11 78 views
1

我正在閱讀JMH框架的樣本,並且我有一個關於樣本代碼JMHSample_12_Forking的問題。運行此代碼後,我有以下結果(正如筆者預測):兩種等同方法的性能差異

testJavaUtilConcurrency.JMHSample_12_Forking.measure_1_c1   avgt 5 3.314 ± 0.200 ns/op 
testJavaUtilConcurrency.JMHSample_12_Forking.measure_2_c2   avgt 5 22.403 ± 1.023 ns/op 
... 

這一結果解釋如下:

注意,C1更快,C2較慢,但C1是緩慢的再次!這是因爲...

但我的問題是:爲什麼C2比C1慢?兩個類中的代碼和兩種方法看起來完全一樣,那麼,性能差異的來源是什麼?

更新:

我已嘗試添加第三個實施計數器並取得以下成果:

testJavaUtilConcurrency.JMHSample_12_Forking.measure_1_c1   avgt 5 3.328 ± 0.073 ns/op 
testJavaUtilConcurrency.JMHSample_12_Forking.measure_2_c2   avgt 5 22.437 ± 0.552 ns/op 
testJavaUtilConcurrency.JMHSample_12_Forking.measure_2_c3   avgt 5 44.614 ± 5.080 ns/op 
testJavaUtilConcurrency.JMHSample_12_Forking.measure_3_c1_again avgt 5 43.535 ± 1.154 ns/op 

回答

4

在第一個試驗中,還有的Counter一個實現。 JIT編譯器能夠假定任何調用measure(Counter)的東西都使用相同的實現,所以它可以從inc()內聯代碼。

在第二個測試中,我們介紹了第二個實現 - 現在調用需要內聯兩個實現,或者對每個迭代執行動態分派。由於這種不確定性(無論選擇哪種),這比第一次測試慢得多。

在第三個測試中,我們使用與第一個測試相同的實現 - 但世界的狀態與第一個測試不同,因爲JIT仍然知道第二個實現存在......它可以不會再相信Counter只有一個實現...所以它仍然必須以比第一次測試更慢的方式執行inc()

這個故事的寓意是它不僅影響性能的代碼 - 它是世界的狀態。第一次測試中的世界狀態比第二次和第三次測試中的世界狀態好得多(從優化角度看)。

+0

有趣。你能否詳細說明你的答案(*第二個測試部分*),爲什麼動態調度會使測試變慢? – rajuGT

+0

@rajuGT:它會比內聯代碼慢,不是嗎?它涉及內存讀取和跳轉,而內聯代碼不會... –

+0

@JonSkeet感謝您的回答,但我不確定您是否正確。我試圖爲Counter添加第三個實現,並且它給出了比第二個更高的結果(請參見更新後的問題)。 –