2011-09-01 81 views
6

我想測試Java中自動裝箱和拆箱的速度,但是當我嘗試將其與原始空循環進行比較時,我注意到一件奇怪的事情。這個片斷:Java:空循環使用多長時間?

for (int j = 0; j < 10; j++) { 
    long t = System.currentTimeMillis(); 
    for (int i = 0; i < 10000000; i++) 
     ; 
    t = System.currentTimeMillis() - t; 
    System.out.print(t + " "); 
} 

我每次運行它,它返回相同的結果:

6 7 0 0 0 0 0 0 0 0

爲什麼前兩個循環總是需要一定的時間,那麼剩下的似乎只是跳過由系統?

In this answer to this post,據說Just-In-Time編譯將能夠優化這一點。但如果是這樣,爲什麼前兩個循環仍需要一些時間?

+3

我認爲'System.nanoTime()'更是爲這個測試表明(客戶端/服務器的虛擬機也應該做一些差異) –

+0

是啊,沒錯,毫秒往往過於粗粒度。 –

回答

20

JIT觸發器多次執行某段代碼後。

HotSpot JVM將嘗試識別代碼中的「熱點」。熱點是您執行很多次代碼的部分。爲此,JVM將「計數」各種指令的執行情況,當它確定某個片段頻繁執行時,它將觸發JIT。 (這是一個近似值,但這很容易理解)。 JIT(Just-In-Time)接受這段代碼,並試圖使其更快。

使用的JIT,使你的代碼運行速度更快的技術有很多,但最常見的造成混亂的一個是:

  1. 它將嘗試以確定是否那塊代碼使用的變量沒有用在其他地方(無用的變量),並刪除它們。
  2. 如果您獲取和同一鎖多次釋放(如調用同一個對象的synchronized方法),它可以一次性獲得鎖定,如果你訪問對象的成員做一個synchronized塊
  3. 所有來電那些沒有被聲明爲volatile的,它可以決定優化它(在寄存器和類似的地方放置值),在多線程代碼中創建奇怪的結果。
  4. 它會內聯方法,以避免通話費用。
  5. 它會將字節碼轉換爲機器碼。
  6. 如果循環完全無用,它可能會被完全刪除。

所以,你的問題的正確答案是,被JITed後的空循環,沒有時間執行..最有可能不存在了。

同樣,還有很多其他的優化,但根據我的經驗,這些是最令人頭痛的問題。此外,JIT在任何新版本的Java中都得到了改進,有時它甚至會根據平臺而有所不同(因爲它在某種程度上是平臺特定的)。由JIT完成的優化難以理解,因爲即使在最近的Java版本中,這些優化中的某些優化已直接移至編譯器中,您通常無法使用javap查找它們並檢查字節碼(例如,自從Java 6編譯器能夠檢測和警告未使用的局部變量和私有方法)。

如果您正在編寫一些循環來測試某些內容,通常是將循環放入方法中,在計時之前調用該方法幾次以使其「加速」,然後執行定時循環。

這通常會觸發在你們這樣一個簡單的程序的JIT,即使不能保證它確實會觸發(或者說,它甚至存在於一定的平臺)。

如果你想得到關於JIT或非JIT時間的偏執(我做過):做第一輪,計算每個執行循環的時間,並等待時間穩定(例如,與平均值的差值小於10 %),然後從「真實」時間開始。

7

的JIT不踢上一段代碼,直到確定有一些好處這樣做。這意味着通過一些代碼的前幾次通過不會被打亂。

+0

謝謝!我明白了,但空循環可能做什麼?或者,正如@Simone詹尼說,JIT(是保證),除非環路已經有一段時間了執行不會被觸發?編輯:再次感謝!我從Simone的回答中得到了答案:) –