LIRS算法的近似实现

算法 专栏收录该内容
4 篇文章 0 订阅
从LIRS算法的描述来看,可以理解为两个LRU队列的组合,利用cold缓冲区来保护Hot缓冲区,提高了进入hot缓冲区的门槛,阻止hot缓冲区频繁地变化。为了验证LIRS算法的有效性,笔者自行开发了LRU算法及LIRS算法的c#实现。
首先说明LRU算法的实现。
采用带表头的单链表来存储数据节点,head指针指向表头,tail指针指向链表最后一个节点。数据节点则记录了每个缓存块的ID,加载状态,数据指针,next指针。另有unUsedSize变量存放未分配的缓存块数量。
当用户访问命中时,将对应的数据节点移到队列尾部。当用户访问未命中时,若能分配新数据节点,则分配,否则从表头摘下一个数据节点,并卸载数据。更新得到的数据节点的信息,并加到队列尾部。
最后,为了避免在查找数据节点时遍历整个链表,增加了Dictionary<ID,数据节点>的字典,利用其hash查找功能提高查找效率。
再说明LIRS算法的实现。
为了简化实现,将算法实现为多个逻辑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,用于指示该节点属于哪个缓冲区。
当用户访问未命中,分配新节点时,先检查N-1号缓冲区是否能分配新节点,若不能则依次向前检查。若分配了新节点,则直接加入相应缓冲区的尾部,否则就从0级缓冲区头部摘下一个节点来,更新节点信息后加入0级缓冲区尾部。
当用户访问命中时,则将x级的节点提升到x+1级,并加到缓冲区尾部,x+1级的头部节点,则被移到x级缓冲区的尾部
同样的,增加了Dictionary<ID,数据节点>字典,提高查找节点的效率。

using System;
using System.Linq;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
//using System.Collections.Concurrent;
using WaveTang.Classes;
public interface ICache<T,U>{
	//T=资源类型,U=资源ID类型
	T this[U id]{get;} //向cache申请使用资源,参数:id=资源ID,返回:资源
	Func<U,T> Loader{set;}//回调函数 要求访问者加载资源
	Action<U> UnLoader{set;}//回调函数 要求访问者卸载资源
}
[System.Security.Permissions.HostProtectionAttribute(Synchronization=true, ExternalThreading=true)]
public class LRUCache<T,U>:ICache<T,U>,IDisposable  where U:IComparable<U>{
	Int32 _unUsedSize;//可用资源数量(缓冲区的块数)
	Node _head,_tail;//使用带表头的单向链表,_head指向表头,_tail指向表尾,新节点在表尾插入,老节点从表头处摘除,允许将某一个节点移动到表尾
	Dictionary<U,Node> _dict;//维护U与Node之间的对照,以提供更高速的Find功能.(ID与Node的前一个节点对应)
	
	test使用
	public Int32 _accessed=0,_missed=0;

	
	//构造函数
	//参数 size : cache拥有的资源数
	//     load : 根据资源id加载资源的delegate
	//     unLoad : 卸载资源的delegate
	public LRUCache(Int32 size,Func<U,T> loader,Action<U> unLoader){
		_unUsedSize=size;
		_tail=_head=new Node(default(U)); //建立表头节点,使用带表头的单向链表存放数据
		Loader=loader;
		UnLoader=unLoader;
		_dict=new Dictionary<U,Node>();
	}
	
	//根据id查找对应的节点,返回目标节点的前一个节点
	protected Node Find(U id){
		//Node p=_head;
		//for(;p.Next!=null&&(p.Next.ID.CompareTo(id)!=0);p=p.Next);

		//改用dictionary查找表,提高查询效率
		return (_dict.ContainsKey(id))?(_dict[id]):_tail;
	}
	//在队列尾部加入节点
	protected Node Append(Node n){
		_dict.Add(n.ID,_tail);
		return _tail=_tail.InsertAfter(n);
	}
	//移除一个节点 (n.Next)
	protected Node Remove(Node n){
		if(n.Next==_tail)
			_tail=n;
		if(n.Next.Next!=null)
			_dict[n.Next.Next.ID]=n;
		_dict.Remove(n.Next.ID);
		return n.RemoveAfter();
	}
	//将指定节点的Next移动到队列尾部
	protected Node SendBack(Node n){
		if(n.Next!=_tail){
			return Append(Remove(n));//摘除节点 加到队列尾部
		}else
			return _tail;
	}

	//获得一个新的资源。
	protected Node CreateResource(U id){
		if(_unUsedSize>0){
			//有未使用的缓冲区,直接创建管理节点即可
			--_unUsedSize;
			return new Node(id);
		}else{
			//从表头摘下一个节点来使用。
			Node p=Remove(_head).UnLoadResource(UnLoader);
			p.ID=id;
			return p;
		}
	}
	protected String Show(Node n){
		String s=String.Empty;
		for(;n!=null;n=n.Next){
			s+=','+n.ID.ToString();
		}
		return s;
	}
	~LRUCache(){
		Dispose();
	}
	
	
	//实现ICache
	public T this[U id]{
		get{
			lock(_head){
				++_accessed;
				//Console.WriteLine("+++"+Show(_head));
				Node p=Find(id);//查找队列中是否有该资源
				if(p.Next==null){
					++_missed;
					p=Append(CreateResource(id));//无,则创建资源,并加入队列尾部
				}else
					p=SendBack(p);//有,将资源移到队列尾部
				p.LoadResource(Loader);//加载资源(若已经加载,则不做任何动作)
				//Console.WriteLine("---"+Show(_head));
				return p.Value;
			}
		}
	}
	public Func<U,T> Loader{protected get;set;}//回调函数 要求访问者加载资源
	public Action<U> UnLoader{protected get;set;}//回调函数 要求访问者卸载资源
	//public event CacheEventHandler<T,U> Load;
	//public event CacheEventHandler<T,U> UnLoad;

	
	//实现IDisposable
	public void Dispose(){
		lock(_head){
			//卸载cache中的资源
			for(Node p=_head.Next;p!=null;p=p.Next)
				if(p.Loaded)
					p.UnLoadResource(UnLoader);
			_head.Next=null;
		}
	}
	
	
	//节点定义
	public class Node{
		public Node(U id){
			Value=default(T);
			ID=id;
			Next=null;
			Loaded=false;
		}
		public T Value{get;set;}
		public U ID{get;set;}
		public Node Next{get;set;}
		public Boolean Loaded{get;set;}
		public Node InsertAfter(Node n){
			n.Next=this.Next;
			this.Next=n;
			return n;
		}
		public Node RemoveAfter(){
			Node n=this.Next;
			if(n!=null){
				this.Next=n.Next;
				n.Next=null;
			}
			return n;
		}
		//加载资源
		public Node LoadResource(Func<U,T> load){
			if(!Loaded){
				Value=load(ID);
				Loaded=true;
			}
			return this;
			
		}
		//卸载资源
		public Node UnLoadResource(Action<U> unLoad){
			//Console.WriteLine("==="+Show(_head));
			if(Loaded){
				unLoad(ID);
				Value=default(T);
				Loaded=false;
			}
			return this;
		}
	}
}

public class LIRSCache<T,U>:ICache<T,U>,IDisposable where U:IComparable<U>{
	Int32 _grade;//缓冲区级数
	Int32[] _unUsedSize;//可用资源数量(缓冲区的块数),_unUsedSize[0]存放cold数据,_unUsedSize[1]存放hot数据
	Node[] _head;//使用带表头的单向链表,_head[0]指向表头,_head[1]指向hot区的前一个节点(cold区的最后一个节点),_head[2]指向表尾
	Dictionary<U,Node> _dict;//维护U与Node之间的对照,以提供更高速的Find功能.(ID与Node的前一个节点对应)
	test使用
	public Int32 _accessed=0,_missed=0;
	
	
	//构造函数
	//参数 size : cache拥有的资源数
	//     load : 根据资源id加载资源的delegate
	//     unLoad : 卸载资源的delegate
	public LIRSCache(Int32 grade,Int32[] size,Func<U,T> loader,Action<U> unLoader){
		_grade=grade;
		//设置每级缓冲的大小
		_unUsedSize=new Int32[_grade];
		for(Int32 i=0;i<_grade;++i)
			_unUsedSize[i]=size[i];
		//初始化缓冲指针//建立表头节点,使用带表头的单向链表存放数据
		_head=new Node[_grade+1];
		_head[_grade]=new Node(default(U)); 
		for(Int32 i=0;i<_grade;++i)
			_head[i]=_head[_grade];
		Loader=loader;
		UnLoader=unLoader;
		_dict=new Dictionary<U,Node>();
	}
	
	protected Node Find(U id){
		/*foreach(var i in _dict)
			Console.WriteLine("{0}->{1}",i.Value.ID,i.Key);*/
		/*Node p=_head[0];
		for(;p.Next!=null&&(p.Next.ID.CompareTo(id)!=0);p=p.Next);
		
		Node q=(_dict.ContainsKey(id))?(_dict[id]):_head[_grade];
		if(p!=q)throw new Exception("Find Error");
		return q;*/
		//改用dictionary查找表,提高查询效率
		return (_dict.ContainsKey(id))?(_dict[id]):_head[_grade];
	}

	//在队列尾部加入节点
	protected Node Append(Int32 selector,Node n){
		_dict.Add(n.ID,_head[selector+1]);
		if(selector+1<_grade)
			_dict[_head[selector+1].Next.ID]=n;
		n.Selector=selector;
		return _head[selector+1]=_head[selector+1].InsertAfter(n);
	}
	//移除一个节点 (n.Next)
	protected Node Remove(Int32 selector,Node n){
		if(n.Next==_head[selector+1])
			_head[selector+1]=n;
		if(n.Next.Next!=null)
			_dict[n.Next.Next.ID]=n;
		_dict.Remove(n.Next.ID);
		return n.RemoveAfter();
	}
	//将指定节点的Next移动到队列尾部
	protected Node SendBack(Int32 selector,Node n){
		if(n.Next!=_head[selector+1])
			return Append(selector,Remove(selector,n));//摘除节点 加到队列尾部
		else
			return _head[selector+1];
	}
	//获得一个新的资源。
	protected Node CreateResource(U id){
		//有未使用的缓冲区,直接创建管理节点即可
		for(Int32 i=_grade-1;i>=0;--i){
			if(_unUsedSize[i]>0){
				--_unUsedSize[i];
				Node n=new Node(id);
				n.Selector=i; //记录新节点的级数
				return n;
			}
		}
		//从表头摘下一个节点来使用。
		Node p=Remove(0,_head[0]).UnLoadResource(UnLoader);
		p.ID=id;
		p.Selector=0;//指定为0级节点
		return p;
	}
	protected String Show(Node n){
		String s=String.Empty;
		for(;n!=null;n=n.Next){
			s+=','+n.ID.ToString();
		}
		return s;
	}
	~LIRSCache(){
		Dispose();
	}
	
	
	//实现ICache
	public T this[U id]{
		get{
			//return default(T);
			lock(_head){
				++_accessed;
				//Console.WriteLine(id);
				Node p=Find(id);
				Int32 selector;
				if(p.Next==null){
					//无,则创建资源,并加入队列尾部
					++_missed;
					p=CreateResource(id);
					//队列未满,将节点加入指定级缓冲的尾部
					//替换下来的节点,加入0级队列的尾部
					Append(p.Selector,p);
				}else{
					//有,若已经到最后一级,则移动到尾部,否则将资源升级到下一级的头部
					selector=p.Next.Selector;
					if(selector+1<_grade){
						p=Remove(selector,p); // 移除节点
						_head[selector+1]=_head[selector+1].Next;//移动selector+1级的头指针,使原先的头节点成为selector级的尾部
						_head[selector+1].Selector=selector;//指定节点的新级别
						Append(selector+1,p);//在selector+1级尾部添加节点
					}else{
						p=SendBack(selector,p);
					}
				}
				p.LoadResource(Loader);//加载资源(若已经加载,则不做任何动作)
				/*Console.WriteLine("0:"+Show(_head[0]));
				Console.WriteLine("1:"+Show(_head[1]));
				Console.WriteLine("2:"+Show(_head[2]));*/
				return p.Value;
			}
		}
	}
	public Func<U,T> Loader{protected get;set;}//回调函数 要求访问者加载资源
	public Action<U> UnLoader{protected get;set;}//回调函数 要求访问者卸载资源
	//public event CacheEventHandler<T,U> Load;
	//public event CacheEventHandler<T,U> UnLoad;

	
	//实现IDisposable
	public void Dispose(){
	}
	
	public class Node{
		public Node(U id){
			Value=default(T);
			ID=id;
			Next=null;
			Loaded=false;
			Selector=0;
		}
		public T Value{get;set;}
		public U ID{get;set;}
		public Int32 Selector{get;set;}
		public Node Next{get;set;}
		public Boolean Loaded{get;set;}
		
		public Node InsertAfter(Node n){
			n.Next=this.Next;
			this.Next=n;
			return n;
		}
		public Node RemoveAfter(){
			Node n=this.Next;
			if(n!=null){
				this.Next=n.Next;
				n.Next=null;
			}
			return n;
		}
		//加载资源
		public Node LoadResource(Func<U,T> load){
			if(!Loaded){
				Value=load(ID);
				Loaded=true;
			}
			return this;
			
		}
		//卸载资源
		public Node UnLoadResource(Action<U> unLoad){
			//Console.WriteLine("==="+Show(_head));
			if(Loaded){
				unLoad(ID);
				Value=default(T);
				Loaded=false;
			}
			return this;
		}
	}
}
public class Test{
	public static void Main(){
		List<Int32> list=GetRandomGaussian(1000000);
		Stopwatch  sw=new Stopwatch ();
		sw.Reset();
		sw.Start();
		TestLRUCache(list);
		sw.Stop();
		Console.WriteLine(sw.ElapsedTicks);
		sw.Reset();
		sw.Start();
		TestLIRSCache(list);
		sw.Stop();
		Console.WriteLine(sw.ElapsedTicks);
		sw.Reset();
		sw.Start();
		TestLIRSCache2(list);
		sw.Stop();
		Console.WriteLine(sw.ElapsedTicks);
		sw.Reset();
		sw.Start();
		TestLIRSCache3(list);
		sw.Stop();
		Console.WriteLine(sw.ElapsedTicks);
	}
	public static List<Int32> GetRandomList(Int32 len){
		List<Int32> list=new List<Int32>();
		for(Int32 i=0;i<len;i++)
			list.Add(i%1001);
		return list;
	}
	public static List<Int32> GetRandomAvg(Int32 len){
		Random r=new Random();
		List<Int32> list=new List<Int32>();
		for(Int32 i=0;i<len;i++)
			list.Add(r.Next(2000));
		return list;
	}
	public static List<Int32> GetRandomGaussian(Int32 len){
		GaussianRandom r=new GaussianRandom();
		List<Int32> list=new List<Int32>();
		for(Int32 i=0;i<len;i++)
			list.Add(r.Next(500,250));
		return list;
	}
	public static void TestLIRSCache(List<Int32> r){
		LIRSCache<String,Int32> cache =new LIRSCache<String,Int32>(2,new Int32[]{100,900},id=>{return "="+id.ToString()+'=';},id=>{});
		Dictionary<Int32,Int32> dict=new Dictionary<Int32,Int32>();
		Int32 k;
		foreach(Int32 j in r){
			if(dict.ContainsKey(j))dict[j]++;else dict.Add(j,1);
			//Console.WriteLine(cache[j]);
			k=cache[j].Length;
		}
		Console.WriteLine("LIRS1 : 1-{0}/{1}={2}",cache._missed,cache._accessed,1.0-1.0*cache._missed/cache._accessed);
		//dict.OrderBy(d=>d.Key).ForEach(k=>{Console.WriteLine("{0},{1}",k.Key,k.Value);});
		
	}
	public static void TestLIRSCache2(List<Int32> r){
		LIRSCache<String,Int32> cache =new LIRSCache<String,Int32>(3,new Int32[]{10,100,890},id=>{return "="+id.ToString()+'=';},id=>{});
		Dictionary<Int32,Int32> dict=new Dictionary<Int32,Int32>();
		Int32 k;
		foreach(Int32 j in r){
			if(dict.ContainsKey(j))dict[j]++;else dict.Add(j,1);
			//Console.WriteLine(cache[j]);
			k=cache[j].Length;
		}
		Console.WriteLine("LIRS2 : 1-{0}/{1}={2}",cache._missed,cache._accessed,1.0-1.0*cache._missed/cache._accessed);
		//dict.OrderBy(d=>d.Key).ForEach(k=>{Console.WriteLine("{0},{1}",k.Key,k.Value);});
		
	}
	public static void TestLIRSCache3(List<Int32> r){
		LIRSCache<String,Int32> cache =new LIRSCache<String,Int32>(2,new Int32[]{10,990},id=>{return "="+id.ToString()+'=';},id=>{});
		Dictionary<Int32,Int32> dict=new Dictionary<Int32,Int32>();
		Int32 k;
		foreach(Int32 j in r){
			if(dict.ContainsKey(j))dict[j]++;else dict.Add(j,1);
			//Console.WriteLine(cache[j]);
			k=cache[j].Length;
		}
		Console.WriteLine("LIRS3 : 1-{0}/{1}={2}",cache._missed,cache._accessed,1.0-1.0*cache._missed/cache._accessed);
		//dict.OrderBy(d=>d.Key).ForEach(k=>{Console.WriteLine("{0},{1}",k.Key,k.Value);});
		
	}
	public static void TestLRUCache(List<Int32> r){
		LRUCache<String,Int32> cache =new LRUCache<String,Int32>(200,id=>{return "="+id.ToString()+'=';},id=>{});
		Dictionary<Int32,Int32> dict=new Dictionary<Int32,Int32>();
		Int32 k;
		foreach(Int32 j in r){
			if(dict.ContainsKey(j))dict[j]++;else dict.Add(j,1);
			//Console.WriteLine(cache[j]);
			k=cache[j].Length;
		}
		Console.WriteLine("LRU   : 1-{0}/{1}={2}",cache._missed,cache._accessed,1.0-1.0*cache._missed/cache._accessed);
		//dict.OrderBy(d=>d.Key).ForEach(k=>{Console.WriteLine("{0},{1}",k.Key,k.Value);});
		
	}
}




  • 0
    点赞
  • 1
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值