कैश के रूप में समवर्ती

कई डेवलपर्स अक्सर एक दुविधा का सामना करते हैं - केवल डेटाबेस से डेटा प्राप्त करने या कई तालिकाओं के लिए कैश रखने के लिए। मूल रूप से, ये कुछ निर्देशिकाएं हैं जिनमें कुछ रिकॉर्ड होते हैं और लगातार हाथों पर इनकी आवश्यकता होती है। यह एक हॉलीवुड मुद्दा है और इस लेख में संबोधित नहीं किया जाएगा।

.NET पर ट्रांसपोर्ट मॉनिटरिंग सिस्टम का अत्यधिक लोडेड सर्वर डिजाइन करते समय मेरे सामने भी यही समस्या आई। नतीजतन, यह तय किया गया कि कैश होना चाहिए। डिक्शनरी कैश को कॉन्ट्रैक्डरबेल पर रैपर में संग्रहीत किया जाने लगा। यह विकल्प बहुत अधिक शोध के बिना लिया गया था, क्योंकि यह थ्रेड-सुरक्षित शब्दकोशों के लिए मानक .NET उपकरण है। अब इस समाधान के प्रदर्शन का परीक्षण करने का समय है। इस बारे में, वास्तव में, एक लेख। इसके अलावा लेख के अंत में थोड़ा अनुसंधान होगा कि यह कैसे समवर्ती छाया के अंदर व्यवस्थित किया गया है।



समस्या का बयान

कुंजी-मूल्य जोड़े का एक थ्रेड-सुरक्षित संग्रह आवश्यक है, जो निम्न कार्य कर सकता है:

  1. एक पहचानकर्ता कुंजी के साथ एक वस्तु का अनुरोध करना - बेशक, सबसे अधिक बार उपयोग किया जाता है;
  2. नए तत्वों को बदलना, हटाना और जोड़ना दुर्लभ है, लेकिन धागा सुरक्षा सुनिश्चित की जानी चाहिए;
  3. मूल्यों के साथ काम करें - सभी मूल्यों का अनुरोध करें, मूल्य ऑब्जेक्ट के गुणों द्वारा खोजें।

क्लॉज 3 शब्दकोश संग्रह के लिए विशिष्ट नहीं है और इसलिए इस निर्माण पर मुख्य ब्रेक है। हालाँकि, यदि लुकअप टेबल का उपयोग किया जाता है, तो ऐसी स्थितियाँ अपरिहार्य हैं (उदाहरण के लिए, संपादन के लिए व्यवस्थापक पैनल में संपूर्ण शब्दकोश प्रदर्शित करना तुच्छ होगा)।

आइए उन विभिन्न प्रणालियों पर विचार करने का प्रयास करें जिनमें कैश का उपयोग किया जाएगा। वे शब्दकोश के साथ संचालन की आवृत्ति में भिन्न होंगे। हम इन कार्यों के प्रकारों को औपचारिक रूप देते हैं:



टेस्ट प्रतिभागियों की सूची

  1. समवर्ती । यह एकीकृत थ्रेड सुरक्षा के साथ Microsoft से टर्नकी समाधान है। कई सुविधाजनक तरीके TryGetValue, TryAdd, TryUpdate, AddOrUpdate, TryDelete, जो आपको सुविधाजनक रूप से एक विरोध समाधान नीति सेट करने की अनुमति देते हैं। लेख के अंत में कार्यान्वयन सुविधाओं पर चर्चा की जाएगी।
  2. मॉनिटर के माध्यम से लॉकिंग के साथ शब्दकोश । सबसे प्रमुख समाधान यह है कि सभी गैर-थ्रेड-सुरक्षित संचालन लॉक निर्माण में लिपटे हुए हैं।
  3. ReaderWriterLock के माध्यम से लॉक के साथ शब्दकोश । पिछले समाधान का अनुकूलन - संचालन को पढ़ने और लिखने के कार्यों में विभाजित किया जाता है। तदनुसार, कई धाराएं एक ही समय में पढ़ सकती हैं, और रिकॉर्डिंग के लिए अनन्य पहुंच की आवश्यकता होती है।
  4. ReaderWriterLockSlim के माध्यम से लॉक के साथ शब्दकोश । अनिवार्य रूप से समान है, लेकिन एक नए वर्ग (अतिरिक्त पुनरावर्तन नियंत्रण सेटिंग्स) का उपयोग कर रहा है। इस कार्य के संदर्भ में, मुझे शायद ही ReaderWriterLock के अलावा कुछ और दिखाना है।
  5. Wintellect PowerThreading लाइब्रेरी से OneManyResourceLocker के माध्यम से लॉकिंग के साथ शब्दकोश - जेफरी रिक्टर द्वारा ReaderWriterLock का एक ट्रिकी कार्यान्वयन। मैं स्पष्ट करूंगा कि आधिकारिक साइट से संस्करण का उपयोग किया गया था, न कि न्यूगेट पैकेज का - वहां संस्करण अलग है और मुझे यह पसंद नहीं आया।
  6. हैशटेबल.संतुलित । Microsoft से तैयार समाधान भी - एक थ्रेड-सुरक्षित इंडेक्स प्रदान करता है। इस तथ्य के कारण उपयोग करना असुविधाजनक है कि यह एक गैर-जेनेरिक संग्रह (बॉक्सिंग, बदतर पठनीयता) है, और कोशिश उपसर्ग के साथ कोई विधियां नहीं हैं (एक ही समय में जोड़ने / अपडेट करने के लिए एक नीति निर्धारित करना असंभव है)।


संक्षेप में आपको बताएंगे कि कैसे संचालकों को लागू किया जाता है।

  1. GET ऑपरेशन : सभी प्रतिभागी IDfox इंटरफ़ेस से थ्रेड-सेफ TryGetValue विधि का उपयोग करते हैं। हैशटेबल के लिए, एक टाइप-कोडेड इंडेक्सर का उपयोग किया जाता है।
  2. जोड़ें ऑपरेशन: समवर्ती के लिए - AddOrUpdate, शब्दकोश के लिए - लॉक लिखें और इंडेक्सर के माध्यम से जोड़ें, हैशटेबल के लिए - बिना लॉक के इंडेक्सर के माध्यम से जोड़ें।
  3. अद्यतन ऑपरेशन: समवर्ती के लिए, पहले TryGetValue, फिर TryUpdate।
    यह विधि दिलचस्प है कि इन दो तरीकों के बीच एक समानांतर अद्यतन हो सकता है (जो परीक्षण के दौरान भी प्रकट हुआ था)। यह इस मामले के लिए है कि OldValue TryUpdate को पास किया जाता है, ताकि इस दुर्लभ मामले में, फिर से लिखना विफल हो जाए। शब्दकोश के लिए, हम ContainsKey के माध्यम से उपलब्धता की जांच करते हैं और, यदि सफल हो, तो लेखन लॉक सेट करें और मूल्य को ओवरराइट करें। हैशटेबल के लिए कोई सुविधाजनक ट्राइअपडेट नहीं है, इसलिए मैंने कुंजी की उपस्थिति के लिए जाँच के साथ परेशान नहीं किया और, जैसा कि जोड़ने के मामले में, मूल्य सूचकांक के माध्यम से ओवरराइट किया जाता है (इस संग्रह के लिए, यह महत्वपूर्ण नहीं है - यह अभी भी वैसे भी बहुत बुरा है)।
  4. ऑपरेशन खोज : समवर्ती छाया के लिए, LINQ का FirstOrDefault उपयोग किया जाता है, बाकी के लिए, उसी FirstOrDefault के लिए एक रीड लॉक का उपयोग किया जाता है।


टेस्ट स्टैंड

परीक्षण के लिए, एक कंसोल एप्लिकेशन बनाया गया था ( लिंक )।
  1. हैंडलर का एक सेट बनाया गया है जो सभी निश्चित प्रकारों के संचालन को संभाल सकता है;
  2. एन तत्वों का एक शब्दकोश बनाया गया है (डिफ़ॉल्ट रूप से 10,000);
  3. एम की मात्रा में विभिन्न प्रकार के कार्यों का एक संग्रह बनाया गया है (डिफ़ॉल्ट रूप से - 10,000);
  4. प्रत्येक हैंडलर उत्पन्न शब्दकोश (सभी हैंडलर के लिए सामान्य) में सभी कार्यों का समानांतर प्रसंस्करण करता है;
  5. प्रयोग (अंक 2-4) कई बार (डिफ़ॉल्ट रूप से - 10) किया जाता है और प्राप्त समय औसत हो जाता है। मशीन पर एक कोर 2 क्वाड 2.66 गीगाहर्ट्ज और 8 जीबी मेमोरी के साथ माप लिया गया था।

डिफ़ॉल्ट मान काफी छोटे हैं, हालांकि, यदि आप उन्हें बढ़ाते हैं, तो मूल रूप से कुछ भी नहीं बदलता है।

परीक्षण के परिणाम

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



निष्कर्ष

सभी प्रतिभागियों का प्रदर्शन मूल्य से रीडिंग की संख्या के साथ रैखिक रूप से घटता है, चाहे लेखन संचालन की संख्या की परवाह किए बिना।

  1. समवर्ती । प्रयोगों से पता चला है कि यह उपकरण इस कार्य के लिए सबसे उपयुक्त है। मूल्य से पढ़ना प्रदर्शन को काफी प्रभावित करता है, लेकिन यह अभी भी अन्य प्रतिभागियों की तुलना में तेज है।
  2. शब्दकोश + मॉनिटर । महत्वपूर्ण रूप से धीमा, अपेक्षित परिणाम।
  3. शब्दकोश + ReaderWriterLock । पिछले संस्करण का अनुकूलन भी सभी अपेक्षित है।
    यह ध्यान दिया जाना चाहिए कि अधिक रिकॉर्डिंग संचालन प्रबल होता है, छोटा अंतर। एक निश्चित बिंदु से, ब्लॉकिंग प्रक्रिया के लिए कम ओवरहेड के कारण मॉनिटर और भी बेहतर हो जाता है।
  4. शब्दकोश + ReaderWriterLockSlim । किसी कारण से मैं एक साधारण मॉनिटर से भी हारने में कामयाब रहा। या तो बढ़ी हुई कार्यक्षमता (पिछले संस्करण की तुलना में) ने प्रदर्शन को प्रभावित किया, या मुझे नहीं पता कि इसे कैसे पकाना है।
  5. शब्दकोश + OneManyResourceLock । लगता है रिक्टर ने अपने पढ़ने / लिखने के ताले से सब कुछ निचोड़ लिया है। परीक्षण के परिणामों के अनुसार, यह डिक्शनरी का सबसे तेज उपयोग है। लेकिन समवर्ती अभी भी तेज है।
  6. हैशटेबल । अपेक्षित असफलता। शायद मैंने इसे गलत तरीके से इस्तेमाल किया था, लेकिन मुझे नहीं लगता है कि अन्य प्रतिभागियों के लिए एक परिणाम प्राप्त करना संभव था। वैसे भी, यह गैर-जेनेरिक संग्रह के साथ काम करने के लिए किसी तरह निराशाजनक था।


आंतरिक उपकरण समवर्ती

चलो विजेता पर करीब से नज़र डालें, अर्थात्: समवर्ती छाया के स्रोतों को देखें।

इस वर्ग को बनाते समय, 2 पैरामीटर सेट किए जाते हैं: क्षमता और समरूपता । पहला ( क्षमता ) संग्रह से परिचित है और उन तत्वों की संख्या निर्धारित करता है जिन्हें आंतरिक संग्रह का विस्तार किए बिना दर्ज किया जा सकता है। इस मामले में, लिंक की गई सूचियां बनाई जाती हैं (m_buckets, इसलिए हम उन्हें बास्केट कहेंगे (अच्छी तरह से, हेलमेट नहीं, सही?)) क्षमता की मात्रा में, और फिर तत्वों को उनके लिए समान रूप से जोड़ा जाता है। डिफ़ॉल्ट मान 31 है।

दूसरा पैरामीटर ( ConsurrencyLevel ) उन थ्रेड की संख्या निर्धारित करता है जो एक ही समय में हमारे शब्दकोश में लिख सकते हैं। यह मॉनिटर द्वारा लॉकिंग के लिए ऑब्जेक्ट्स का संग्रह बनाकर प्राप्त किया जाता है। प्रत्येक ऐसी अवरोधक वस्तु लगभग एक ही संख्या की टोकरियों के लिए जिम्मेदार है। डिफ़ॉल्ट मान Environment.ProcessorCount * 4 है।

इस प्रकार, शब्दकोश में प्रत्येक वस्तु टोकरी के साथ विशिष्ट रूप से जुड़ी होती है, जहां यह झूठ होता है और लेखन के लिए लॉक ऑब्जेक्ट होता है। यह निम्नलिखित विधि द्वारा किया जाता है:

/// <summary> /// Computes the bucket and lock number for a particular key. /// </summary> private void GetBucketAndLockNo( int hashcode, out int bucketNo, out int lockNo, int bucketCount, int lockCount) { bucketNo = (hashcode & 0x7fffffff) % bucketCount; lockNo = bucketNo % lockCount; Assert(bucketNo >= 0 && bucketNo < bucketCount); Assert(lockNo >= 0 && lockNo < lockCount); } 


उत्सुकता से, यहां तक ​​कि ConcurrencyLevel = 1 के साथ, समवर्ती छाया एक नियमित लॉक शब्दकोश की तुलना में अभी भी तेज है। यह भी ध्यान देने योग्य है कि कक्षा एक इट्रेटर के माध्यम से उपयोग के लिए अनुकूलित है (जैसा कि परीक्षण दिखाया गया है)। विशेष रूप से, जब ToArray () विधि को कॉल किया जाता है, तो सभी बास्केटों पर लॉकिंग का प्रदर्शन किया जाता है, और इट्रेटर का उपयोग अपेक्षाकृत रूप से किया जाता है।

उदाहरण के लिए: डिक्शनरी का उपयोग करना बेहतर है। सेलेक्ट (x => x.Value) ।ToArray () से शब्दकोश ।Values.ToArray ()।

लेख कंपनी के एक प्रमुख डेवलपर द्वारा लिखा गया था।
आपका ध्यान के लिए धन्यवाद!

Source: https://habr.com/ru/post/In156125/


All Articles