2017-10-28 310 views
5

注意:這是關於性能問題的而不是。我只觀察到我無法解釋/理解的表現差異。HashMap性能Java 9比Java 8少25%?

基準測試針對Java 9的一些新開發的代碼,我發現了一些奇怪的東西。使用5個鍵的HashMap(非常)簡單的基準測試表明Java 9比Java 8慢得多。可以解釋這個還是我的(基準)代碼只是錯誤的?

代碼:

@Fork(
    jvmArgsAppend = {"-Xmx512M", "-disablesystemassertions"} 
) 
public class JsonBenchmark { 

    @State(Scope.Thread) 
    public static class Data { 

     final static Locale RUSSIAN = new Locale("ru"); 
     final static Locale DUTCH = new Locale("nl"); 

     final Map<Locale, String> hashmap = new HashMap<>(); 

     public Data() { 
      hashmap.put(Locale.ENGLISH, "Flat flashing adjustable for flat angled roof with swivel"); 
      hashmap.put(Locale.FRENCH, "Solin pour toit plat inclinée"); 
      hashmap.put(Locale.GERMAN, "Flachdachkragen Flach Schrägdach"); 
      hashmap.put(DUTCH, "Plakplaat vlak/hellend dak inclusief glijschaal"); 
      hashmap.put(RUSSIAN, "Проход через плоскую кровлю регулир. для накл. кровли"); 
     } 

    } 

    @Benchmark 
    public int bmHashMap(JsonBenchmark.Data data) { 
     final Map<Locale, String> m = data.hashmap; 
     int sum = 0; 
     sum += m.get(Data.RUSSIAN).length(); 
     sum += m.get(Locale.FRENCH).length(); 
     sum += m.get(Data.DUTCH).length(); 
     sum += m.get(Locale.ENGLISH).length(); 
     sum += m.get(Locale.GERMAN).length(); 
     return sum; 
    }  

} 

結果:

  • 爪哇8_151:JsonBenchmark.bmHashMap thrpt 40 47948546.439±560763.711 OPS/s的
  • 爪哇9_181:JsonBenchmark.bmHashMap thrpt 40 34962904.479±276045.691 OPS/s(-/- 27%!)

UPDATE

感謝您的回答和很好的評論。

  1. @Holger的建議。我的第一反應是:這一定是解釋。但是,如果我只以String#length()函數爲基準,性能沒有顯着差異。而且,當我僅以HashMap#get()方法爲基準時(正如@Eugene所建議的),仍有約10-12%的差異。

  2. @Eugene的建議。我改變了參數(更多的熱身迭代,更多的記憶),但我無法重現你的結果。然而,我把堆增加到了4G。但是這不能解釋差異,不是嗎?

  3. @Alan Bateman的建議。是的,這提高了性能!不過,相差約20%。

回答

7

您正在測試的不僅僅是HashMap。你不僅打電話給HashMap.get,你隱含地打電話給Locale.hashCodeLocale.equals。此外,您致電String.length

現在,所有四個人都可以改變他們的表現特徵,所以你需要更多的測試來推斷哪種方法表現出不同的表現。

但是最熱門的候選人是String.length。在Java 9中,String類不再使用char[]數組,而是使用byte[]數組來對每個字符只使用一個字節的拉丁語1字符串進行編碼,從而大大減少了典型應用程序的內存佔用量。然而,這意味着長度不再總是與陣列長度相同。所以這個操作的複雜性已經改變了。

但請記住,您的結果是在微型基準標記中的差值大約爲77納秒。這是不夠的,估計在實際應用中的影響......

+0

雖然這可能是一個有點多的要求,我不會介意你否認這個共。但是,如果可能的話(作爲jmh的新手),你能否在兩個版本中聲明'.length'的複雜度比較。在理解的答案和統計數據中看到這一點很棒。看了看[這裏](http://cr.openjdk.java.net/~shade/8085796/notes.txt),看看我能不能找出一些東西,但大部分都在我的頭上。 – nullpointer

+2

很好的答案。另一件要提到的是-XX:-CompactStrings在不使用緊湊字符串時可以看到不同的性能。 –

+1

@nullpointer:舊的實現就像'array.length',新的實現就像'arrays.length >> coder',編碼器可以是零或一個,這取決於字符串。實際應用中的影響取決於實際調用'length()'的頻率以及latin1字符串與其他字符串之間的比率。一些其他的字符串操作可能對latin1字符串來說更快(少量字節來剷除),這可能不僅僅是補償。 – Holger

3

我有一個提示,這是關於jmh設置,更多的則是約HashMap。如前所述,您在這裏測量很多更多隻是HashMap::get。但即使如此,我還是懷疑java-9會慢很多,所以我測量了自己(最新的jmh源自java-8和9)。

我沒有改變你的代碼 - 只是增加了更多的方式堆(10GB)和方式更多的熱身,從而減少了「錯誤」你看±

使用後的java-8:

Benchmark   Mode Cnt Score Error Units 
SOExample.bmHashMap avgt 25 22.059 ± 0.276 ns/op 

用java-9:

Benchmark   Mode Cnt Score Error Units 
SOExample.bmHashMap avgt 25 23.954 ± 0.383 ns/op 

結果都在標準桿幾乎沒有明顯的差異(這是納米秒後全部),你看到它。此外,如果你真的測試只是HashMap::get比你的方法可以簡單地返回該調用,就像這樣:

@Benchmark 
@Fork(5) 
public int bmHashMap(SOExample.Data data) { 
    return data.hashmap.get(data.key); // where key is a random generated possible key 
}