2017-10-28 7 views
5

注:パフォーマンスの問題については、ではなく、です。私は説明できない/理解できないパフォーマンスの違いだけを観察します。HashMapのパフォーマンスJava 9 Java 8より25%少ない?

Java 9をターゲットにして新しく開発されたコードをベンチマークしているうちに、私は奇妙なことを発見しました。 HashMapの5つのキーを持つ(非常に)単純なベンチマークは、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; 
    }  

} 

結果:

  • のJava 8_151:JsonBenchmark.bmHashMapは、Java 9_181 40 47948546.439±560763.711のops /秒
  • をthrpt:JsonBenchmark.bmHashMapは40の34962904.479±276045.691 OPSをthrpt /秒(-/27%!)

更新

お返事ありがとうございました。

  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に電話しています。

4つすべてがパフォーマンス特性を変更している可能性があるため、どの方法が異なるパフォーマンスを示すかについて、より多くのテストが必要になります。

しかし、最もホットな候補はString.lengthです。 Java 9では、Stringクラスでは、char[]配列は使用されませんが、文字あたり1バイトのみを使用してラテン文字1文字をエンコードするためには、byte[]配列が使用され、標準的アプリケーションのメモリフットプリントが大幅に削減されます。しかしこれは、長さが配列の長さと常に同じではないことを意味します。そのため、この操作の複雑さが変化しました。

あなたの結果は、マイクロベンチマークで約77 nsの違いであることに注意してください。これは、実際のアプリケーションへの影響を見積もるのには十分ではありません。

+0

と私はあなたがこれを否定する気にしないだろう全部。可能であれば(jmhの初心者ですが)、両方のバージョンで '.length'の複雑さの比較を記述できますか?答えを見て、理解しながら統計的に見ることは素晴らしいことでしょう。私が何かを見つけ出すことができるかどうかを調べるために[ここ](http://cr.openjdk.java.net/~shade/8085796/notes.txt)を見ましたが、そのほとんどが私の頭を越えました。 – nullpointer

+2

いい答えです。コンパクトな文字列を使用しないときのパフォーマンスを見るために、-XX:-CompactStringsを挙げてください。 –

+1

@nullpointer:古い実装は 'array.length'に似ていましたが、新しい実装は' arrays.length >> coder'のようなものです。wheres coderは文字列に応じて0または1です。実際のアプリケーションの影響は、 'length()'が実際に呼び出される頻度と、latin1文字列と他の文字列の比率に依存します。他の文字列演算では、latin1文字列(バイトを減らすバイト数)がさらに高速になる可能性があります。 – Holger

3

これは約jmhのセットアップについてのヒントを持っていましたが、それ以上は約HashMapでした。すでに述べたように、が多く、以上はHashMap::get以上です。しかしそれでも、私はJava-9がそれほど遅くなるのではないかと疑っていたので、私は自分自身を測定しました(最新のjmhはソース、java-8と9からビルドしています)。

私はあなたのコードを変更していない - ちょうどので、「エラー」は、Java-8を使用して±

後に参照削減、道よりヒープ(10ギガバイト)と道よりウォームアップを追加しました:

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 
} 
関連する問題