乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      蛙蛙推薦: LRU緩存的實(shí)現(xiàn)算法討論...

       Dawnxu 2009-07-23

      業(yè)務(wù)模型

      讀、寫(xiě)、刪的比例大致是7:3:1,至少要支持500w條緩存,平均每條緩存6k,要求設(shè)計(jì)一套性能比較好的緩存算法。
      算法分析

      不考慮MemCached,Velocity等現(xiàn)成的key-value緩存方案,也不考慮脫離.net gc自己管理內(nèi)存,不考慮隨機(jī)讀取數(shù)據(jù)及順序讀取數(shù)據(jù)的場(chǎng)景,目前想到的有如下幾種LRU方案

      算法

      分析

      SortedDictionary

      .net自帶的,內(nèi)部用二叉搜索樹(shù)(應(yīng)該不是普通樹(shù),至少是做過(guò)優(yōu)化的樹(shù))實(shí)現(xiàn)的,檢索為O(log n),比普通的DictionayO(1))慢一點(diǎn)。
      插入和刪除都是O(log n),而且插入和刪除,會(huì)實(shí)時(shí)排序。
      但是.net 2.0的這個(gè)類(lèi)沒(méi)有First屬性

      Dictionary + PriorityQueue

      Dictionay可以保證檢索是O(1);
      優(yōu)先隊(duì)列可以保證插入和刪除都為O(log n);
      但是優(yōu)先隊(duì)列刪除指定的項(xiàng)不支持(至少我找到的優(yōu)先隊(duì)列不支持),所以在刪除緩存的時(shí)候不知道咋實(shí)現(xiàn)

      Dictionay + Binary heap

      二叉堆也是優(yōu)先隊(duì)列,分析應(yīng)該同上,我沒(méi)有詳細(xì)評(píng)估。

      b樹(shù)

      查找,刪除,插入效率都很好,數(shù)據(jù)庫(kù)都用它,但實(shí)現(xiàn)復(fù)雜,寫(xiě)一個(gè)沒(méi)有BUGB樹(shù)幾乎不可能。有人提到stl:map是自頂向下的紅黑樹(shù),查找,刪除,插入都是O(log n),但咱不懂c++,沒(méi)做詳細(xì)測(cè)試。

      Dictionay + List

      Dict用來(lái)檢索;
      List用來(lái)排序;
      檢索、添加、刪除都沒(méi)問(wèn)題,只有在清空的時(shí)候需要執(zhí)行List的排序方法,這時(shí)候緩存條目比較多的話,可能比較慢。

      Dictionay + LinkedList

      Dict用來(lái)檢索;
      LinkedList的添加和刪除都是O(1),添加緩存時(shí)在鏈表頭加節(jié)點(diǎn),獲取緩存時(shí)把特定節(jié)點(diǎn)移動(dòng)(先刪除特定節(jié)點(diǎn)(O(n)),再到頭部添加節(jié)點(diǎn)(O(1)))到頭,緩存滿地時(shí)候截?cái)嗟粑膊康囊恍┕?jié)點(diǎn)。

      目前幾種方案在多線程下應(yīng)該都需要加鎖,不太好設(shè)計(jì)無(wú)鎖的方案,下面這個(gè)鏈接是一個(gè)支持多線程的方案,但原理至今沒(méi)搞特明白
      A High Performance Multi-Threaded LRU Cache
      http://www./KB/recipes/LRUCache.aspx

      用普通鏈表簡(jiǎn)單實(shí)現(xiàn)LRU緩存
      以下是最后一種方案的簡(jiǎn)單實(shí)現(xiàn),大家討論下這個(gè)方案值不值得優(yōu)化,或者其它的哪個(gè)方案比較合適
       

      public class LRUCacheHelper<K, V> {
          
      readonly Dictionary<K, V> _dict;
          
      readonly LinkedList<K> _queue = new LinkedList<K>();
          
      readonly object _syncRoot = new object();
          
      private readonly int _max;
          
      public LRUCacheHelper(int capacity, int max) {
              _dict 
      = new Dictionary<K, V>(capacity);
              _max 
      = max;
          }
       
          
      public void Add(K key, V value) {
              
      lock (_syncRoot) {
                  checkAndTruncate();
                  _queue.AddFirst(key);   
      //O(1)
                  _dict[key] = value;     //O(1)
              }
          }
       
          
      private void checkAndTruncate() {
              
      lock (_syncRoot) {
                  
      int count = _dict.Count;                        //O(1)
                  if (count >= _max) {
                      
      int needRemoveCount = count / 10;
                      
      for (int i = 0; i < needRemoveCount; i++) {
                          _dict.Remove(_queue.Last.Value);        
      //O(1)
                          _queue.RemoveLast();                    //O(1)
                      }
                  }
              }
          }
       
          
      public void Delete(K key) {
              
      lock (_syncRoot) {
                  _dict.Remove(key); 
      //(1)
                  _queue.Remove(key); // O(n)
              }
          }
          
      public V Get(K key) {
              
      lock (_syncRoot) {
                  V ret;
                  _dict.TryGetValue(key, 
      out ret);    //O(1)
                  _queue.Remove(key);                 //O(n)
                  _queue.AddFirst(key);               //(1)
                  return ret;
              }
          }
      }


      用雙頭鏈表代替普通鏈表

      突然想起來(lái)了,可以把鏈表?yè)Q成雙頭鏈表,然后在字典里保存鏈表節(jié)點(diǎn),在Get方法的時(shí)候直接從字典里獲取到要移動(dòng)的節(jié)點(diǎn),然后把這個(gè)節(jié)點(diǎn)的上一個(gè)節(jié)點(diǎn)的Next指針指向給下一個(gè)節(jié)點(diǎn),下一個(gè)節(jié)點(diǎn)的Previous指針指向上一個(gè)節(jié)點(diǎn),這樣就把移動(dòng)節(jié)點(diǎn)的操作簡(jiǎn)化成O(1)了,提高了緩存讀取的效率。

      _dict.TryGetValue(key, out ret);    //O(1)
      ret.Next.Previous = ret.Previous     //O(1)
      ret. Previous.Next. = ret.Next         //O(1)
        _queue.AddFirst(key);                      //O(1)

      我改進(jìn)后的鏈表就差不多滿足需求了,

      操作

      基本操作

      復(fù)雜度

      讀取

      Dict.Get

      Queue.Move

      O 1

      O 1

      刪除

      Dict.Remove

      Queue.Remove

      O 1

      O 1

      增加

      Dict.Add

      Queue.AddFirst

      O 1

      O 1

      截?cái)?/span>

      Dict.Remove

      Queue.RemoveLast

      O k

      O k

      K表示截?cái)嗑彺嬖氐膫€(gè)數(shù)

       

      其中截?cái)嗟臅r(shí)候可以指定當(dāng)緩存滿的時(shí)候截?cái)喟俜种嗌俚淖钌偈褂玫木彺骓?xiàng)。

      其它的就是多線程的時(shí)候鎖再看看怎么優(yōu)化,字典有線程安全的版本,就把.net 3.0的讀寫(xiě)鎖扣出來(lái)再把普通的泛型字典保證成ThreadSafelyDictionay就行了,性能應(yīng)該挺好的。

      鏈表的話不太好用讀寫(xiě)鎖來(lái)做線程同步,大不了用互斥鎖,但得考慮下鎖的粒度,Move,AddFirst,RemoveLast的時(shí)候只操作一兩個(gè)節(jié)點(diǎn),是不是想辦法只lock這幾個(gè)節(jié)點(diǎn)就行了,Truncate的時(shí)候因?yàn)橐坎僮骱芏喙?jié)點(diǎn),所以要上個(gè)大多鏈表鎖,但這時(shí)候怎么讓其它操作停止得考慮考慮,類(lèi)似數(shù)據(jù)庫(kù)的表鎖和行鎖。

      實(shí)現(xiàn)代碼

       

      public class DoubleLinkedListNode<T> {
          
      public T Value { getset; }
       
          
      public DoubleLinkedListNode<T> Next { getset; }
       
          
      public DoubleLinkedListNode<T> Prior { getset; }
       
          
      public DoubleLinkedListNode(T t) { Value = t; }
       
          
      public DoubleLinkedListNode() { }
       
          
      public void RemoveSelf() {
              Prior.Next 
      = Next;
              Next.Prior 
      = Prior;
          }
       
      }
      public class DoubleLinkedList<T> {
          
      protected DoubleLinkedListNode<T> m_Head;
          
      private DoubleLinkedListNode<T> m_Tail;
       
          
      public DoubleLinkedList() {
              m_Head 
      = new DoubleLinkedListNode<T>();
              m_Tail 
      = m_Head;
          }
       
          
      public DoubleLinkedList(T t)
              : 
      this() {
              m_Head.Next 
      = new DoubleLinkedListNode<T>(t);
              m_Tail 
      = m_Head.Next;
              m_Tail.Prior 
      = m_Head;
          }
       
          
      public DoubleLinkedListNode<T> Tail {
              
      get { return m_Tail; }
          }
       
          
      public DoubleLinkedListNode<T> AddHead(T t) {
              DoubleLinkedListNode
      <T> insertNode = new DoubleLinkedListNode<T>(t);
              DoubleLinkedListNode
      <T> currentNode = m_Head;
              insertNode.Prior 
      = null;
              insertNode.Next 
      = currentNode;
              currentNode.Prior 
      = insertNode;
              m_Head 
      = insertNode;
              
      return insertNode;
          }
          
      public void RemoveTail() {
              m_Tail 
      = m_Tail.Prior;
              m_Tail.Next 
      = null;
              
      return;
          }
      }
      public class LRUCacheHelper<K, V> {
          
      class DictItem {
              
      public DoubleLinkedListNode<K> Node { getset; }
              
      public V Value { getset; }
          }
          
      readonly Dictionary<K, DictItem> _dict;
          
      readonly DoubleLinkedList<K> _queue = new DoubleLinkedList<K>();
          
      readonly object _syncRoot = new object();
          
      private readonly int _max;
          
      public LRUCacheHelper(int capacity, int max) {
              _dict 
      = new Dictionary<K, DictItem>(capacity);
              _max 
      = max;
          }
       
          
      public void Add(K key, V value) {
              
      lock (this)
              {
       
                  checkAndTruncate();
                  DoubleLinkedListNode
      <K> v = _queue.AddHead(key);   //O(1)
                  _dict[key] = new DictItem() { Node = v, Value = value }; //O(1)
              }
          }
       
          
      private void checkAndTruncate() {
              
      int count = _dict.Count;                        //O(1)
              if (count >= _max) {
                  
      int needRemoveCount = count / 10;
                  
      for (int i = 0; i < needRemoveCount; i++) {
                      _dict.Remove(_queue.Tail.Value);        
      //O(1)
                      _queue.RemoveTail();                    //O(1)
                  }
              }
          }
       
          
      public void Delete(K key) {
              
      lock (this) {
                  _dict[key].Node.RemoveSelf();
                  _dict.Remove(key); 
      //(1) 
              }
          }
          
      public V Get(K key) {
              
      lock (this) {
                  DictItem ret;
                  
      if (_dict.TryGetValue(key, out ret)) {
                      ret.Node.RemoveSelf();
                      _queue.AddHead(key);
                      
      return ret.Value;
                  }
                  
      return default(V); 
              }
          }
      }


      性能測(cè)試

      用雙頭鏈表測(cè)試了一下,感覺(jué)性能還可以接受,每秒鐘讀取可達(dá)80w,每秒鐘寫(xiě)操作越20w。

      程序初始化200w條緩存,然后不斷的加,每加到500w,截?cái)嗟?/span>10分之一,然后繼續(xù)加。

      測(cè)試模型中每秒鐘的讀和寫(xiě)的比例是7:3,以下是依次在3個(gè)時(shí)間點(diǎn)截取的性能計(jì)數(shù)器圖。
      圖1

       
      圖2

       

       
      圖3

       


      內(nèi)存最高會(huì)達(dá)到
      1gcpu也平均百分之90以上,但測(cè)試到后期會(huì)發(fā)現(xiàn)每隔一段時(shí)間,就會(huì)有一兩秒,吞吐量為0,如最后一張截圖,后來(lái)觀察發(fā)現(xiàn),停頓的那一兩秒是二代內(nèi)存在回收,等不停頓的時(shí)候# gen 2 collections就會(huì)加1,這個(gè)原因應(yīng)該是鏈表引起的,對(duì)鏈表中節(jié)點(diǎn)的添加和刪除是很耗費(fèi)GC的,因?yàn)闀?huì)頻繁的創(chuàng)建和銷(xiāo)毀對(duì)象。 

      后續(xù)改進(jìn)

      1、 用游標(biāo)鏈表來(lái)代替普通的雙頭鏈表,程序起來(lái)就收工分配固定大小的數(shù)組,然后用數(shù)組的索引來(lái)做鏈表,省得每次添加和刪除節(jié)點(diǎn)都要GC的參與,這相當(dāng)于手工管理內(nèi)存了,但目前我還沒(méi)找到c#合適的實(shí)現(xiàn)。

      2、 有人說(shuō)鏈表不適合用在多線程環(huán)境中,因?yàn)閷?duì)鏈表的每個(gè)操作都要加互斥鎖,連讀寫(xiě)鎖都用不上,我目前的實(shí)現(xiàn)是直接用互斥鎖做的線程同步,每秒的吞吐量七八十萬(wàn),感覺(jué)lock也不是瓶頸,如果要改進(jìn)的話可以把DictionaryThreadSafelyDictionary來(lái)代替,然后鏈表還用互斥鎖(剛開(kāi)始設(shè)想的鏈表操作只鎖要操作的幾個(gè)節(jié)點(diǎn)以降低并發(fā)沖突的想法應(yīng)該不可取,不嚴(yán)謹(jǐn))。

      3、 還有一個(gè)地方就是把鎖細(xì)分以下,鏈表還用鏈表,但每個(gè)鏈表的節(jié)點(diǎn)是個(gè)HashSet,對(duì)HashSet的操作如果只有讀,寫(xiě),刪,沒(méi)有遍歷的話應(yīng)該不需要做線程同步(我感覺(jué)不用,因?yàn)?/span>Set就是一個(gè)集合,一個(gè)線程往里插入,一個(gè)線程往里刪除,一個(gè)線程讀取應(yīng)該沒(méi)問(wèn)題,頂多讀進(jìn)來(lái)的數(shù)據(jù)可能馬上就刪除了,而整個(gè)Set的結(jié)構(gòu)不會(huì)破壞)。然后新增數(shù)據(jù)的時(shí)候往鏈表頭頂Set里插入,讀取某個(gè)數(shù)據(jù)的時(shí)候把它所在的節(jié)點(diǎn)的Set里刪除該數(shù)據(jù),然后再鏈表頭的Set里插入一個(gè)數(shù)據(jù),這樣反復(fù)操作后,鏈表的最后一個(gè)節(jié)點(diǎn)的Set里的數(shù)據(jù)都是舊數(shù)據(jù)了,可以安全的刪除了,當(dāng)然這個(gè)刪除的時(shí)候應(yīng)該是要鎖整個(gè)鏈表的。每個(gè)Set應(yīng)該有個(gè)大小上限,比如20w,但set不能安全的遍歷,就不能得到當(dāng)前大小,所以添加、刪除Set的數(shù)據(jù)的時(shí)候應(yīng)該用Interlocked.Decrement() Interlocked.Increment()維護(hù)一個(gè)Count,一遍一個(gè)Set滿的時(shí)候,再到鏈表的頭新增一個(gè)Set節(jié)點(diǎn)。

      性能測(cè)試腳本

       

      class Program {
          
      private static PerformanceCounter _addCounter;
          
      private static PerformanceCounter _getCounter;
       
          
      static void Main(string[] args) {
              SetupCategory();
              _addCounter 
      = new PerformanceCounter("wawasoft.lrucache""add/sec"false);
              _getCounter 
      = new PerformanceCounter("wawasoft.lrucache""get/sec"false);
              _addCounter.RawValue 
      = 0;
              _getCounter.RawValue 
      = 0;
       
              Random rnd 
      = new Random();
              
      const int max = 500 * 10000;
       
       
              LRUCacheHelper
      <intint> cache = new LRUCacheHelper<intint>(200 * 10000, max);
       
              
      for (int i = 10000*100000 - 1; i >= 0; i--)
              {
                  
      if(i % 10 > 7)
                  {
                      ThreadPool.QueueUserWorkItem(
                          
      delegate
                              {
                                  cache.Add(rnd.Next(
      010000), 0);
                                  _addCounter.Increment(); 
                              });
                  }
                  
      else
                  {
                      ThreadPool.QueueUserWorkItem(
                         
      delegate
                         {
                             
      int pop = cache.Get(i);
                             _getCounter.Increment();
                         });
                  }
              }
              Console.ReadKey();
          }
       
          
      private static void SetupCategory() {
              
      if (!PerformanceCounterCategory.Exists("wawasoft.lrucache")) {
       
                  CounterCreationDataCollection CCDC 
      = new CounterCreationDataCollection();
       
                  CounterCreationData addcounter 
      = new CounterCreationData();
                  addcounter.CounterType 
      = PerformanceCounterType.RateOfCountsPerSecond32;
                  addcounter.CounterName 
      = "add/sec";
                  CCDC.Add(addcounter);
       
       
                  CounterCreationData getcounter 
      = new CounterCreationData();
                  getcounter.CounterType 
      = PerformanceCounterType.RateOfCountsPerSecond32;
                  getcounter.CounterName 
      = "get/sec";
                  CCDC.Add(getcounter);
       
                  PerformanceCounterCategory.Create(
      "wawasoft.lrucache","lrucache",CCDC);
       
              }
          }
       
      }


      參考鏈接

      不分先后,都是隨時(shí)從網(wǎng)上找的,大多看不懂
      潛心學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)-C#語(yǔ)言描述系列文章
      http://space.cnblogs.com/group/topic/6922/
      《C++數(shù)據(jù)結(jié)構(gòu)原理與經(jīng)典問(wèn)題求解》
      http://www./itbook/bookinfodetail.asp?lbbh=10087298&sort=ml
      數(shù)據(jù)結(jié)構(gòu)(C#):循環(huán)鏈表
      http://www.cnblogs.com/zxjay/archive/2008/12/07/1349688.html
      表的游標(biāo)實(shí)現(xiàn)
      http://www.comp./~xujia/mirror/algorithm.myrice.com/datastructure/basic/list/chapter3_3.htm
      我所理解的鏈表1
      http://www./course/3_program/c/csuanfa/2007213/21570.html
      cursor implementation of linked list
      http://wiki./Q/Explain_the_Cursor_implementation_of_linked_list
      Sorting Algorithms In C#
      http://www./KB/recipes/cssorters.aspx
      The C5 Generic Collection Library
      http://www./research/c5/
      當(dāng)弱引用對(duì)象成為集合元素時(shí)
      http://www./post/Coding/Weakreference-Collection-CSharp.html
      QuickSort in Functional C#
      http://blogs./cs/blogs/jlee/archive/2008/05/23/quicksort-in-functional-c.aspx
      LRU頁(yè)面置換算法模擬
      http://dev.csdn.net/article/73207.shtm

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類(lèi)似文章 更多