2017-10-28 31 views
5

नोट: यह एक प्रदर्शन समस्या के बारे में नहीं है। मैं केवल प्रदर्शन में एक अंतर देखता हूं जिसे मैं समझा नहीं सकता/समझ सकता हूं।हैश मैप प्रदर्शन जावा 9 जावा 8 से 25% कम?

जावा 9 के लिए लक्षित कुछ नए विकसित कोड को बेंचमार्क करते समय मुझे कुछ अजीब पता चला। 512 के साथ HashMap का एक (बहुत) सरल बेंचमार्क बताता है कि जावा 9 जावा 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 ४७९४८५४६.४३९ ± 560763.711 ऑप्स/एस
  • जावा 9_181: JsonBenchmark.bmHashMap thrpt 40 ३४९६२९०४.४७९ ± 276045.691 ऑप्स/एस (-/- 27%!)

अद्यतन

उत्तर और महान टिप्पणियों के लिए धन्यवाद।

  1. @ होल्गर द्वारा सुझाव। मेरी पहली प्रतिक्रिया थी: यह स्पष्टीकरण होना चाहिए। हालांकि, अगर मैं केवल String#length() फ़ंक्शन को बेंचमार्क करता हूं, तो प्रदर्शन में कोई महत्वपूर्ण अंतर नहीं है। और, जब मैं केवल HashMap#get() विधियों को बेंचमार्क करता हूं (जैसा कि @ यूजीन द्वारा सुझाया गया है) अभी भी लगभग 10-12% का अंतर है।

  2. @Eugene द्वारा सुझाव। मैंने पैरामीटर बदल दिए (अधिक गर्मजोशी पुनरावृत्तियों, अधिक मेमोरी), लेकिन मैं आपके परिणाम को पुन: उत्पन्न करने में सक्षम नहीं हूं। हालांकि मैंने ढेर में 4 जी तक वृद्धि की। लेकिन यह अंतर की व्याख्या नहीं कर सकता है, है ना?

  3. @Alan Bateman द्वारा सुझाव। हाँ, यह प्रदर्शन में सुधार करता है! हालांकि, अभी भी लगभग 20% का अंतर है।

उत्तर

7

आप केवल HashMap से अधिक परीक्षण कर रहे हैं। आप केवल HashMap.get पर कॉल नहीं कर रहे हैं, आप निहित रूप से Locale.hashCode और Locale.equals पर कॉल कर रहे हैं। इसके अलावा, आप String.length पर कॉल कर रहे हैं।

अब, सभी चार अपनी प्रदर्शन विशेषताओं को बदल सकते थे, इसलिए आपको किस तरीके (तरीके) प्रदर्शन के विभिन्न प्रदर्शनों के बारे में तर्क के लिए और अधिक परीक्षण की आवश्यकता होगी।

लेकिन सबसे गर्म उम्मीदवार String.length है। जावा 9 में, String कक्षा अब char[] सरणी का उपयोग नहीं करती है, लेकिन byte[] सरणी, केवल एक बाइट प्रति चरित्र का उपयोग करके लैटिन 1 तारों को एन्कोड करने के लिए, नाटकीय रूप से सामान्य अनुप्रयोगों की स्मृति पदचिह्न को कम करती है। हालांकि, यह दर्शाता है कि लंबाई हमेशा सरणी लंबाई के समान नहीं है। तो इस ऑपरेशन की जटिलता बदल गई है।

लेकिन ध्यान रखें कि आपका परिणाम लगभग 77 एनएस अंतर माइक्रोबेंमार्क में है।वास्तविक अनुप्रयोग पर प्रभाव का आकलन करने के लिए पर्याप्त नहीं है ...

+0

हालांकि इस एक पूछने का एक छोटा ज्यादा हो सकता है और मैं तुम्हें इस बात का खंडन कोई फ़र्क नहीं पड़ेगा कुल मिलाकर। लेकिन (यदि संभव हो तो jmh में नौसिखिया होने के नाते, क्या आप दोनों संस्करणों में 'लम्बाई' की जटिलता तुलना बता सकते हैं। समझने के दौरान उत्तर और सांख्यिकीय में यह देखना बहुत अच्छा होगा। यह देखने के लिए कि क्या मैं कुछ ढूंढ सकता हूं, [http://cr.openjdk.java.net/~shade/8085796/notes.txt) देखा, लेकिन इसमें से अधिकांश मेरे सिर पर चले गए। – nullpointer

+2

अच्छा जवाब। उल्लेख करने की एक और बात है -XX: - कॉम्पैक्ट तारों का उपयोग न करने पर प्रदर्शन को अलग देखने के लिए कॉम्पैक्ट्सट्रिंग्स। –

+1

@nullpointer: पुराना कार्यान्वयन 'array.length' जैसा था, नया कार्यान्वयन स्ट्रिंग के आधार पर' arrays.length >> coder' wheres coder या तो शून्य या एक जैसा है। असल में एक वास्तविक अनुप्रयोग इस बात पर निर्भर करता है कि कितनी बार 'लंबाई() 'वास्तव में बुलाया जाता है और लैटिन 1 तारों और अन्य तारों के बीच अनुपात होता है। कुछ अन्य स्ट्रिंग ऑपरेशन लैटिन 1 स्ट्रिंग (चारों ओर फावड़े के लिए कम बाइट्स) के लिए भी तेज़ हो सकते हैं, जो कि क्षतिपूर्ति से अधिक हो सकता है। – Holger

3

मुझे संकेत था कि यह लगभग jmh सेट-अप था, और फिर यह लगभग HashMap था। जैसा कि पहले से ही उल्लेख किया गया है कि आप को HashMap::get से बहुत अधिक माप रहे हैं। लेकिन फिर भी, मुझे संदेह था कि जावा-9 इतना धीमा होगा, इसलिए मैंने खुद को माप लिया (नवीनतम जेएमएच स्रोतों, जावा -8 और 9 से निर्मित)।

मैं अपने कोड नहीं बदला है - अभी जिस तरह अधिक ढेर (10GB) और जिस तरह से अधिक गर्म-अप जोड़ा है, इस प्रकार "त्रुटि" जैसा कि आप देख कम करने ±

का उपयोग करने के बाद जावा-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 
} 
संबंधित मुद्दे