2017-08-11 9 views
0

Java List/Mapのパフォーマンステストを実行しようとすると、奇妙なパフォーマンス結果が検出されます。値がハードコードされた文字列を使用すると、Javaマップのパフォーマンスが最悪になる

私のテストロジックは、データ準備とマップ操作を分割し、分離されたデータ準備プロセスは、マップ操作コストを正確に計算するオブジェクトです。

さまざまな文字列の作成方法が異なる結果の結果になることは理解できますが、奇妙なことは、特定のハードコードされた文字列を使用するとパフォーマンスが最悪になることです。

なぜこの結果はそうですか?ここで

は、テストコードで、パフォーマンスの結果は、私が正しいプロセスに従わなかったとして、それが悪いベンチマークですテストケースメソッドのコメント

package cn.codeworks.test; 

import java.util.ArrayList; 
import java.util.List; 
import java.util.Map; 
import java.util.TreeMap; 
import java.util.function.Function; 
import org.junit.Test; 

public class TreeMapPerformanceTest { 

    private static final int dataSize = 1000 * 1000; 

    static class Pair { 

    private final Integer key; 
    private final String value; 

    public Pair(Integer key, String value) { 
     this.key = key; 
     this.value = value; 
    } 

    public Integer getKey() { 
     return key; 
    } 

    public String getValue() { 
     return value; 
    } 

    } 

    /** 
    * time cost (3 times) = 196, 178, 186 
    */ 
    @Test 
    public void testPutPerformance_string_intern() { 
    testPutPerformance((loc) -> new String("abc").intern()); 
    } 

    /** 
    * time cost (3 times) = 275, 317, 331 
    */ 
    @Test 
    public void testPutPerformance_string_new() { 
    testPutPerformance((loc) -> new String("abc")); 
    } 

    /** 
    * this case got bad performance 
    * time cost (3 times) = 591, 565, 570 
    */ 
    @Test 
    public void testPutPerformance_string_hardcoded() { 
    testPutPerformance((loc) -> "abc"); 
    } 


    private void testPutPerformance(Function<Integer, String> stringCreateMethod) { 
    // prepare data 
    List<Pair> data = new ArrayList(dataSize); 
    for (int i = 0; i < dataSize; i++) { 
     Pair pair = new Pair(i, stringCreateMethod.apply(i)); 
     data.add(pair); 
    } 
    int size = data.size(); 

    // map operation 
    Map<Integer, String> map = new TreeMap<>(); 

    long startTimeMillis = System.currentTimeMillis(); 

    for (int i = 0; i < size; i++) { 
     Pair pair = data.get(i); 
     map.put(pair.getKey(), pair.getValue()); 
    } 

    long endTimeMillis = System.currentTimeMillis(); 
    System.out.println("time cost = " + (endTimeMillis - startTimeMillis)); 
    } 

} 
+3

ベンチマークは間違っているため、得られる結果は完全に信頼できません。 https://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java – Kayaman

+0

私は3番目(遅い)テストのランタイムはゴミを集めることによって支配されると確信しています第1および第2の試験から得た。テストはVALUESでのみ異なるので、KEYSではなくマップに格納するため、メインコードのパフォーマンスの違いはまったくありません。 Map.put()は値を調べません。短い文字列、長い文字列、中断されているかどうか、異なっていても同じであっても問題ありません。 –

+0

これらの3つのテストケースは、互いに影響を及ぼさないはずです.3つのケースを同時に1つのプロセスで実行したり、1つのプロセスで1つのケースを実行したりしないでください。 3番目のケースは常に最悪のパフォーマンスになります。 –

答えて

1

に含まれています。

[Full GC (Ergonomics) 114779K->32543K(270848K), 0.2512151 secs] 
[Full GC (Ergonomics) 141433K->35541K(340992K), 0.3268599 secs] 
[Full GC (Ergonomics) 59544K->55008K(227328K), 0.9451788 secs] 

が起きた1時間各ケースのフルGC、ほとんどの最も遅い場合の費用:追加のJVMオプション-XX:+PrintCompilation -verbose:gc

は、ここで再度テストを実行し、3テスト・ケースの結果であり、最も長いGC時間。

を追加した後、テストケースの実行中にFull GCを避けることを期待している場合、ケース間の時間コストの差が消えてしまいます。

関連する問題