2011-04-26 145 views
2

我試圖剖析一些代碼,但我遇到了一些問題,我認爲Java的編譯器很聰明並且改變了代碼的工作方式。性能分析方法性能

例如方法:

public int method1(int bits) 
{ 
    // seed is a long object field 
    seed = seed * 0x5DEECE66DL + 0xBL & (1L << 48) - 1; 
    return (int) (seed >>> 48 - bits); 
} 

public int method2(int bits) 
{ 
    // seed is a long object field 
    seed *= 0x5DEECE66DL; 
    seed += 0xBL & (1L << 48) - 1; 
    return (int) (seed >>> 48 - bits); 
} 

public int method3(int bits) 
{ 
     // multiplier, seeds, n, and carry are long/long array object fields 
     final long t = multiplier * seeds[n] + carry; 
     // carry = t/b (done in an unsigned way) 
     final long div32 = t >>> 32; // div32 = t/(b+1) 
     carry = div32 + ((t & 0xFFFFFFFFL) >= 0xFFFFFFFFL - div32 ? 1L : 0L); 
     // seeds[n] = (b-1)-t%b (done in an unsigned way) 
     seeds[n] = 0xFFFFFFFEL - (t & 0xFFFFFFFFL) - (carry - div32 << 32) - carry & 0xFFFFFFFFL; 
     final long result = seeds[n]; 
     n = n + 1 & r - 1; 
     return (int) (result >>> 32 - bits); 
} 

現在,比較性能,我使用該測試臺:

// gen is the object containing the methods/fields 
int result; 
long start = System.currentTimeMillis(); 
for (int i = 0; i < 0x7FFFFFFF; ++i) 
{ 
    result = gen.method1(32); 
} 
long end = System.currentTimeMillis(); 
System.out.println(end - start); 

start = System.currentTimeMillis(); 
for (int i = 0; i < 0x7FFFFFFF; ++i) 
{ 
    result = gen.method2(32); 
} 
end = System.currentTimeMillis(); 
System.out.println(end - start); 

start = System.currentTimeMillis(); 
for (int i = 0; i < 0x7FFFFFFF; ++i) 
{ 
    result = gen.method3(32); 
} 
end = System.currentTimeMillis(); 
System.out.println(end - start); 

不過,我找回結果是相當奇怪:

7 
3109 
13402

method1method2的計算時間應該與計算完全相同的計算時間大致相同。 method3我可以理解需要更長時間,但看看計算量,3個結果中沒有一個對我來說足夠長。這似乎標誌着我以某種方式Java的優化我的測試平臺,而不是全部運行0x7FFFFFFF次。

爲了解決這個問題,我想聲明result作爲static volatile場(試驗檯法外),以及這樣做產生可信的結果:

21814 
21468 
26962

現在的問題是這是公認的如何配置方法?我仍然希望優化發生在我正在分析的方法中,而不是在測試平臺中(至少不會導致該方法被調用的次數少於指定次數或在運行之間緩存結果的優化)。

+0

問題的javap -v在你的類以查看編譯器做什麼不同。我認爲這種差異將會是一個額外的LLOAD和LSTORE操作(完成0x7FFFFFFF次)。 – MeBigFatGuy 2011-04-26 02:14:34

+0

我看到的唯一區別是在結果的聲明行中,無論是靜態的還是靜態的和易失性的。 Diff:' - 靜態int結果; + static volatile int result; public static void main(java.lang.String [])' – helloworld922 2011-04-26 02:33:21

回答

4

您應該在上使用探查器,比如YourKit或整個應用程序的類似。專注於問題領域而不是隨機數發生器,這不太可能成爲任何形式的瓶頸。你真的有一個每秒消耗超過8000萬的用例嗎?除了這些方法之外,其他任何事情都會花費更多的時間。你幾乎可以肯定地擔心大局的錯誤。

如果你真的想要或者有充足的理由繼續這種方法,至少請先閱讀:How do I write a correct micro-benchmark in Java?有很多因素會影響微基準,並將它們轉化爲真實的世界,而這些因素很少有作用。分析人員會告訴你你真的需要知道什麼,其餘的不值得花費太多時間。

+0

我這樣做更像是對不同生成器的原始性能的好奇心,而不一定是除了其他代碼之外它們的表現。感謝您的鏈接,我會看看他們。 – helloworld922 2011-04-26 02:40:17

+0

好奇心殺死了這隻貓;)如果你只是對發電機的原始性能感到好奇,那麼你需要一個微基準。我只想和Mersenne Twister一起去,而不用擔心更多的問題(除非你用它來加密)。 – WhiteFang34 2011-04-26 02:52:09

+0

好東西我是一個狗人:D我考慮了一些微基準的因素,現在結果更可信。 – helloworld922 2011-04-26 03:01:50

2

我認爲這是錯誤的做法。如果您試圖規避運行時優化器,我認爲測試不會有意義地預測您在生產中會遇到的情況。我建議放棄這條大道。

+0

它會繞過所有優化,還是隻是在測試裝備中優化?我只是想知道這些特定方法運行的時間是多少次,就好像它們被獨立調用了很多次一樣。 – helloworld922 2011-04-26 02:36:01

0

您應該按照不同的順序執行測試,因爲JVM可以在代碼運行時以不同的方式提取代碼。即編譯器不需要優化,所以你可以在同一個程序中多次運行代碼,它的性能可以改變,通常它會改善。

注意:對於短循環,如果放棄結果,JVM可以確定您沒有做任何有用的事情並放棄循環。不是確定循環運行時,而是確定JVM放棄循環需要多長時間。

如果您使用volatile,這可能比您正在測試的代價更高,這可能是使用voltile時結果大致相同的原因之一。;)

一個簡單的方法,以避免JVM優化環路什麼是增加的結果,並打印總(可以忽略)