2017-10-28 20 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; 
    }  

} 

결과 :

  • 자바 8_151 : JsonBenchmark.bmHashMap 40 47948546.439 ± 560763.711 작전을 thrpt/s의
  • 자바 9_181는 : JsonBenchmark.bmHashMap 40 34962904.479 ± 276045.691 작전을 thrpt/s (-/- 27 %!)

업데이트

답변과 훌륭한 의견을 보내 주셔서 감사합니다.

  1. @Holger의 제안. 저의 첫 반응은 설명 일 것입니다. 그러나, 만약 내가 벤치마킹 String#length() 기능, 성능에 큰 차이가 없습니다. 그리고 (@Eugene에서 제안한 것처럼) HashMap#get() 메서드를 벤치 마크 할 때 여전히 약 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 바이트 만 사용하여 라틴어 1 문자열을 인코딩함으로써 일반 응용 프로그램의 메모리 사용 공간을 크게 줄입니다. 그러나 이것은 길이가 더 이상 배열 길이와 항상 동일하지 않다는 것을 의미합니다. 따라서이 작업의 복잡성이 변경되었습니다.

결과는 마이크로 벤치에 의 차이가 약 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에서 빌드했습니다.

난 당신의 코드를 변경하지 않은

- 다만, 따라서 "오류"당신이 볼을 줄일 방법이 더 힙 (10기가바이트) 및 방법이 더 워밍업을 추가하여 ±

후 자바-8 :

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

자바-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 
}