मुझे समवर्तीSkipListMap का उपयोग कब करना चाहिए?


82

जावा में, ConcurrentHashMapबेहतर multithreadingसमाधान के लिए है। फिर मुझे कब उपयोग करना चाहिए ConcurrentSkipListMap? क्या यह अतिरेक है?

क्या इन दोनों के बीच बहुआयामी पहलू आम हैं?

जवाबों:


80

ये दोनों वर्ग कुछ मायनों में अलग-अलग हैं।

ConcurrentHashMap अपने अनुबंध के भाग के रूप में अपने संचालन के क्रम की गारंटी नहीं देता है। यह कुछ लोड फैक्टर के लिए ट्यूनिंग की अनुमति देता है (मोटे तौर पर, थ्रेड की संख्या समवर्ती इसे संशोधित करता है)।

दूसरी ओर, समवर्ती SipListMap , कई प्रकार के संचालन पर औसत O (लॉग (n)) प्रदर्शन की गारंटी देता है। यह भी संगामिति के लिए ट्यूनिंग का समर्थन नहीं करता है। ConcurrentSkipListMapकई ऑपरेशन भी हैं जो ConcurrentHashMapनहीं करते हैं: सीलिंगइंट्री / की, फ्लोरएंट्री / की, आदि। यह एक क्रमबद्ध क्रम भी रखता है, जिसे यदि आप उपयोग कर रहे हैं, तो अन्यथा गणना करना होगा (उल्लेखनीय खर्च पर) ConcurrentHashMap

मूल रूप से, विभिन्न उपयोग मामलों के लिए अलग-अलग कार्यान्वयन प्रदान किए जाते हैं। यदि आपको त्वरित एकल कुंजी / मान जोड़ी जोड़ और त्वरित एकल कुंजी लुकअप की आवश्यकता है, तो उपयोग करें HashMap। यदि आपको तेज़-इन-ऑर्डर ट्रैवर्सल की आवश्यकता है, और प्रविष्टि के लिए अतिरिक्त लागत वहन कर सकते हैं, का उपयोग करें SkipListMap

* हालांकि मुझे उम्मीद है कि कार्यान्वयन ओ (1) सम्मिलन / देखने की सामान्य हैश-मैप गारंटी के अनुरूप है; फिर से हैशिंग की अनदेखी


ठीक। लॉग (n) ठीक है, लेकिन क्या समवर्ती SsipListMap स्थान कुशल है?
DKSRathore

1
छोड़ें सूचियाँ आवश्यक रूप से हैशटेबल्स से बड़ी होती हैं, लेकिन समवर्ती हाशपा की ट्यून करने योग्य प्रकृति से एक हैशटेबल का निर्माण संभव हो जाता है जो समतुल्य समवर्तीSkipListMap से बड़ा होता है। सामान्य तौर पर, मुझे उम्मीद है कि स्किप सूची बड़ी होगी, लेकिन परिमाण के समान क्रम पर।
केविन मॉन्ट्रोस

"यह भी संगामिति के लिए ट्यूनिंग का समर्थन नहीं करता है" .. क्यों? लिंक क्या है?
पचेरियर

2
@Pacerier - मेरा मतलब यह नहीं था कि यह ट्यूनिंग का समर्थन करता है क्योंकि यह समवर्ती है, मेरा मतलब है कि यह आपको मानकों को ट्यून करने की अनुमति नहीं देता है जो इसे समवर्ती प्रदर्शन को प्रभावित करता है (जबकि समवर्ती हाशपा करता है)।
केविन Montrose

@KevinMontrose Ic, तो आपका मतलब था "यह भी संगामिति ट्यूनिंग का समर्थन नहीं करता है।"
पैशियर

17

क्रमबद्ध, नौगम्य और समवर्ती

डेटा संरचना की परिभाषा के लिए छोड़ें सूची देखें ।

इसकी कुंजी के प्राकृतिक क्रम में एक ConcurrentSkipListMapस्टोर Map(या आपके द्वारा परिभाषित कुछ अन्य महत्वपूर्ण आदेश)। तो यह धीमी होगा get/ put/ containsएक से संचालन HashMap, लेकिन इस की भरपाई के लिए यह समर्थन करता है SortedMap, NavigableMapऔर ConcurrentNavigableMapइंटरफेस।


8

प्रदर्शन के संदर्भ में, skipListजब मानचित्र के रूप में उपयोग किया जाता है - 10-20 गुना धीमा प्रतीत होता है। यहाँ मेरे परीक्षणों का परिणाम है (Java 1.8.0_102-b14, x32 जीतें)

Benchmark                    Mode  Cnt  Score    Error  Units
MyBenchmark.hasMap_get       avgt    5  0.015 ?  0.001   s/op
MyBenchmark.hashMap_put      avgt    5  0.029 ?  0.004   s/op
MyBenchmark.skipListMap_get  avgt    5  0.312 ?  0.014   s/op
MyBenchmark.skipList_put     avgt    5  0.351 ?  0.007   s/op

और इसके अतिरिक्त - उपयोग-मामला जहां एक-दूसरे की तुलना करना वास्तव में समझ में आता है। इन दोनों संग्रहों का उपयोग करके हाल ही में उपयोग की गई वस्तुओं के कैश का कार्यान्वयन। अब SkipList की दक्षता अधिक संदिग्ध होने लगती है।

MyBenchmark.hashMap_put1000_lru      avgt    5  0.032 ?  0.001   s/op
MyBenchmark.skipListMap_put1000_lru  avgt    5  3.332 ?  0.124   s/op

यहाँ JMH के लिए कोड है (के रूप में निष्पादित java -jar target/benchmarks.jar -bm avgt -f 1 -wi 5 -i 5 -t 1)

static final int nCycles = 50000;
static final int nRep = 10;
static final int dataSize = nCycles / 4;
static final List<String> data = new ArrayList<>(nCycles);
static final Map<String,String> hmap4get = new ConcurrentHashMap<>(3000, 0.5f, 10);
static final Map<String,String> smap4get = new ConcurrentSkipListMap<>();

static {
    // prepare data
    List<String> values = new ArrayList<>(dataSize);
    for( int i = 0; i < dataSize; i++ ) {
        values.add(UUID.randomUUID().toString());
    }
    // rehash data for all cycles
    for( int i = 0; i < nCycles; i++ ) {
        data.add(values.get((int)(Math.random() * dataSize)));
    }
    // rehash data for all cycles
    for( int i = 0; i < dataSize; i++ ) {
        String value = data.get((int)(Math.random() * dataSize));
        hmap4get.put(value, value);
        smap4get.put(value, value);
    }
}

@Benchmark
public void skipList_put() {
    for( int n = 0; n < nRep; n++ ) {
        Map<String,String> map = new ConcurrentSkipListMap<>();

        for( int i = 0; i < nCycles; i++ ) {
            String key = data.get(i);
            map.put(key, key);
        }
    }
}

@Benchmark
public void skipListMap_get() {
    for( int n = 0; n < nRep; n++ ) {
        for( int i = 0; i < nCycles; i++ ) {
            String key = data.get(i);
            smap4get.get(key);
        }
    }
}

@Benchmark
public void hashMap_put() {
    for( int n = 0; n < nRep; n++ ) {
        Map<String,String> map = new ConcurrentHashMap<>(3000, 0.5f, 10);

        for( int i = 0; i < nCycles; i++ ) {
            String key = data.get(i);
            map.put(key, key);
        }
    }
}

@Benchmark
public void hasMap_get() {
    for( int n = 0; n < nRep; n++ ) {
        for( int i = 0; i < nCycles; i++ ) {
            String key = data.get(i);
            hmap4get.get(key);
        }
    }
}

@Benchmark
public void skipListMap_put1000_lru() {
    int sizeLimit = 1000;

    for( int n = 0; n < nRep; n++ ) {
        ConcurrentSkipListMap<String,String> map = new ConcurrentSkipListMap<>();

        for( int i = 0; i < nCycles; i++ ) {
            String key = data.get(i);
            String oldValue = map.put(key, key);

            if( (oldValue == null) && map.size() > sizeLimit ) {
                // not real lru, but i care only about performance here
                map.remove(map.firstKey());
            }
        }
    }
}

@Benchmark
public void hashMap_put1000_lru() {
    int sizeLimit = 1000;
    Queue<String> lru = new ArrayBlockingQueue<>(sizeLimit + 50);

    for( int n = 0; n < nRep; n++ ) {
        Map<String,String> map = new ConcurrentHashMap<>(3000, 0.5f, 10);

        lru.clear();
        for( int i = 0; i < nCycles; i++ ) {
            String key = data.get(i);
            String oldValue = map.put(key, key);

            if( (oldValue == null) && lru.size() > sizeLimit ) {
                map.remove(lru.poll());
                lru.add(key);
            }
        }
    }
}

2
मुझे लगता है कि समवर्तीSkipListMap की तुलना उनके गैर-समवर्ती काउंटर भाग, TreeMap से की जानी चाहिए।
अब्बास

2
जैसा कि abbas ने टिप्पणी की है, प्रदर्शन को ConcurrentHashMap की तुलना करना मूर्खतापूर्ण लगता है। ConcurrentSkipListMap का उद्देश्य (a) संगामिति प्रदान करना है, और (b) कुंजियों के बीच क्रमबद्धता बनाए रखना है। समवर्ती हाशिए एक करता है, लेकिन ख नहीं। टेस्ला और डंप ट्रक की 0 से 60 की गति की तुलना करना बहुत ही बेकार है, क्योंकि वे विभिन्न उद्देश्यों की पूर्ति करते हैं।
बेसिल बोर्क

जब आप प्रदर्शन मेट्रिक्स नहीं जानते हैं, तो आप नहीं जानते कि कौन सा टेस्ला है और कौन सा "डंप ट्रक" है :) इसके अलावा ... आप मेट्रिक्स के बिना "बी" की कीमत नहीं जानते हैं। इसलिए - प्रदर्शन की तुलना आमतौर पर उपयोगी चीज है।
एक्स्ट्रा कोडर

1
शायद पेड़ के नक्शे से एक तुलना जोड़ें! : डी
सिमगिनेर

4

फिर मुझे समवर्ती SsipListMap का उपयोग कब करना चाहिए?

जब आपको (ए) कुंजी को क्रमबद्ध रखने की आवश्यकता होती है, और / या (बी) को एक नेविगेट करने योग्य नक्शे के पहले / अंतिम, सिर / पूंछ, और उपमैप सुविधाओं की आवश्यकता होती है।

ConcurrentHashMapवर्ग को लागू करता है ConcurrentMapइंटरफ़ेस, के रूप में करता है ConcurrentSkipListMap। लेकिन अगर आप भी के व्यवहार चाहते हैं SortedMapऔर NavigableMap, का उपयोग करेंConcurrentSkipListMap

ConcurrentHashMap

  • ❌ क्रमबद्ध
  • Able नौगम्य
  • ✅ समवर्ती

ConcurrentSkipListMap

  • ✅ क्रमबद्ध
  • Able नौगम्य
  • ✅ समवर्ती

यहां सारणी Map11 के साथ बंडल किए गए विभिन्न कार्यान्वयनों की प्रमुख विशेषताओं के माध्यम से तालिका आपको निर्देशित कर रही है । ज़ूम करने के लिए क्लिक / टैप करें।

जावा 11 में मानचित्र कार्यान्वयन की तालिका, उनकी विशेषताओं की तुलना करते हुए

ध्यान रखें कि आप अन्य Mapकार्यान्वयन प्राप्त कर सकते हैं , और इसी तरह की डेटा संरचनाएं, अन्य स्रोतों जैसे कि Google अमरूद से


ये तस्वीर कमाल की है। क्या आपके पास सामान्य या समवर्ती संग्रह के कुछ या सभी के लिए समान चित्र हैं?
ए वी

1
@ धन्यवाद धन्यवाद, उस चार्ट को बनाने में काफी काम आया। यह जावा उपयोगकर्ता समूहों के लिए मेरी प्रस्तुति का हिस्सा है: जावा मैप्स का एक नक्शा । और, नहीं, मैंने संबंधित कक्षाओं और इंटरफ़ेस के लिएString सिर्फ एक अन्य वर्ग आरेख बनाया है
बेसिल बोर्के

1

वर्कलोड के आधार पर समवर्ती SkipListMap रेंजफाइ की जरूरत पड़ने पर KAFKA-8802 में सिंक्रनाइज़ किए गए तरीकों के साथ TreeMap की तुलना में धीमा हो सकता है ।


साझा करने के लिए धन्यवाद। मैं अपने एक प्रोजेक्ट में ट्रीपार्ट को कॉन्ट्रासेंटसिप्लिस्टस्टैप से बदलने की सोच रहा हूं, इसलिए यह जानना अच्छा है! क्या आपके पास इस बारे में अधिक संदर्भ है कि समवर्तीSkipListMap धीमा क्यों है, और प्रदर्शन की तुलना के बारे में अधिक विवरण?
yusong

0

समवर्ती हाशिए: जब आप चाहते हैं कि मल्टीथ्रेडेड इंडेक्स आधारित मिल / पुट हो, तो केवल इंडेक्स आधारित संचालन समर्थित हैं। Get / Put O (1) के हैं

ConcurrentSkipListMap: बस द्वारा संचालित / पुट की तुलना में अधिक संचालन, जैसे कुंजी द्वारा छांटे गए टॉप / बॉटम एन आइटम, अंतिम प्रविष्टि प्राप्त करना, कुंजी द्वारा छाँटे गए / पूरा नक्शा प्राप्त करना आदि जटिलता O (लॉग (n)) का है, इसलिए प्रदर्शन नहीं किया जाता है के रूप में महान समवर्ती HashMap। यह SkipList के साथ ConcurrentNavigableMap का कार्यान्वयन नहीं है।

जब आप मानचित्र पर अधिक संचालन करना चाहते हैं तो समवर्तीSkipListMap का उपयोग संक्षेप में करने के लिए केवल साधारण प्राप्त करने और डालने के बजाय छंटनी की आवश्यकता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.