LIRS算法的近似实现

  1. 从LIRS算法的描述来看,可以理解为两个LRU队列的组合,利用cold缓冲区来保护Hot缓冲区,提高了进入hot缓冲区的门槛,阻止hot缓冲区频繁地变化。为了验证LIRS算法的有效性,笔者自行开发了LRU算法及LIRS算法的c#实现。  
  2. 首先说明LRU算法的实现。  
  3. 采用带表头的单链表来存储数据节点,head指针指向表头,tail指针指向链表最后一个节点。数据节点则记录了每个缓存块的ID,加载状态,数据指针,next指针。另有unUsedSize变量存放未分配的缓存块数量。  
  4. 当用户访问命中时,将对应的数据节点移到队列尾部。当用户访问未命中时,若能分配新数据节点,则分配,否则从表头摘下一个数据节点,并卸载数据。更新得到的数据节点的信息,并加到队列尾部。  
  5. 最后,为了避免在查找数据节点时遍历整个链表,增加了Dictionary<ID,数据节点>的字典,利用其hash查找功能提高查找效率。  
  6. 再说明LIRS算法的实现。  
  7. 为了简化实现,将算法实现为多个逻辑LRU队列(可实现多级缓冲区),在物理实现上,仍采用带表头的单链表来存储数据节点。对于N级缓冲区的实现(原始的LIRS算法为2级),使用unUsedSize[N]数组存放每个缓冲区的未分配数量,使用head[N+1]存放缓冲区的头尾指针。这样head[0]、head[1]指示了第0块缓冲区的头尾,依次类推,head[N-1]、head[N]指示了第N-1块缓冲区的头尾。0号缓冲区是cold的缓冲区,N-1号则是最hot的。由于使用了多级缓冲区,数据节点也需要增加一个选择器selector,用于指示该节点属于哪个缓冲区。  
  8. 当用户访问未命中,分配新节点时,先检查N-1号缓冲区是否能分配新节点,若不能则依次向前检查。若分配了新节点,则直接加入相应缓冲区的尾部,否则就从0级缓冲区头部摘下一个节点来,更新节点信息后加入0级缓冲区尾部。  
  9. 当用户访问命中时,则将x级的节点提升到x+1级,并加到缓冲区尾部,x+1级的头部节点,则被移到x级缓冲区的尾部  
  10. 同样的,增加了Dictionary<ID,数据节点>字典,提高查找节点的效率。  
  1. using System;  
  2. using System.Linq;  
  3. using System.Diagnostics;  
  4. using System.Collections;  
  5. using System.Collections.Generic;  
  6. //using System.Collections.Concurrent;  
  7. using WaveTang.Classes;  
  8. public interface ICache<T,U>{  
  9.     //T=资源类型,U=资源ID类型  
  10.     T this[U id]{get;} //向cache申请使用资源,参数:id=资源ID,返回:资源  
  11.     Func<U,T> Loader{set;}//回调函数 要求访问者加载资源  
  12.     Action<U> UnLoader{set;}//回调函数 要求访问者卸载资源  
  13. }  
  14. [System.Security.Permissions.HostProtectionAttribute(Synchronization=true, ExternalThreading=true)]  
  15. public class LRUCache<T,U>:ICache<T,U>,IDisposable  where U:IComparable<U>{  
  16.     Int32 _unUsedSize;//可用资源数量(缓冲区的块数)  
  17.     Node _head,_tail;//使用带表头的单向链表,_head指向表头,_tail指向表尾,新节点在表尾插入,老节点从表头处摘除,允许将某一个节点移动到表尾  
  18.     Dictionary<U,Node> _dict;//维护U与Node之间的对照,以提供更高速的Find功能.(ID与Node的前一个节点对应)  
  19.       
  20.     ////////test使用  
  21.     public Int32 _accessed=0,_missed=0;  
  22.   
  23.     ////////////////  
  24.     //构造函数  
  25.     //参数 size : cache拥有的资源数  
  26.     //     load : 根据资源id加载资源的delegate  
  27.     //     unLoad : 卸载资源的delegate  
  28.     public LRUCache(Int32 size,Func<U,T> loader,Action<U> unLoader){  
  29.         _unUsedSize=size;  
  30.         _tail=_head=new Node(default(U)); //建立表头节点,使用带表头的单向链表存放数据  
  31.         Loader=loader;  
  32.         UnLoader=unLoader;  
  33.         _dict=new Dictionary<U,Node>();  
  34.     }  
  35.       
  36.     //根据id查找对应的节点,返回目标节点的前一个节点  
  37.     protected Node Find(U id){  
  38.         //Node p=_head;  
  39.         //for(;p.Next!=null&&(p.Next.ID.CompareTo(id)!=0);p=p.Next);  
  40.   
  41.         //改用dictionary查找表,提高查询效率  
  42.         return (_dict.ContainsKey(id))?(_dict[id]):_tail;  
  43.     }  
  44.     //在队列尾部加入节点  
  45.     protected Node Append(Node n){  
  46.         _dict.Add(n.ID,_tail);  
  47.         return _tail=_tail.InsertAfter(n);  
  48.     }  
  49.     //移除一个节点 (n.Next)  
  50.     protected Node Remove(Node n){  
  51.         if(n.Next==_tail)  
  52.             _tail=n;  
  53.         if(n.Next.Next!=null)  
  54.             _dict[n.Next.Next.ID]=n;  
  55.         _dict.Remove(n.Next.ID);  
  56.         return n.RemoveAfter();  
  57.     }  
  58.     //将指定节点的Next移动到队列尾部  
  59.     protected Node SendBack(Node n){  
  60.         if(n.Next!=_tail){  
  61.             return Append(Remove(n));//摘除节点 加到队列尾部  
  62.         }else  
  63.             return _tail;  
  64.     }  
  65.   
  66.     //获得一个新的资源。  
  67.     protected Node CreateResource(U id){  
  68.         if(_unUsedSize>0){  
  69.             //有未使用的缓冲区,直接创建管理节点即可  
  70.             --_unUsedSize;  
  71.             return new Node(id);  
  72.         }else{  
  73.             //从表头摘下一个节点来使用。  
  74.             Node p=Remove(_head).UnLoadResource(UnLoader);  
  75.             p.ID=id;  
  76.             return p;  
  77.         }  
  78.     }  
  79.     protected String Show(Node n){  
  80.         String s=String.Empty;  
  81.         for(;n!=null;n=n.Next){  
  82.             s+=','+n.ID.ToString();  
  83.         }  
  84.         return s;  
  85.     }  
  86.     ~LRUCache(){  
  87.         Dispose();  
  88.     }  
  89.       
  90.     ////////////////////////////  
  91.     //实现ICache  
  92.     public T this[U id]{  
  93.         get{  
  94.             lock(_head){  
  95.                 ++_accessed;  
  96.                 //Console.WriteLine("+++"+Show(_head));  
  97.                 Node p=Find(id);//查找队列中是否有该资源  
  98.                 if(p.Next==null){  
  99.                     ++_missed;  
  100.                     p=Append(CreateResource(id));//无,则创建资源,并加入队列尾部  
  101.                 }else  
  102.                     p=SendBack(p);//有,将资源移到队列尾部  
  103.                 p.LoadResource(Loader);//加载资源(若已经加载,则不做任何动作)  
  104.                 //Console.WriteLine("---"+Show(_head));  
  105.                 return p.Value;  
  106.             }  
  107.         }  
  108.     }  
  109.     public Func<U,T> Loader{protected get;set;}//回调函数 要求访问者加载资源  
  110.     public Action<U> UnLoader{protected get;set;}//回调函数 要求访问者卸载资源  
  111.     //public event CacheEventHandler<T,U> Load;  
  112.     //public event CacheEventHandler<T,U> UnLoad;  
  113.   
  114.     ////////////////////////////  
  115.     //实现IDisposable  
  116.     public void Dispose(){  
  117.         lock(_head){  
  118.             //卸载cache中的资源  
  119.             for(Node p=_head.Next;p!=null;p=p.Next)  
  120.                 if(p.Loaded)  
  121.                     p.UnLoadResource(UnLoader);  
  122.             _head.Next=null;  
  123.         }  
  124.     }  
  125.       
  126.     ////////////////////////////  
  127.     //节点定义  
  128.     public class Node{  
  129.         public Node(U id){  
  130.             Value=default(T);  
  131.             ID=id;  
  132.             Next=null;  
  133.             Loaded=false;  
  134.         }  
  135.         public T Value{get;set;}  
  136.         public U ID{get;set;}  
  137.         public Node Next{get;set;}  
  138.         public Boolean Loaded{get;set;}  
  139.         public Node InsertAfter(Node n){  
  140.             n.Next=this.Next;  
  141.             this.Next=n;  
  142.             return n;  
  143.         }  
  144.         public Node RemoveAfter(){  
  145.             Node n=this.Next;  
  146.             if(n!=null){  
  147.                 this.Next=n.Next;  
  148.                 n.Next=null;  
  149.             }  
  150.             return n;  
  151.         }  
  152.         //加载资源  
  153.         public Node LoadResource(Func<U,T> load){  
  154.             if(!Loaded){  
  155.                 Value=load(ID);  
  156.                 Loaded=true;  
  157.             }  
  158.             return this;  
  159.               
  160.         }  
  161.         //卸载资源  
  162.         public Node UnLoadResource(Action<U> unLoad){  
  163.             //Console.WriteLine("==="+Show(_head));  
  164.             if(Loaded){  
  165.                 unLoad(ID);  
  166.                 Value=default(T);  
  167.                 Loaded=false;  
  168.             }  
  169.             return this;  
  170.         }  
  171.     }  
  172. }  
  1. public class LIRSCache<T,U>:ICache<T,U>,IDisposable where U:IComparable<U>{  
  2.     Int32 _grade;//缓冲区级数  
  3.     Int32[] _unUsedSize;//可用资源数量(缓冲区的块数),_unUsedSize[0]存放cold数据,_unUsedSize[1]存放hot数据  
  4.     Node[] _head;//使用带表头的单向链表,_head[0]指向表头,_head[1]指向hot区的前一个节点(cold区的最后一个节点),_head[2]指向表尾  
  5.     Dictionary<U,Node> _dict;//维护U与Node之间的对照,以提供更高速的Find功能.(ID与Node的前一个节点对应)  
  6.     ////////test使用  
  7.     public Int32 _accessed=0,_missed=0;  
  8.       
  9.     ////////////////  
  10.     //构造函数  
  11.     //参数 size : cache拥有的资源数  
  12.     //     load : 根据资源id加载资源的delegate  
  13.     //     unLoad : 卸载资源的delegate  
  14.     public LIRSCache(Int32 grade,Int32[] size,Func<U,T> loader,Action<U> unLoader){  
  15.         _grade=grade;  
  16.         //设置每级缓冲的大小  
  17.         _unUsedSize=new Int32[_grade];  
  18.         for(Int32 i=0;i<_grade;++i)  
  19.             _unUsedSize[i]=size[i];  
  20.         //初始化缓冲指针//建立表头节点,使用带表头的单向链表存放数据  
  21.         _head=new Node[_grade+1];  
  22.         _head[_grade]=new Node(default(U));   
  23.         for(Int32 i=0;i<_grade;++i)  
  24.             _head[i]=_head[_grade];  
  25.         Loader=loader;  
  26.         UnLoader=unLoader;  
  27.         _dict=new Dictionary<U,Node>();  
  28.     }  
  29.       
  30.     protected Node Find(U id){  
  31.         /*foreach(var i in _dict) 
  32.             Console.WriteLine("{0}->{1}",i.Value.ID,i.Key);*/  
  33.         /*Node p=_head[0]; 
  34.         for(;p.Next!=null&&(p.Next.ID.CompareTo(id)!=0);p=p.Next); 
  35.          
  36.         Node q=(_dict.ContainsKey(id))?(_dict[id]):_head[_grade]; 
  37.         if(p!=q)throw new Exception("Find Error"); 
  38.         return q;*/  
  39.         //改用dictionary查找表,提高查询效率  
  40.         return (_dict.ContainsKey(id))?(_dict[id]):_head[_grade];  
  41.     }  
  42.   
  43.     //在队列尾部加入节点  
  44.     protected Node Append(Int32 selector,Node n){  
  45.         _dict.Add(n.ID,_head[selector+1]);  
  46.         if(selector+1<_grade)  
  47.             _dict[_head[selector+1].Next.ID]=n;  
  48.         n.Selector=selector;  
  49.         return _head[selector+1]=_head[selector+1].InsertAfter(n);  
  50.     }  
  51.     //移除一个节点 (n.Next)  
  52.     protected Node Remove(Int32 selector,Node n){  
  53.         if(n.Next==_head[selector+1])  
  54.             _head[selector+1]=n;  
  55.         if(n.Next.Next!=null)  
  56.             _dict[n.Next.Next.ID]=n;  
  57.         _dict.Remove(n.Next.ID);  
  58.         return n.RemoveAfter();  
  59.     }  
  60.     //将指定节点的Next移动到队列尾部  
  61.     protected Node SendBack(Int32 selector,Node n){  
  62.         if(n.Next!=_head[selector+1])  
  63.             return Append(selector,Remove(selector,n));//摘除节点 加到队列尾部  
  64.         else  
  65.             return _head[selector+1];  
  66.     }  
  67.     //获得一个新的资源。  
  68.     protected Node CreateResource(U id){  
  69.         //有未使用的缓冲区,直接创建管理节点即可  
  70.         for(Int32 i=_grade-1;i>=0;--i){  
  71.             if(_unUsedSize[i]>0){  
  72.                 --_unUsedSize[i];  
  73.                 Node n=new Node(id);  
  74.                 n.Selector=i; //记录新节点的级数  
  75.                 return n;  
  76.             }  
  77.         }  
  78.         //从表头摘下一个节点来使用。  
  79.         Node p=Remove(0,_head[0]).UnLoadResource(UnLoader);  
  80.         p.ID=id;  
  81.         p.Selector=0;//指定为0级节点  
  82.         return p;  
  83.     }  
  84.     protected String Show(Node n){  
  85.         String s=String.Empty;  
  86.         for(;n!=null;n=n.Next){  
  87.             s+=','+n.ID.ToString();  
  88.         }  
  89.         return s;  
  90.     }  
  91.     ~LIRSCache(){  
  92.         Dispose();  
  93.     }  
  94.       
  95.     ////////////////////////////  
  96.     //实现ICache  
  97.     public T this[U id]{  
  98.         get{  
  99.             //return default(T);  
  100.             lock(_head){  
  101.                 ++_accessed;  
  102.                 //Console.WriteLine(id);  
  103.                 Node p=Find(id);  
  104.                 Int32 selector;  
  105.                 if(p.Next==null){  
  106.                     //无,则创建资源,并加入队列尾部  
  107.                     ++_missed;  
  108.                     p=CreateResource(id);  
  109.                     //队列未满,将节点加入指定级缓冲的尾部  
  110.                     //替换下来的节点,加入0级队列的尾部  
  111.                     Append(p.Selector,p);  
  112.                 }else{  
  113.                     //有,若已经到最后一级,则移动到尾部,否则将资源升级到下一级的头部  
  114.                     selector=p.Next.Selector;  
  115.                     if(selector+1<_grade){  
  116.                         p=Remove(selector,p); // 移除节点  
  117.                         _head[selector+1]=_head[selector+1].Next;//移动selector+1级的头指针,使原先的头节点成为selector级的尾部  
  118.                         _head[selector+1].Selector=selector;//指定节点的新级别  
  119.                         Append(selector+1,p);//在selector+1级尾部添加节点  
  120.                     }else{  
  121.                         p=SendBack(selector,p);  
  122.                     }  
  123.                 }  
  124.                 p.LoadResource(Loader);//加载资源(若已经加载,则不做任何动作)  
  125.                 /*Console.WriteLine("0:"+Show(_head[0])); 
  126.                 Console.WriteLine("1:"+Show(_head[1])); 
  127.                 Console.WriteLine("2:"+Show(_head[2]));*/  
  128.                 return p.Value;  
  129.             }  
  130.         }  
  131.     }  
  132.     public Func<U,T> Loader{protected get;set;}//回调函数 要求访问者加载资源  
  133.     public Action<U> UnLoader{protected get;set;}//回调函数 要求访问者卸载资源  
  134.     //public event CacheEventHandler<T,U> Load;  
  135.     //public event CacheEventHandler<T,U> UnLoad;  
  136.   
  137.     ////////////////////////////  
  138.     //实现IDisposable  
  139.     public void Dispose(){  
  140.     }  
  141.       
  142.     public class Node{  
  143.         public Node(U id){  
  144.             Value=default(T);  
  145.             ID=id;  
  146.             Next=null;  
  147.             Loaded=false;  
  148.             Selector=0;  
  149.         }  
  150.         public T Value{get;set;}  
  151.         public U ID{get;set;}  
  152.         public Int32 Selector{get;set;}  
  153.         public Node Next{get;set;}  
  154.         public Boolean Loaded{get;set;}  
  155.           
  156.         public Node InsertAfter(Node n){  
  157.             n.Next=this.Next;  
  158.             this.Next=n;  
  159.             return n;  
  160.         }  
  161.         public Node RemoveAfter(){  
  162.             Node n=this.Next;  
  163.             if(n!=null){  
  164.                 this.Next=n.Next;  
  165.                 n.Next=null;  
  166.             }  
  167.             return n;  
  168.         }  
  169.         //加载资源  
  170.         public Node LoadResource(Func<U,T> load){  
  171.             if(!Loaded){  
  172.                 Value=load(ID);  
  173.                 Loaded=true;  
  174.             }  
  175.             return this;  
  176.               
  177.         }  
  178.         //卸载资源  
  179.         public Node UnLoadResource(Action<U> unLoad){  
  180.             //Console.WriteLine("==="+Show(_head));  
  181.             if(Loaded){  
  182.                 unLoad(ID);  
  183.                 Value=default(T);  
  184.                 Loaded=false;  
  185.             }  
  186.             return this;  
  187.         }  
  188.     }  
  189. }  
  190. public class Test{  
  191.     public static void Main(){  
  192.         List<Int32> list=GetRandomGaussian(1000000);  
  193.         Stopwatch  sw=new Stopwatch ();  
  194.         sw.Reset();  
  195.         sw.Start();  
  196.         TestLRUCache(list);  
  197.         sw.Stop();  
  198.         Console.WriteLine(sw.ElapsedTicks);  
  199.         sw.Reset();  
  200.         sw.Start();  
  201.         TestLIRSCache(list);  
  202.         sw.Stop();  
  203.         Console.WriteLine(sw.ElapsedTicks);  
  204.         sw.Reset();  
  205.         sw.Start();  
  206.         TestLIRSCache2(list);  
  207.         sw.Stop();  
  208.         Console.WriteLine(sw.ElapsedTicks);  
  209.         sw.Reset();  
  210.         sw.Start();  
  211.         TestLIRSCache3(list);  
  212.         sw.Stop();  
  213.         Console.WriteLine(sw.ElapsedTicks);  
  214.     }  
  215.     public static List<Int32> GetRandomList(Int32 len){  
  216.         List<Int32> list=new List<Int32>();  
  217.         for(Int32 i=0;i<len;i++)  
  218.             list.Add(i%1001);  
  219.         return list;  
  220.     }  
  221.     public static List<Int32> GetRandomAvg(Int32 len){  
  222.         Random r=new Random();  
  223.         List<Int32> list=new List<Int32>();  
  224.         for(Int32 i=0;i<len;i++)  
  225.             list.Add(r.Next(2000));  
  226.         return list;  
  227.     }  
  228.     public static List<Int32> GetRandomGaussian(Int32 len){  
  229.         GaussianRandom r=new GaussianRandom();  
  230.         List<Int32> list=new List<Int32>();  
  231.         for(Int32 i=0;i<len;i++)  
  232.             list.Add(r.Next(500,250));  
  233.         return list;  
  234.     }  
  235.     public static void TestLIRSCache(List<Int32> r){  
  236.         LIRSCache<String,Int32> cache =new LIRSCache<String,Int32>(2,new Int32[]{100,900},id=>{return "="+id.ToString()+'=';},id=>{});  
  237.         Dictionary<Int32,Int32> dict=new Dictionary<Int32,Int32>();  
  238.         Int32 k;  
  239.         foreach(Int32 j in r){  
  240.             if(dict.ContainsKey(j))dict[j]++;else dict.Add(j,1);  
  241.             //Console.WriteLine(cache[j]);  
  242.             k=cache[j].Length;  
  243.         }  
  244.         Console.WriteLine("LIRS1 : 1-{0}/{1}={2}",cache._missed,cache._accessed,1.0-1.0*cache._missed/cache._accessed);  
  245.         //dict.OrderBy(d=>d.Key).ForEach(k=>{Console.WriteLine("{0},{1}",k.Key,k.Value);});  
  246.           
  247.     }  
  248.     public static void TestLIRSCache2(List<Int32> r){  
  249.         LIRSCache<String,Int32> cache =new LIRSCache<String,Int32>(3,new Int32[]{10,100,890},id=>{return "="+id.ToString()+'=';},id=>{});  
  250.         Dictionary<Int32,Int32> dict=new Dictionary<Int32,Int32>();  
  251.         Int32 k;  
  252.         foreach(Int32 j in r){  
  253.             if(dict.ContainsKey(j))dict[j]++;else dict.Add(j,1);  
  254.             //Console.WriteLine(cache[j]);  
  255.             k=cache[j].Length;  
  256.         }  
  257.         Console.WriteLine("LIRS2 : 1-{0}/{1}={2}",cache._missed,cache._accessed,1.0-1.0*cache._missed/cache._accessed);  
  258.         //dict.OrderBy(d=>d.Key).ForEach(k=>{Console.WriteLine("{0},{1}",k.Key,k.Value);});  
  259.           
  260.     }  
  261.     public static void TestLIRSCache3(List<Int32> r){  
  262.         LIRSCache<String,Int32> cache =new LIRSCache<String,Int32>(2,new Int32[]{10,990},id=>{return "="+id.ToString()+'=';},id=>{});  
  263.         Dictionary<Int32,Int32> dict=new Dictionary<Int32,Int32>();  
  264.         Int32 k;  
  265.         foreach(Int32 j in r){  
  266.             if(dict.ContainsKey(j))dict[j]++;else dict.Add(j,1);  
  267.             //Console.WriteLine(cache[j]);  
  268.             k=cache[j].Length;  
  269.         }  
  270.         Console.WriteLine("LIRS3 : 1-{0}/{1}={2}",cache._missed,cache._accessed,1.0-1.0*cache._missed/cache._accessed);  
  271.         //dict.OrderBy(d=>d.Key).ForEach(k=>{Console.WriteLine("{0},{1}",k.Key,k.Value);});  
  272.           
  273.     }  
  274.     public static void TestLRUCache(List<Int32> r){  
  275.         LRUCache<String,Int32> cache =new LRUCache<String,Int32>(200,id=>{return "="+id.ToString()+'=';},id=>{});  
  276.         Dictionary<Int32,Int32> dict=new Dictionary<Int32,Int32>();  
  277.         Int32 k;  
  278.         foreach(Int32 j in r){  
  279.             if(dict.ContainsKey(j))dict[j]++;else dict.Add(j,1);  
  280.             //Console.WriteLine(cache[j]);  
  281.             k=cache[j].Length;  
  282.         }  
  283.         Console.WriteLine("LRU   : 1-{0}/{1}={2}",cache._missed,cache._accessed,1.0-1.0*cache._missed/cache._accessed);  
  284.         //dict.OrderBy(d=>d.Key).ForEach(k=>{Console.WriteLine("{0},{1}",k.Key,k.Value);});  
  285.           
  286.     }  


你可能感兴趣的:(LIRS算法的近似实现)