2009-05-14 7 views
14

मैं एक आवेदन लिख रहा हूं जहां स्मृति, और कुछ हद तक गति, महत्वपूर्ण हैं। मुझे प्रोफाइलिंग से मिला है कि मै मैप और सेट ऑपरेशंस में काफी समय बिताता हूं। जबकि मैं इन तरीकों को कम करने के तरीकों को कम करता हूं, मैं सोच रहा हूं कि क्या वहां से बाहर कोई भी लिखा है, या पूरे समय में, कार्यान्वयन जो पहुंच समय या मेमोरी ओवरहेड में काफी सुधार करता है? या कम से कम, कुछ चीजों को देखते हुए इन चीजों में सुधार कर सकते हैं?java.util.Map और java.util.Set के अनुकूलित कार्यान्वयन?

जेडीके स्रोत को देखने से मुझे विश्वास नहीं है कि इसे तेज या दुबला नहीं बनाया जा सकता है।

मुझे कॉमन्स संग्रह से अवगत है, लेकिन मुझे विश्वास नहीं है कि इसका कोई कार्यान्वयन है जिसका लक्ष्य तेज़ या दुबला होना है। Google संग्रह के लिए वही।

अद्यतन: ध्यान दिया जाना चाहिए कि मुझे थ्रेड सुरक्षा की आवश्यकता नहीं है।

+0

संचालन किस तरह धीमा करने के लिए कर रहे हैं, प्रविष्टि या देखने या यात्रा? आपको अपने संग्रहों के साथ क्या करने की ज़रूरत है, वस्तुओं को पुनर्प्राप्त करें या उन्हें ऑर्डर करें या जांचें कि क्या वे संग्रह में निहित हैं या नहीं? क्या आपको सभी वस्तुओं को स्मृति में रखने की आवश्यकता है या नहीं? – pgras

+0

यह मुझे भी रूचि देता है ... धीमा क्या है और क्यों? यदि हैशकोड और बराबर हैं तो मानचित्र/सेट आमतौर पर बहुत तेज़ होते हैं। क्या आपका हैशकोड अलग और अद्वितीय है? – ReneS

+0

मैं लगभग पूरी तरह से() संचालन करता हूं। हैशसेट वास्तव में आमतौर पर ठीक है; यह है कि मेरे पास बहुत से सेट हैं, और लाखों मिलते हैं() एस। स्मृति या गति में 1% लाभ भी ढूंढना सार्थक होगा। निश्चित रूप से मैं बस() कम करने, या सेट को छीनने के तरीकों को देखता हूं। –

उत्तर

11

आम तौर पर ये विधियां बहुत तेज़ हैं। कुछ ऐसी चीजें हैं जिन्हें आपको जांचना चाहिए: क्या आपके हैश कोड लागू किए गए हैं? क्या वे पर्याप्त रूप से समान हैं? अन्यथा आप बकवास प्रदर्शन प्राप्त करेंगे।

http://trove4j.sourceforge.net/ < - यह थोड़ा तेज है और कुछ स्मृति बचाता है। मैंने 50,000 अपडेट पर कुछ एमएस बचाए

क्या आप वाकई नक्शे/सेट का सही उपयोग कर रहे हैं? यानी सभी मूल्यों या समान कुछ पर फिर से प्रयास करने की कोशिश नहीं कर रहा है। इसके अलावा, उदा। एक शामिल नहीं है और फिर एक हटा दें। बस निकालें की जांच करें।

यह भी जांचें कि क्या आप डबल बनाम डबल का उपयोग कर रहे हैं। मैंने दस हजारों चेक पर कुछ एमएस प्रदर्शन सुधार देखा।

क्या आपने प्रारंभिक क्षमता को सही/उचित तरीके से स्थापित किया है?

+0

हाँ hashCode (समवर्ती संग्रह ठीक है के रूप में बराबर है का उपयोग करें() और हाँ मैं भी बेवकूफ नहीं किया जा रहा है कर रहा हूँ (अर्थात entrySet का उपयोग कर() जहां उदाहरण के लिए लागू हो)। trove4j एक अच्छा सीसा है। –

+3

सिर्फ एक विचार: क्या आपने अपनी वस्तुओं को अपरिवर्तनीय बनाने और फिर हैश कोड को पूर्व-कंप्यूटिंग करने के बारे में सोचा है। – Egwor

0

शायद यह Map या Set इतना नहीं है जो समस्या का कारण बनता है, लेकिन उनके पीछे की वस्तुएं। आपकी समस्या के आधार पर, आप एक और डेटाबेस-प्रकार योजना चाहते हैं जहां "ऑब्जेक्ट" जावा ऑब्जेक्ट्स के बजाए बाइट्स के समूह के रूप में संग्रहीत किए जाते हैं। आप एक डेटाबेस (जैसे अपाचे डर्बी) एम्बेड कर सकते हैं या अपनी खुद की विशेषज्ञ चीज कर सकते हैं। यह वास्तव में आप क्या कर रहे हैं पर निर्भर है। HashMap जानबूझकर बड़ा और धीमा नहीं है ...

+0

मैं नहीं दिख रहा है कि कैसे वस्तुओं की प्रकृति में परिवर्तन एक सेट या मानचित्र उन्हें देख सकते हैं कितनी तेजी से, या क्यों एक डेटाबेस होगा नक्शा कार्यान्वयन से दुबला और तेज़ –

+0

अधिक मेमोरी का मतलब कठिन काम कैश है। बराबर और हैशकोड का कार्यान्वयन भी महत्वपूर्ण है। यदि समानता को स्मृति के विभिन्न आवंटन में विभिन्न डेटा का पीछा करना पड़ता है, तो यह धीमा होने वाला है। यदि हैशकोड टकराव का कारण बनता है जो धीमा होने वाला है। –

2

आप प्रारंभिक बिंदु के रूप में सार सार और/या सारसेट को बढ़ा सकते हैं। मैंने यह बहुत पहले एक बाइनरी त्रिभुज आधारित मानचित्र को लागू करने के लिए नहीं किया था (कुंजी एक पूर्णांक थी, और पेड़ पर प्रत्येक "स्तर" थोड़ा सा स्थान था। बायां बच्चा 0 था और दायां बच्चा 1 था)। यह हमारे लिए अच्छा काम करता है क्योंकि कुंजी ईयूआई -64 पहचानकर्ता थी, और हमारे लिए अधिकांश समय शीर्ष 5 बाइट समान होने जा रहे थे।

एक सार मैप को लागू करने के लिए, आपको Map.Entry का एक सेट वापस करने के लिए एंट्रीसेट() विधि को कम से कम कार्यान्वित करने की आवश्यकता है, जिनमें से प्रत्येक एक कुंजी/मान जोड़ी है।

एक सेट को लागू करने के लिए, आप सार सेटसेट बढ़ाते हैं और आकार() और इटरेटर() के कार्यान्वयन की आपूर्ति करते हैं।

हालांकि, कम से कम यह है। आप प्राप्त करने और डालने के लिए भी लागू करना चाहते हैं, क्योंकि डिफ़ॉल्ट मानचित्र अपरिवर्तनीय है, और प्रविष्टिसेट के माध्यम से पुनरावृत्त करने का डिफ़ॉल्ट कार्यान्वयन एक मैच की तलाश में है।

+0

मई को इस तरह जाना होगा - यह सुनकर उम्मीद थी कि किसी ने इसे पहले से ही खींचा है। –

7

क्या आपने Trove4J पर देखा है? वेबसाइट से:

ट्रोव का लक्ष्य java.util.Collections API के तेज़, हल्के कार्यान्वयन प्रदान करना है।

बेंचमार्क here प्रदान किए गए।

4

अपने बराबर और हैशकोड विधियों के प्रदर्शन में सुधार करने का प्रयास करें, इससे आपके ऑब्जेक्ट्स के मानक कंटेनर का उपयोग तेज हो सकता है।

+1

हाँ वे जितनी जल्दी हो सके हैं - केवल मेरे मामले में इंट्स की तुलना/वापसी। हालांकि अच्छा बिंदु। –

0

कॉमन्स संग्रह FastArrayList, FastHashMap और FastTreeMap है, लेकिन मैं नहीं जानता कि वे क्या लायक हो ...

+0

कॉमन्स संग्रह जेनरिक का समर्थन नहीं करता और पुराना है। Google संग्रह बहुत सारे स्मार्ट लोगों द्वारा बहुत सी जांच के माध्यम से किया गया है। मैं पहले वहां देखता हूं। – erickson

+0

हाँ यहां अच्छा नेतृत्व है लेकिन ये कार्यान्वयन थ्रेड-सुरक्षित कार्यान्वयन में थ्रेड विवाद को दूर करने के लिए अधिकतर पढ़ने वाले वातावरण में ऑप्टिमाइज़ करने का प्रयास कर रहे हैं। मुझे ध्यान रखना चाहिए कि मुझे थ्रेड-सुरक्षा की आवश्यकता नहीं है। –

+0

आजकल, मैं वास्तव में सिर्फ) जावा में शुरू की गई 5. –

1

कॉमन्स-संग्रह में कम से कम एक कार्यान्वयन है जो विशेष रूप से गति के लिए बनाया गया है: Flat3Map यह बहुत विशिष्ट है कि यह वास्तव में तब तक त्वरित होगा जब तक 3 से अधिक तत्व नहीं हैं।

मुझे संदेह है कि आप @ थैगी की सलाह के बाद अधिक मिलेज प्राप्त कर सकते हैं, बराबर/हैशकोड विधि के समय को देखें।

0
  • कॉमन्स संग्रह में एक आईडी मानचित्र है जो == के माध्यम से तुलना करता है, जो तेज़ होना चाहिए। - [Joda Primities][1] जैसा कि प्राचीन संग्रह है, जैसा ट्रोव करता है। मैंने ट्रोव के साथ प्रयोग किया और पाया कि इसकी स्मृति उपयोग बेहतर है।
  • मैं कुछ इंटीग्रियों के साथ कई छोटी वस्तुओं के संग्रह मैपिंग कर रहा था। इन्हें इनट्स में बदलने से लगभग आधा मेमोरी बचाई जाती है (हालांकि क्षतिपूर्ति के लिए कुछ मेसीयर एप्लिकेशन कोड की आवश्यकता होती है)।
  • यह मेरे लिए उचित लगता है कि पेड़ को क्रमशः हैशैप्स की तुलना में कम स्मृति का उपभोग करना चाहिए क्योंकि उन्हें लोड फैक्टर की आवश्यकता नहीं होती है (हालांकि अगर कोई पुष्टि कर सकता है या इसका कारण है कि यह वास्तव में गूंगा क्यों है तो टिप्पणियों में पोस्ट करें)।
+0

सॉर्ट किए गए पेड़ों को सामान्य लुकअप के लिए धीमा होना चाहिए क्योंकि उनकी संरचना ऑर्डरिंग को बनाए रखने के लिए उन्मुख है। तुलना में हैश-आधारित कार्यान्वयन ओ (1) होना चाहिए। आपको डेटा संरचनाओं में ओवरहेड के बारे में सोचने का अधिकार है - यही वही है जो मुझे चिंतित है। ट्रीमैप और हैशैप दोनों एक कुंजी का उपयोग करते हैं। प्रत्येक कुंजी के लिए आंतरिक रूप से आंतरिक वस्तु का उपयोग करें। हैश मैप मुझे लगता है कि खाली हैश टेबल स्लॉट के कारण थोड़ा अधिक ओवरहेड है लेकिन यह मामूली है। लेकिन हाँ, मैं उन सभी मानचित्रों से बचना चाहता हूं। उदाहरण के लिए एंटर्री ऑब्जेक्ट्स। –

1

आपने कहा कि आपने कुछ कक्षाएं प्रोफाइल की हैं लेकिन क्या आपने अपनी गति की जांच करने के लिए कोई समय किया है? मुझे यकीन नहीं है कि आप उनकी मेमोरी उपयोग की जांच कैसे करेंगे। ऐसा लगता है कि जब आप विभिन्न कार्यान्वयन की तुलना कर रहे हों तो कुछ विशिष्ट आंकड़े हाथ में रखना अच्छा लगेगा।

+0

रूपरेखा महत्वपूर्ण समय HashMap, HashSet, आदि उनकी पूर्ण गति वहाँ बिताए समय के रिश्तेदार राशि की तुलना में अप्रासंगिक है के तरीकों के भीतर खर्च को दर्शाता है। मैं सरणियों और Map.Entry वस्तुओं, HashMap से आवंटित उदाहरण के लिए देख सकते हैं, डेटा संरचना की स्मृति भूमि के ऊपर की भावना लाने के लिए। –

6

यहाँ गूगल और कॉमन्स संग्रह के अलावा, लोगों को मैं जानता हूँ कि इस प्रकार हैं:

बेशक आप हमेशा अपने स्वयं के डेटा संरचनाओं को लागू कर सकते जो आपके उपयोग के मामलों के लिए अनुकूलित हैं। बेहतर मदद करने में सक्षम होने के लिए, हमें आपको पैटर्न तक पहुंचने और संग्रह में किस प्रकार का डेटा स्टोर करना होगा, उसे जानने की आवश्यकता होगी।

1

यहाँ कुछ नोट और कई वैकल्पिक डेटा संरचना पुस्तकालयों के लिए लिंक कर रहे हैं: http://www.leepoint.net/notes-java/data/collections/ds-alternatives.html

मैं भी fastutil के लिए एक मजबूत मतदान में फेंक देंगे।(एक और प्रतिक्रिया में उल्लिखित, और उस पृष्ठ पर) इसमें एक छड़ी को हिलाकर, और मूल प्रकार के लिए कुंजी या मान के रूप में अनुकूलित संस्करणों की तुलना में अधिक भिन्न डेटा संरचनाएं हैं। (ए दोष यह है कि जार फ़ाइल बहुत बड़ा है, लेकिन आप शायद करने के लिए यह ट्रिम कर सकते हैं तो आप सिर्फ क्या जरूरत है)

2

आप संभवतः द्वारा स्मृति पर एक छोटे से बचा सकते हैं:

(क) एक मजबूत, व्यापक उपयोग करते हुए हैश कोड, और इस प्रकार कुंजी स्टोर करने से परहेज करते हुए;

(बी) खुद को एक सरणी से आवंटित करके, प्रति हैश तालिका प्रविष्टि पर एक अलग ऑब्जेक्ट बनाने से परहेज करें।

यदि यह उपयोगी है, तो संख्यात्मक प्राप्तकर्ता हैश तालिका का कोई भी फ्रिल्स जावा कार्यान्वयन नहीं है जिसे मैंने कभी-कभी उपयोगी पाया है। आप सीधे CharSequence (स्ट्रिंग समेत) पर कुंजी कर सकते हैं, अन्यथा आपको अपने ऑब्जेक्ट्स के लिए एक मजबूत-आश 64-बिट हैश फ़ंक्शन के साथ आना चाहिए।

याद रखें, यह कार्यान्वयन कुंजी को स्टोर नहीं करता है, इसलिए यदि दो आइटमों में एक ही हैश कोड है (जिसे आप 2^32 या दो बिलियन आइटम के क्रम में हैशिंग के बाद उम्मीद करेंगे एक अच्छा हैश फ़ंक्शन), फिर एक आइटम दूसरे को ओवरराइट करेगा:

public class CompactMap<E> implements Serializable { 
    static final long serialVersionUID = 1L; 

    private static final int MAX_HASH_TABLE_SIZE = 1 << 24; 
    private static final int MAX_HASH_TABLE_SIZE_WITH_FILL_FACTOR = 1 << 20; 

    private static final long[] byteTable; 
    private static final long HSTART = 0xBB40E64DA205B064L; 
    private static final long HMULT = 7664345821815920749L; 

    static { 
    byteTable = new long[256]; 
    long h = 0x544B2FBACAAF1684L; 
    for (int i = 0; i < 256; i++) { 
     for (int j = 0; j < 31; j++) { 
     h = (h >>> 7)^h; 
     h = (h << 11)^h; 
     h = (h >>> 10)^h; 
     } 
     byteTable[i] = h; 
    } 
    } 

    private int maxValues; 
    private int[] table; 
    private int[] nextPtrs; 
    private long[] hashValues; 
    private E[] elements; 
    private int nextHashValuePos; 
    private int hashMask; 
    private int size; 

    @SuppressWarnings("unchecked") 
    public CompactMap(int maxElements) { 
    int sz = 128; 
    int desiredTableSize = maxElements; 
    if (desiredTableSize < MAX_HASH_TABLE_SIZE_WITH_FILL_FACTOR) { 
     desiredTableSize = desiredTableSize * 4/3; 
    } 
    desiredTableSize = Math.min(desiredTableSize, MAX_HASH_TABLE_SIZE); 
    while (sz < desiredTableSize) { 
     sz <<= 1; 
    } 
    this.maxValues = maxElements; 
    this.table = new int[sz]; 
    this.nextPtrs = new int[maxValues]; 
    this.hashValues = new long[maxValues]; 
    this.elements = (E[]) new Object[sz]; 
    Arrays.fill(table, -1); 
    this.hashMask = sz-1; 
    } 

    public int size() { 
    return size; 
    } 

    public E put(CharSequence key, E val) { 
    return put(hash(key), val); 
    } 

    public E put(long hash, E val) { 
    int hc = (int) hash & hashMask; 
    int[] table = this.table; 
    int k = table[hc]; 
    if (k != -1) { 
     int lastk; 
     do { 
     if (hashValues[k] == hash) { 
      E old = elements[k]; 
      elements[k] = val; 
      return old; 
     } 
     lastk = k; 
     k = nextPtrs[k]; 
     } while (k != -1); 
     k = nextHashValuePos++; 
     nextPtrs[lastk] = k; 
    } else { 
     k = nextHashValuePos++; 
     table[hc] = k; 
    } 
    if (k >= maxValues) { 
     throw new IllegalStateException("Hash table full (size " + size + ", k " + k); 
    } 
    hashValues[k] = hash; 
    nextPtrs[k] = -1; 
    elements[k] = val; 
    size++; 
    return null; 
    } 

    public E get(long hash) { 
    int hc = (int) hash & hashMask; 
    int[] table = this.table; 
    int k = table[hc]; 
    if (k != -1) { 
     do { 
     if (hashValues[k] == hash) { 
      return elements[k]; 
     } 
     k = nextPtrs[k]; 
     } while (k != -1); 
    } 
    return null; 
    } 

    public E get(CharSequence hash) { 
    return get(hash(hash)); 
    } 

    public static long hash(CharSequence cs) { 
    if (cs == null) return 1L; 
    long h = HSTART; 
    final long hmult = HMULT; 
    final long[] ht = byteTable; 
    for (int i = cs.length()-1; i >= 0; i--) { 
     char ch = cs.charAt(i); 
     h = (h * hmult)^ht[ch & 0xff]; 
     h = (h * hmult)^ht[(ch >>> 8) & 0xff]; 
    } 
    return h; 
    } 

} 
+0

हालांकि मेरे मामले में, केवल एक विकल्प नहीं है, केवल एक विकल्प है। हाँ, मैं एक कूबड़ है कि मैं एक कार्यान्वयन रैखिक जांच, बल्कि अलग श्रृंखलन से साथ एक सरणी का उपयोग करता है चाहते हैं - जो है, कंटेनर वस्तुओं के पास कोई भी लिंक सूचियों। –

+0

एनबी कड़ाई से यह उदाहरण रैखिक जांच नहीं है। हम वास्तव में एक "बाल्टी" में मिनी सूचियों का आवंटन, यह सिर्फ है कि उन मिनी सूचियों एक सरणी से आवंटित कर रहे हैं। –

+0

* "यदि आप एक अच्छा हैश फ़ंक्शन है तो 2^32 या दो अरब वस्तुओं के क्रम में हैशिंग के बाद आप उम्मीद करेंगे" * - इससे कोई फर्क नहीं पड़ता कि आपके हैश फ़ंक्शन कितना अच्छा है, लगभग 64-बिट लंबे हैंश और 2 ** 32 कुंजी संघर्ष लगभग निश्चित हैं। उनकी संभावना कम करने के लिए आपको बहुत कम चाबियाँ चाहिए। आईएमएचओ, यह मानचित्र के रूप में उपयोग करने योग्य नहीं है, लेकिन यह कैश के लिए काफी अच्छा हो सकता है। – maaartinus

0

जेवीएम का कौन सा संस्करण आप उपयोग कर रहे हैं?

यदि आप 6 पर नहीं हैं (हालांकि मुझे संदेह है कि आप हैं) तो 6 पर स्विच मदद कर सकता है।

यदि यह एक सर्वर अनुप्रयोग है और विंडोज़ पर चल रहा है तो सही हॉटस्पॉट कार्यान्वयन का उपयोग करने के लिए सर्वर का उपयोग करने का प्रयास करें।

+0

हाँ, जावा 6 पर, और निश्चित रूप से पास करने वाले सर्वर। –

1

मैं कुछ साल पहले इस तरह से कुछ चला गया - बहुत बड़े मानचित्र और सेट्स के साथ-साथ उनमें से बहुत से। डिफ़ॉल्ट जावा कार्यान्वयन ने बहुत अधिक जगह का उपभोग किया। अंत में मैंने अपना खुद का लुत्फ उठाया, लेकिन केवल मेरे उपयोग के वास्तविक उपयोग पैटर्न की जांच करने के बाद ही। उदाहरण के लिए, मेरे पास वस्तुओं का एक बड़ा समूह था जिसे शुरुआती बनाया गया था और कुछ मानचित्र स्पैस थे जबकि अन्य घने थे। अन्य संरचनाएं एक-दूसरे से बढ़ीं (कोई हटाना नहीं) जबकि अन्य स्थानों में यह "संग्रह" का उपयोग करने के लिए तेज़ था और डुप्लिकेट से बचने के लिए समय और स्थान खर्च करने के बजाय डुप्लिकेट आइटमों को संसाधित करने के कभी-कभी लेकिन हानिरहित अतिरिक्त काम करता था। मेरे द्वारा उपयोग किए जाने वाले कई कार्यान्वयन सरणी समर्थित थे और इस तथ्य का शोषण करते थे कि मेरे हैशकोड अनुक्रमिक रूप से आवंटित किए गए थे और इस प्रकार घने मानचित्रों के लिए एक लुकअप केवल एक सरणी पहुंच था।

ले गए संदेशों: अपने एल्गोरिथ्म पर

  1. देखो,
  2. कई कार्यान्वयन पर विचार करें, और
  3. याद रखें कि पुस्तकालयों के सबसे वहाँ सामान्य प्रयोजन के उपयोग के लिए पूरा कर रहे हैं बाहर (जैसे सम्मिलित और हटाएं, आकारों की एक श्रृंखला, न तो स्पैस और न ही घने, इत्यादि) ताकि वे ओवरहेड प्राप्त कर सकें जिनसे आप शायद बच सकते हैं।

ओह, और लिखने इकाई परीक्षण ...

1

कई बार जब मैं देख रहा हूँ मानचित्र और सेट संचालन सीपीयू के एक उच्च प्रतिशत का उपयोग कर रहे है, यह संकेत दिया है से अधिक इस्तेमाल किया मानचित्र और सेट और पुनर्गठन किया है कि मैं मेरे डेटा ने शीर्ष 10% सीपीयू उपभोक्ता से संग्रह को लगभग समाप्त कर दिया है।

देखें आप संग्रह की प्रतियां से बच सकते हैं अगर, संग्रह और किसी भी अन्य आपरेशन जो संग्रह के तत्वों का सबसे पहुँचने और वस्तुओं बनाने में परिणाम से अधिक पुनरावृत्ति।

0

मैं निम्नलिखित पैकेज (koloboke) का उपयोग करें, एक पूर्णांक-पूर्णांक hashmap ऐसा करने के लिए, क्योंकि यह promitive प्रकार का समर्थन करता है और यह एक लंबे समय तक चर में दो पूर्णांक संग्रहीत करता है, यह मेरे लिए अच्छा है। koloboke