2011-08-04 12 views
5

में शब्दकोश को लॉक करने या पुनः पकड़ने के लिए अधिक कुशल है। मेरे पास एक सामान्य शब्दकोश है जिसे मैं थ्रेडेड .NET प्रोजेक्ट (सी #) में कैश के रूप में उपयोग कर रहा हूं। मैं शब्दकोश पर बहुत सारे पढ़ूंगा (संभावित रूप से सैकड़ों या अधिक प्रति सेकंड चोटी पर)।क्या यह थ्रेड (.NET)

मेरे पास एक साधारण गेट (इंट आईडी) विधि है जो कैश में होने पर कस्टम ऑब्जेक्ट को वापस करनी चाहिए, या अन्यथा शून्य।

मेरा प्रश्न: क्या यह शब्दकोश को लॉक करने के लिए तेज़/अधिक कुशल है, या बस कोशिश/पकड़ ब्लॉक का उपयोग करें?

शब्दकोश का मानना ​​है कि "dic" नामक चर में संग्रहीत किया जाता है।

ताला नमूना:

public CustomObject Get(int id) 
{ 
    lock(dic) 
    { 
     if (dic.ContainsKey(id)) 
     return dic[id]; 
    } 
    return null; 
} 

ट्राई/कैच नमूना:

public CustomObject Get(int id) 
{ 
    try 
    { 
     return dic[id]; 
    } 
    catch 
    { 
     return null; 
    } 
} 

उत्तर

8

मुझे लगता है कि आप अपने खुद के वातावरण में यह परीक्षण करना चाहिए। मूल रूप से:

  • लॉक सस्ती है
  • एक अपवाद हो रही बिना
  • कोशिश, सस्ते भी हो सकता है सस्ता तो ताला
  • प्रयास करें और अपवाद हो रही बहुत महंगा

तो अब सवाल है, कैसे है है अक्सर आप कैश-मिस होने की उम्मीद करते हैं, और इसलिए एक अपवाद फेंक दिया जाता है। मैं लॉक() के लिए जाऊंगा क्योंकि यह निष्पादन समय इस बात पर निर्भर नहीं है कि आप कैश-हिट प्राप्त करेंगे या नहीं, जिसका मतलब है कि यह अधिक अनुमानित और मापनीय है, जबकि अभी भी - बहुत सस्ता है। मुझे नहीं लगता कि प्रति सेकंड सैकड़ों हिट कोई समस्या होगी।

सरल परीक्षण मैंने संकेत दिया है कि कोशिश/पकड़ के साथ कैश-मिस प्राप्त करना बहुत महंगा है।

संपादित:

सरल परीक्षण से पता चलता है कि:

  • कोशिश नहीं फेंक 100k के लिए 2ms के बारे में लागत को पुन: प्राप्त
  • ताला 4seconds
  • के बारे में के बारे में 6ms
  • कोशिश फेंक लागत की लागत

जिसका मतलब है, लॉक() के लिए मिला है, क्योंकि यह अधिक कुशल है तो अगर आप कुछ और हजारों प्रयासों में 1 कैश मिस प्राप्त कर रहे हैं तो प्रयास करें/पकड़ें, और यह अधिक स्थिर है, किस्मत पर निर्भर नहीं है।

+0

बेंचमार्किंग के लिए +1। –

+0

इस परिदृश्य में प्रयास/पकड़ को मापने में कोई बात नहीं है क्योंकि यह थ्रेड सुरक्षित नहीं है। कृपया नीचे ब्रायन गिडियन प्रतिक्रिया देखें। – Dmitry

+0

** सिस्टम लॉक नहीं हैं! ** यह सब विवाद के स्तर पर निर्भर करता है, कोर की संख्या, सीपीयू को अन्य ऐप्स आदि द्वारा कितना लोड किया जाता है ** ** 6k ** में 100k किया जाता है? ऐसा लगता है कि लॉक सेक्शन में सिंगल थ्रेडेड एक्सेस की तरह दिखता है, लगभग 60 एनएस क्या होगा, लगभग 50 एनएस अनचाहे लॉक() लेता है, बाकी शब्दकोष का उपयोग होता है। आपका बेंचमार्क अनचाहे लॉक() पर किया जाता है और उस स्थिति में, लॉक को मापने की कोई आवश्यकता नहीं है। – ipavlu

4

मैं आपके परिदृश्य में lock/interlocked के साथ जाऊंगा। यदि आप लॉक नहीं करते हैं, तो आप दूषित/अमान्य डेटा प्राप्त कर सकते हैं, और कहीं और, शब्दकोश को कुछ लिखने का प्रयास करें।

यदि आप प्रदर्शन के बारे में चिंतित हैं तो आप Interlocked कक्षा का उपयोग कर सकते हैं ... lock से अधिक प्रदर्शन के साथ लॉकिंग प्राप्त करने के लिए इसका उपयोग करने के तरीके पर बहुत सारी तकनीकें हैं।

Interlocked.CompareExchange का उपयोग करके और एक नियंत्रण चर का उपयोग करके एक कार्यान्वयन संभव होगा।

Interlocked नमूना:

मैं Microsoft साइट में इस नमूने (सिर्फ इतना है कि आप इसे यहाँ देख सकते हैं की नकल की) पाया:

स्रोत: http://msdn.microsoft.com/en-us/library/system.threading.interlocked(v=VS.100).aspx#Y1291

 if(0 == Interlocked.Exchange(ref usingResource, 1)) 
     { 
      Console.WriteLine("{0} acquired the lock", Thread.CurrentThread.Name); 

      //Code to access a resource that is not thread safe would go here. 

      //Simulate some work 
      Thread.Sleep(500); 

      Console.WriteLine("{0} exiting lock", Thread.CurrentThread.Name); 

      //Release the lock 
      Interlocked.Exchange(ref usingResource, 0); 
      return true; 
     } 
     else 
     { 
      Console.WriteLine(" {0} was denied the lock", Thread.CurrentThread.Name); 
      return false; 
     } 

ध्यान दें कि, में यह नमूना, अगर ब्लॉक के अंदर लगभग सबकुछ बंद है!

परिवर्तनीय usingResource कक्षा का एक क्षेत्र है, विधि के संदर्भ में पारित किया जा रहा है ... यह नियंत्रण चर है जिसे मैंने पहले उल्लेख किया था।

6

आप आगे बढ़ सकते हैं और कोशिश-पकड़ विकल्प लिख सकते हैं। मुझे नहीं पता कि यह धीमा है या नहीं, लेकिन मुझे पता है कि अगर Dictionary अद्यतन करने वाला कोई अन्य धागा है तो यह हमेशा सही, सुसंगत और अनुमानित परिणाम नहीं देगा। समस्या यह है कि किसी बिंदु पर लेखक के पास आधे बेक्ड राज्य में Dictionary होगा और पाठकों को क्या दिखाई देगा, यह नहीं बताएगा। यह सिर्फ काम नहीं करेगा।

विकल्प 1: यदि .NET 4.0 आपके लिए उपलब्ध है तो मैं ConcurrentDictionary का उपयोग करूंगा।

विकल्प 2: यदि आप .NET 3.5 का उपयोग कर रहे हैं तो आप Reactive Extensions बैकपोर्ट डाउनलोड कर सकते हैं। ConcurrentDictionary System.Threading.dll में शामिल है।

विकल्प 3: एक और विचार Dictionary की दो अलग-अलग प्रतियां रखने के लिए है। एक का उपयोग केवल पढ़ने के लिए किया जाएगा जबकि दूसरा आधिकारिक प्रति के रूप में कार्य करेगा जो अद्यतन स्वीकार करता है। जब भी आप "आधिकारिक" Dictionary अपडेट करते हैं तो आप इसे क्लोन करेंगे और प्रतिलिपि के संदर्भ को ओवरराइट करेंगे।

public class Example 
{ 
    // This is the official version which can accept updates. 
    private readonly Dictionary<int, CustomObject> official = new Dictionary<int, CustomObject>(); 

    // This is a readonly copy. This must be marked as volatile for this to work correctly. 
    private volatile Dictionary<int, CustomObject> copy = new Dictionary<int, CustomObject>(); 

    public class Example() 
    { 
    } 

    public void Set(int id, CustomObject value) 
    { 
    lock (official) 
    { 
     // Update the official dictionary. 
     official[id] = value; 

     // Now create a clone of the official dictionary. 
     var clone = new Dictionary<int, CustomObject>(); 
     foreach (var kvp in official) 
     { 
     clone.Add(kvp.Key, kvp.Value); 
     } 

     // Swap out the reference. 
     copy = clone; 
    } 
    } 

    public CustomObject Get(int id) 
    { 
    // No lock is required here. 
    CustomObject value = null; 
    if (copy.TryGetValue(id, out value)) 
    { 
     return value; 
    } 
    return null; 
    } 

} 

यह विकल्प अच्छी तरह से काम नहीं करता है अगर वहाँ Dictionary में मदों की एक बहुत हैं या यदि आधिकारिक प्रति को बार-बार अपडेट होता है। लेकिन, यह एक चाल है जिसे मैं समय-समय पर उपयोग करता हूं।

विकल्प 4: सादे पुराने lock के साथ एक समान रूप से उचित दृष्टिकोण होगा।

+0

@ डिमिट्री: मैंने विकल्प # 5 के रूप में 'रीडरवाइटर लॉकस्लिम' को शामिल करने के लिए आपके संपादन को खारिज कर दिया। इसका कारण यह है कि यह एक सादे पुराने 'लॉक' की तुलना में ~ 2x धीमी है। आरडब्लूएलएस बेहतर काम करता है जब ऑपरेशन गार्ड लंबे होते हैं और बाहर निकलते हैं। अन्यथा, ओवरहेड को दूर करने के लिए पर्याप्त काम नहीं है। 'डिक्शनरी' को पढ़ना और अपडेट करना अपेक्षाकृत तेज़ ऑपरेशंस है, इसलिए सामान्य 'लॉक' * संभावित * तेज हो जाएगा और निश्चित रूप से अधिक कॉम्पैक्ट कोड उत्पन्न करेगा। हालांकि यह एक अच्छा विचार है :) –

+0

लॉक अधिग्रहण खुद को सरल लॉक से धीमा हो सकता है लेकिन यह एक बेहतर समवर्ती और थ्रूपुट की अनुमति देता है क्योंकि पाठक एक-दूसरे को अवरुद्ध नहीं करेंगे। सरल ताला धागे के मामले में पढ़ने के दौरान एक दूसरे के लिए इंतजार करेंगे। यह विशेष रूप से महत्वपूर्ण है क्योंकि सवाल "शब्दकोष पर बहुत सारे पढ़ने" का उल्लेख करता है। इसे निश्चित रूप से मापना है, लेकिन मुझे लगता है कि रीडरवाइटर लॉकस्लिम एक व्यवहार्य विकल्प है। – Dmitry

+0

@ प्रेषण: मुझे गलत मत समझो। आरडब्लूएलएस वास्तव में वास्तव में एक अच्छा विचार है। मैंने आज सुबह एक त्वरित परीक्षण किया और मुझे सिर्फ एक पुराने पुराने 'लॉक' की तुलना में किसी भी तेजी से काम करने के लिए आरडब्लूएलएस स्लिम भिन्नता नहीं मिली। मैंने इसे एक कोर मशीन और दोहरी कोर मशीन पर करने की कोशिश की। एक 'लॉक' काफी तेजी से समाप्त हो गया। दोबारा, ऐसा इसलिए है क्योंकि महत्वपूर्ण खंड में किया गया काम आरडब्लूएलएस ओवरहेड को पार करने के लिए पर्याप्त नहीं था। ध्यान दें, जब परीक्षण में लेखक अनुपात में 100 से 1 पाठक था। मुझे लगता है कि यह संभव है कि अगर मेरे पास 8-कोर या 16-कोर मशीन थी तो मैंने एक सुधार देखना शुरू कर दिया होगा ... सुनिश्चित नहीं है। –

1

यह हमेशा सुरक्षित और क्लीनर ताले साथ जाने के लिए है, लेकिन प्रदर्शन कितने धागे ताला

0

ताले & विवाद प्राप्त करने के लिए विवाद कर रहे हैं आधार पर भिन्न होगी => सस्ता नहीं !!

कृपया, सिस्टम लॉक सस्ता नहीं है, इसे प्रोसेसर के अंगूठी के स्तर के बीच स्विच करना है क्योंकि लॉक ऑपरेटिंग सिस्टम द्वारा प्रदान किया जाता है। उस सम्मान के लिए, लॉक() कम से कम 50ns लेता है अगर लॉक नहीं बदला जाता है! विवाद के साथ, यह बहुत बुरा हो जाता है।

लॉक() के साथ 6ms/100k पुनर्प्राप्ति जैसे बेंचमार्क अनचाहे लॉक() के साथ बेंचमार्किंग के लिए अधिक संगत हैं, जो लॉक पुनर्प्राप्ति के लिए 60ns प्रति पहुंच बनाता है। बहुआयामी और समकालिक डेटा संरचनाओं के लिए सिंक्रनाइज़ेशन प्राइमेटिव्स द्वारा संरक्षित, कई धागे से विवाद पैदा करना होगा, केवल तभी हम इसके व्यवहार के बारे में जानकारी प्राप्त कर सकते हैं।

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

मूल रूप से लॉक सभी बुराइयों की जड़ है यदि आप उच्च थ्रूपुट चाहते हैं, मल्टीकोर सीपीयू पर एकाधिक धागे के बीच डेटा संरचना के माध्यम से डेटा को धक्का देना या धागे के बीच उच्च प्रतिक्रिया।

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

यहां टीपीएल से सबसे अच्छा ConcurrentDictionary होगा, डॉटनेट 3.5 में (टीपीएल को न्यूजेट पर देखें) और नए संस्करण।

0

ब्रायन गिडियन के उत्तर से बेहतर वर्ग उदाहरण। कई बार लिखने वाले बोझ पढ़ने पर यह एक अच्छा विचार है। मैंने इसे सामान्य वर्ग बना दिया, अंतःस्थापित विधियों के साथ अस्थिर स्थानांतरित किया और पहली बार प्रारंभिक शुरुआत के दौरान एक संभावित दौड़ की स्थिति की मरम्मत की।

using System.Threading; 
using System.Collections.Generic; 


public class Example<TKeys, TValues> 
{ 
    private readonly object obj = new object(); 

    // Read/Write version. 
    private readonly Dictionary<TKeys, TValues> official = new Dictionary<TKeys, TValues>(); 

    // Read/Replace version. 
    //it must be initialized as new empty dictionary as well, 
    // they can not share reference or it won't be thread safe 
    //initially because Set be inserting data into official and copy 
    // in the same time. 
    private Dictionary<TKeys, TValues> copy = new Dictionary<TKeys, TValues>(); 

    public void Set(TKeys id, TValues value) 
    { 
     lock (obj) 
     { 
      // Update the official dictionary. 
      official[id] = value; 

      // Now create a clone of official dictionary 
      var clone = new Dictionary<int, CustomObject>(official); 

      // interlocked sets atomically latest reference 
      Interlocked.Exchange(ref copy, clone); 
     } 
    } 

    public bool TryGetValue(TKeys id, out TValues value) 
    { 
     // interlocked gets latest reference atomically, 
     //also providing access to full TryGetValue makes it 
     //safer for struct data types, if dictionary empty, 
     //it wont return default value! 
     return 
     Interlocked 
     .CompareExchange(ref copy, null, null) 
     .TryGetValue(id, ref value) 
     ; 
    } 

} 
संबंधित मुद्दे