2009-04-30 10 views
8

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

हालांकि, मेरा मानना ​​है कि मुझे एहसास हुआ है कि कार्यान्वयन में कुछ प्रदर्शन प्रदर्शनकर्ता है। मुझे एहसास है कि एक्सटेंशन विधियों को वास्तव में प्रदर्शन को प्रभावित नहीं करना चाहिए, इसलिए मुझे लगता है कि कार्यान्वयन में कुछ कारण है ... डिस्पोजेबल structs की मात्रा बनाई/एकत्र की गई?

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

namespace LockPlay { 

    static class RWLSExtension { 
     struct Disposable : IDisposable { 
      readonly Action _action; 
      public Disposable(Action action) { 
       _action = action; 
      } 
      public void Dispose() { 
       _action(); 
      } 
     } // end struct 
     public static IDisposable ReadLock(this ReaderWriterLockSlim rwls) { 
      rwls.EnterReadLock(); 
      return new Disposable(rwls.ExitReadLock); 
     } 
     public static IDisposable UpgradableReadLock(this ReaderWriterLockSlim rwls) { 
      rwls.EnterUpgradeableReadLock(); 
      return new Disposable(rwls.ExitUpgradeableReadLock); 
     } 
     public static IDisposable WriteLock(this ReaderWriterLockSlim rwls) { 
      rwls.EnterWriteLock(); 
      return new Disposable(rwls.ExitWriteLock); 
     } 
    } // end class 

    class Program { 

     class MonitorList<T> : List<T>, IList<T> { 
      object _syncLock = new object(); 
      public MonitorList(IEnumerable<T> collection) : base(collection) { } 
      T IList<T>.this[int index] { 
       get { 
        lock(_syncLock) 
         return base[index]; 
       } 
       set { 
        lock(_syncLock) 
         base[index] = value; 
       } 
      } 
     } // end class 

     class RWLSList<T> : List<T>, IList<T> { 
      ReaderWriterLockSlim _rwls = new ReaderWriterLockSlim(); 
      public RWLSList(IEnumerable<T> collection) : base(collection) { } 
      T IList<T>.this[int index] { 
       get { 
        try { 
         _rwls.EnterReadLock(); 
         return base[index]; 
        } finally { 
         _rwls.ExitReadLock(); 
        } 
       } 
       set { 
        try { 
         _rwls.EnterWriteLock(); 
         base[index] = value; 
        } finally { 
         _rwls.ExitWriteLock(); 
        } 
       } 
      } 
     } // end class 

     class RWLSExtList<T> : List<T>, IList<T> { 
      ReaderWriterLockSlim _rwls = new ReaderWriterLockSlim(); 
      public RWLSExtList(IEnumerable<T> collection) : base(collection) { } 
      T IList<T>.this[int index] { 
       get { 
        using(_rwls.ReadLock()) 
         return base[index]; 
       } 
       set { 
        using(_rwls.WriteLock()) 
         base[index] = value; 
       } 
      } 
     } // end class 

     static void Main(string[] args) { 
      const int ITERATIONS = 100; 
      const int WORK = 10000; 
      const int WRITE_THREADS = 4; 
      const int READ_THREADS = WRITE_THREADS * 3; 

      // create data - first List is for comparison only... not thread safe 
      int[] copy = new int[WORK]; 
      IList<int>[] l = { new List<int>(copy), new MonitorList<int>(copy), new RWLSList<int>(copy), new RWLSExtList<int>(copy) }; 

      // test each list 
      Thread[] writeThreads = new Thread[WRITE_THREADS]; 
      Thread[] readThreads = new Thread[READ_THREADS]; 
      foreach(var list in l) { 
       Stopwatch sw = Stopwatch.StartNew(); 
       for(int k=0; k < ITERATIONS; k++) { 
        for(int i = 0; i < writeThreads.Length; i++) { 
         writeThreads[i] = new Thread(p => { 
          IList<int> il = p as IList<int>; 
          int c = il.Count; 
          for(int j = 0; j < c; j++) { 
           il[j] = j; 
          } 
         }); 
         writeThreads[i].Start(list); 
        } 
        for(int i = 0; i < readThreads.Length; i++) { 
         readThreads[i] = new Thread(p => { 
          IList<int> il = p as IList<int>; 
          int c = il.Count; 
          for(int j = 0; j < c; j++) { 
           int temp = il[j]; 
          } 
         }); 
         readThreads[i].Start(list); 
        } 
        for(int i = 0; i < readThreads.Length; i++) 
         readThreads[i].Join(); 
        for(int i = 0; i < writeThreads.Length; i++) 
         writeThreads[i].Join(); 
       }; 
       sw.Stop(); 
       Console.WriteLine("time: {0} class: {1}", sw.Elapsed, list.GetType()); 
      } 
      Console.WriteLine("DONE"); 
      Console.ReadLine(); 
     } 
    } // end class 
} // end namespace 

यहाँ एक विशिष्ट परिणाम है::

यहाँ कुछ परीक्षण कोड है

time: 00:00:03.0965242 class: System.Collections.Generic.List`1[System.Int32] 
time: 00:00:11.9194573 class: LockPlay.Program+MonitorList`1[System.Int32] 
time: 00:00:08.9510258 class: LockPlay.Program+RWLSList`1[System.Int32] 
time: 00:00:16.9888435 class: LockPlay.Program+RWLSExtList`1[System.Int32] 
DONE

आप देख सकते हैं, एक्सटेंशन का उपयोग वास्तव में प्रदर्शन सिर्फ lock (मॉनिटर) का उपयोग कर से भी बदतर बनाता है ।

उत्तर

9

ऐसा लगता है कि यह लाखों structs और invocations के अतिरिक्त बिट को तत्काल करने की कीमत की तरह दिखता है।

मैं कहूंगा कि इस नमूने में रीडरवाइटर लॉकस्लिम का दुरुपयोग किया जा रहा है, इस मामले में लॉक काफी अच्छा है और रीडरवाइटर लॉकस्लिम के साथ प्राप्त प्रदर्शन किनारे इन अवधारणाओं को समझाने की कीमत की तुलना में नगण्य है कनिष्ठ देवता

आपको विशाल पाठक लेखक शैली ताले के साथ लाभ मिलता है जब यह पढ़ने और लिखने के लिए एक गैर-नगण्य समय लेता है। जब आपके पास मुख्य रूप से पढ़ा जाने वाला सिस्टम होता है तो बूस्ट सबसे बड़ा होगा।

थ्रेड डालने का प्रयास करें। नींद (1) जबकि ताले को यह देखने के लिए अधिग्रहित किया जाता है कि यह कितना बड़ा अंतर बनाता है।

इस बेंचमार्क देखें:

 
Time for Test.SynchronizedList`1[System.Int32] Time Elapsed 12310 ms 
Time for Test.ReaderWriterLockedList`1[System.Int32] Time Elapsed 547 ms 
Time for Test.ManualReaderWriterLockedList`1[System.Int32] Time Elapsed 566 ms 

मेरी बेंच मार्किंग में मैं वास्तव में दो शैलियों के बीच बहुत अधिक अंतर नोटिस नहीं है, मैं का उपयोग कर सहज महसूस करते हैं यह बशर्ते वह था मामला लोगों में कुछ finalizer संरक्षण निपटान के लिए भूल जाते हैं ....

using System.Threading; 
using System.Diagnostics; 
using System.Collections.Generic; 
using System; 
using System.Linq; 

namespace Test { 

    static class RWLSExtension { 
     struct Disposable : IDisposable { 
      readonly Action _action; 
      public Disposable(Action action) { 
       _action = action; 
      } 
      public void Dispose() { 
       _action(); 
      } 
     } 

     public static IDisposable ReadLock(this ReaderWriterLockSlim rwls) { 
      rwls.EnterReadLock(); 
      return new Disposable(rwls.ExitReadLock); 
     } 
     public static IDisposable UpgradableReadLock(this ReaderWriterLockSlim rwls) { 
      rwls.EnterUpgradeableReadLock(); 
      return new Disposable(rwls.ExitUpgradeableReadLock); 
     } 
     public static IDisposable WriteLock(this ReaderWriterLockSlim rwls) { 
      rwls.EnterWriteLock(); 
      return new Disposable(rwls.ExitWriteLock); 
     } 
    } 

    class SlowList<T> { 

     List<T> baseList = new List<T>(); 

     public void AddRange(IEnumerable<T> items) { 
      baseList.AddRange(items); 
     } 

     public virtual T this[int index] { 
      get { 
       Thread.Sleep(1); 
       return baseList[index]; 
      } 
      set { 
       baseList[index] = value; 
       Thread.Sleep(1); 
      } 
     } 
    } 

    class SynchronizedList<T> : SlowList<T> { 

     object sync = new object(); 

     public override T this[int index] { 
      get { 
       lock (sync) { 
        return base[index]; 
       } 

      } 
      set { 
       lock (sync) { 
        base[index] = value; 
       } 
      } 
     } 
    } 


    class ManualReaderWriterLockedList<T> : SlowList<T> { 

     ReaderWriterLockSlim slimLock = new ReaderWriterLockSlim(); 

     public override T this[int index] { 
      get { 
       T item; 
       try { 
        slimLock.EnterReadLock(); 
        item = base[index]; 
       } finally { 
        slimLock.ExitReadLock(); 
       } 
       return item; 

      } 
      set { 
       try { 
        slimLock.EnterWriteLock(); 
        base[index] = value; 
       } finally { 
        slimLock.ExitWriteLock(); 
       } 
      } 
     } 
    } 

    class ReaderWriterLockedList<T> : SlowList<T> { 

     ReaderWriterLockSlim slimLock = new ReaderWriterLockSlim(); 

     public override T this[int index] { 
      get { 
       using (slimLock.ReadLock()) { 
        return base[index]; 
       } 
      } 
      set { 
       using (slimLock.WriteLock()) { 
        base[index] = value; 
       } 
      } 
     } 
    } 


    class Program { 


     private static void Repeat(int times, int asyncThreads, Action action) { 
      if (asyncThreads > 0) { 

       var threads = new List<Thread>(); 

       for (int i = 0; i < asyncThreads; i++) { 

        int iterations = times/asyncThreads; 
        if (i == 0) { 
         iterations += times % asyncThreads; 
        } 

        Thread thread = new Thread(new ThreadStart(() => Repeat(iterations, 0, action))); 
        thread.Start(); 
        threads.Add(thread); 
       } 

       foreach (var thread in threads) { 
        thread.Join(); 
       } 

      } else { 
       for (int i = 0; i < times; i++) { 
        action(); 
       } 
      } 
     } 

     static void TimeAction(string description, Action func) { 
      var watch = new Stopwatch(); 
      watch.Start(); 
      func(); 
      watch.Stop(); 
      Console.Write(description); 
      Console.WriteLine(" Time Elapsed {0} ms", watch.ElapsedMilliseconds); 
     } 

     static void Main(string[] args) { 

      int threadCount = 40; 
      int iterations = 200; 
      int readToWriteRatio = 60; 

      var baseList = Enumerable.Range(0, 10000).ToList(); 

      List<SlowList<int>> lists = new List<SlowList<int>>() { 
       new SynchronizedList<int>() , 
       new ReaderWriterLockedList<int>(), 
       new ManualReaderWriterLockedList<int>() 
      }; 

      foreach (var list in lists) { 
       list.AddRange(baseList); 
      } 


      foreach (var list in lists) { 
       TimeAction("Time for " + list.GetType().ToString(),() => 
       { 
        Repeat(iterations, threadCount,() => 
        { 
         list[100] = 99; 
         for (int i = 0; i < readToWriteRatio; i++) { 
          int ignore = list[i]; 
         } 
        }); 
       }); 
      } 



      Console.WriteLine("DONE"); 
      Console.ReadLine(); 
     } 
    } 
} 
+0

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

+0

@wekempf देखें: http://gist.github.com/104477 साइड इफेक्ट यह है कि डिस्पोजेबल को कम से कम DEBUG –

+0

@ sambo99 में काम करने के लिए एक वर्ग होना आवश्यक है: मैं वहां तर्क देख सकता हूं, लेकिन अभी भी नहीं इस बात से सहमत। सबसे अच्छा आप मृत लॉक को मृत लॉक में बदल दिया है + अंतिम कार्यवाही, बिना किसी अतिरिक्त लाभ के (यानी डीबग करना आसान नहीं है)। सब कुछ एक गलती के लिए जो मेरे लिए होने की संभावना नहीं है (विशेष रूप से यदि आप या तो विधियों के नाम बदलते हैं या उन्हें गैर-विस्तार विधियां बनाते हैं)। – wekempf

2

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

संपादित करें: @ 280Z28 की पोस्ट पुष्टि करता है कि यह कार्य प्रतिनिधियों के ढेर आवंटन है जो मंदी का कारण बन रहा है।

7

कोड ऑब्जेक्ट निर्माण ओवरहेड से बचने के लिए एक स्ट्रक्चर का उपयोग करने के लिए प्रतीत होता है, लेकिन यह हल्का वजन रखने के लिए अन्य आवश्यक कदम नहीं उठाता है। मेरा मानना ​​है कि यह ReadLock से रिटर्न वैल्यू बॉक्स करता है, और यदि संरचना के पूरे लाभ को अस्वीकार करता है। यह सभी मुद्दों को ठीक करना चाहिए और साथ ही साथ IDisposable इंटरफ़ेस के माध्यम से नहीं जाना चाहिए।

संपादित करें: बेंचमार्क ने मांग की।इन परिणामों को सामान्यीकृत किया गया है ताकि मैनुअल विधि (सुरक्षित Enter/ExitReadLock और Enter/ExitWriteLock संरक्षित कोड के साथ इनलाइन) का समय मूल्य 1.00 है। मूल विधि धीमी है क्योंकि यह ढेर पर ऑब्जेक्ट आवंटित करता है कि मैन्युअल विधि नहीं है। मैंने इस समस्या को ठीक किया है, और रिलीज मोड में भी विस्तार विधि कॉल ओवरहेड इसे मैन्युअल विधि के रूप में उतनी ही तेजी से छोड़कर चला जाता है।

डीबग बिल्ड:

Manual:    1.00 
Original Extensions: 1.62 
My Extensions:  1.24 

रिलीज बिल्ड:

Manual:    1.00 
Original Extensions: 1.51 
My Extensions:  1.00 

मेरे कोड:

internal static class RWLSExtension 
{ 
    public static ReadLockHelper ReadLock(this ReaderWriterLockSlim readerWriterLock) 
    { 
     return new ReadLockHelper(readerWriterLock); 
    } 

    public static UpgradeableReadLockHelper UpgradableReadLock(this ReaderWriterLockSlim readerWriterLock) 
    { 
     return new UpgradeableReadLockHelper(readerWriterLock); 
    } 

    public static WriteLockHelper WriteLock(this ReaderWriterLockSlim readerWriterLock) 
    { 
     return new WriteLockHelper(readerWriterLock); 
    } 

    public struct ReadLockHelper : IDisposable 
    { 
     private readonly ReaderWriterLockSlim readerWriterLock; 

     public ReadLockHelper(ReaderWriterLockSlim readerWriterLock) 
     { 
      readerWriterLock.EnterReadLock(); 
      this.readerWriterLock = readerWriterLock; 
     } 

     public void Dispose() 
     { 
      this.readerWriterLock.ExitReadLock(); 
     } 
    } 

    public struct UpgradeableReadLockHelper : IDisposable 
    { 
     private readonly ReaderWriterLockSlim readerWriterLock; 

     public UpgradeableReadLockHelper(ReaderWriterLockSlim readerWriterLock) 
     { 
      readerWriterLock.EnterUpgradeableReadLock(); 
      this.readerWriterLock = readerWriterLock; 
     } 

     public void Dispose() 
     { 
      this.readerWriterLock.ExitUpgradeableReadLock(); 
     } 
    } 

    public struct WriteLockHelper : IDisposable 
    { 
     private readonly ReaderWriterLockSlim readerWriterLock; 

     public WriteLockHelper(ReaderWriterLockSlim readerWriterLock) 
     { 
      readerWriterLock.EnterWriteLock(); 
      this.readerWriterLock = readerWriterLock; 
     } 

     public void Dispose() 
     { 
      this.readerWriterLock.ExitWriteLock(); 
     } 
    } 
} 
+1

आपने अपना कोड भी प्रोफाइल नहीं किया है, न ही आपने समझाया है कि इसकी धीमी गति –

+0

मेरा कोड कोई नया ढेर-आवंटित ऑब्जेक्ट नहीं बनाता है, इसलिए यदि समय एक छोटे बेंचमार्क पर समान है, तो यह विधि कम तनाव देती है मेमोरी मैनेजर बाद में। –

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