如何使简单的.NET LRU缓存更快?
昨晚和今晚,我尝试了几种不同的方法,并提出了一种类似于Jeff在下面列出的方法(我甚至已经完成了他在更新中提出的建议,并将我自己的简单LL实现组合起来以获得更多收益)。这是代码,在这一点上,它看起来不再特别干净了,但我已经过了很多次了,为了提高性能,我做了很多改变如何使简单的.NET LRU缓存更快?,.net,performance,data-structures,caching,lru,.net,Performance,Data Structures,Caching,Lru,昨晚和今晚,我尝试了几种不同的方法,并提出了一种类似于Jeff在下面列出的方法(我甚至已经完成了他在更新中提出的建议,并将我自己的简单LL实现组合起来以获得更多收益)。这是代码,在这一点上,它看起来不再特别干净了,但我已经过了很多次了,为了提高性能,我做了很多改变 public class NewLRU2<K, V> where V : class { int m_iMaxItems; Dictionary<K, LRUNode<K, V>>
public class NewLRU2<K, V> where V : class
{
int m_iMaxItems;
Dictionary<K, LRUNode<K, V>> m_oMainDict;
private LRUNode<K,V> m_oHead;
private LRUNode<K,V> m_oTail;
private LRUNode<K,V> m_oCurrent;
public NewLRU2(int iSize)
{
m_iMaxItems = iSize;
m_oMainDict = new Dictionary<K, LRUNode<K,V>>();
m_oHead = null;
m_oTail = null;
}
public V this[K key]
{
get
{
m_oCurrent = m_oMainDict[key];
if (m_oCurrent == m_oHead)
{
//do nothing
}
else if (m_oCurrent == m_oTail)
{
m_oTail = m_oCurrent.Next;
m_oTail.Prev = null;
m_oHead.Next = m_oCurrent;
m_oCurrent.Prev = m_oHead;
m_oCurrent.Next = null;
m_oHead = m_oCurrent;
}
else
{
m_oCurrent.Prev.Next = m_oCurrent.Next;
m_oCurrent.Next.Prev = m_oCurrent.Prev;
m_oHead.Next = m_oCurrent;
m_oCurrent.Prev = m_oHead;
m_oCurrent.Next = null;
m_oHead = m_oCurrent;
}
return m_oCurrent.Value;
}
}
public void Add(K key, V value)
{
if (m_oMainDict.Count >= m_iMaxItems)
{
//remove old
m_oMainDict.Remove(m_oTail.Key);
//reuse old
LRUNode<K, V> oNewNode = m_oTail;
oNewNode.Key = key;
oNewNode.Value = value;
m_oTail = m_oTail.Next;
m_oTail.Prev = null;
//add new
m_oHead.Next = oNewNode;
oNewNode.Prev = m_oHead;
oNewNode.Next = null;
m_oHead = oNewNode;
m_oMainDict.Add(key, oNewNode);
}
else
{
LRUNode<K, V> oNewNode = new LRUNode<K, V>(key, value);
if (m_oHead == null)
{
m_oHead = oNewNode;
m_oTail = oNewNode;
}
else
{
m_oHead.Next = oNewNode;
oNewNode.Prev = m_oHead;
m_oHead = oNewNode;
}
m_oMainDict.Add(key, oNewNode);
}
}
public bool Contains(K key)
{
return m_oMainDict.ContainsKey(key);
}
}
internal class LRUNode<K,V>
{
public LRUNode(K key, V val)
{
Key = key;
Value = val;
}
public K Key;
public V Value;
public LRUNode<K, V> Next;
public LRUNode<K, V> Prev;
}
公共类NewLRU2其中V:class
{
int m_iMaxItems;
字典m_omadiment;
马赫德私人酒店;
私人住宅;
私人住宅;
公共新LRU2(国际标准化)
{
m_iMaxItems=iSize;
m_omainct=新字典();
m_oHead=null;
m_oTail=null;
}
公共V本[K键]
{
得到
{
m_oCurrent=m_omadiment[键];
if(m_oCurrent==m_oHead)
{
//无所事事
}
else if(m_oCurrent==m_oTail)
{
m_oTail=m_oCurrent.Next;
m_oTail.Prev=null;
m_oHead.Next=m_oCurrent;
m_occurrent.Prev=m_oHead;
m_occurrent.Next=null;
m_oHead=m_oCurrent;
}
其他的
{
m_oCurrent.Prev.Next=m_oCurrent.Next;
m_oCurrent.Next.Prev=m_oCurrent.Prev;
m_oHead.Next=m_oCurrent;
m_occurrent.Prev=m_oHead;
m_occurrent.Next=null;
m_oHead=m_oCurrent;
}
返回m_oCurrent.Value;
}
}
公共无效添加(K键,V值)
{
如果(m_omainct.Count>=m_iMaxItems)
{
//除去旧的
m_omainct.Remove(m_oTail.Key);
//再利用旧的
LRUNode oNewNode=m_oTail;
oNewNode.Key=Key;
oNewNode.Value=值;
m_oTail=m_oTail.Next;
m_oTail.Prev=null;
//新增
m_oHead.Next=oNewNode;
oNewNode.Prev=m_oHead;
oNewNode.Next=null;
m_oHead=一个节点;
m_omadiment.Add(键,oNewNode);
}
其他的
{
LRUNode oNewNode=新的LRUNode(键,值);
if(m_oHead==null)
{
m_oHead=一个节点;
m_oTail=一个节点;
}
其他的
{
m_oHead.Next=oNewNode;
oNewNode.Prev=m_oHead;
m_oHead=一个节点;
}
m_omadiment.Add(键,oNewNode);
}
}
公共布尔包含(K键)
{
返回m_omadication.ContainsKey(键);
}
}
内部类LRUNode
{
公共LRUNode(K键,V值)
{
钥匙=钥匙;
值=val;
}
公钥;
公共价值观;
公共LRUNode Next;
前卢诺德公共酒店;
}
有几个部分看起来/感觉不可靠——比如在进行添加时重用旧节点——但我能够从中获得可观的性能提升。我还对从节点上的实际属性切换到公共变量所产生的差异感到有点惊讶,但我想这就是它处理这些事情的方式。在这一点上,上面的代码几乎完全受字典操作的性能限制,所以我不确定是否能从混搭中获得更多。我会继续思考,并调查一些回应
原帖解释:
大家好。
因此,我编写了一个简单的轻量级LRU实现,用于压缩库(我使用它根据哈希、LZW样式在输入中查找匹配的字节字符串),并正在寻找使其更快的方法。更新#2
这减少了对链表遍历的需要。它引入了一个同时具有键和值的lrucchenode。该键仅在修剪缓存时使用。如果编写自己的链表实现,其中每个节点本质上都是一个lrucchenode以及一个Next和Back引用,则可以获得更好的性能。这就是LinkedHashMap所做的(参见问题)
公共类LruCache
{
私有只读int m_iMaxItems;
私人只读字典m_omadicate;
私有只读链接列表m_oMainList;
公共LruCache(国际标准化)
{
m_iMaxItems=iSize;
m_omainct=新字典();
m_oMainList=新链接列表();
}
公共V本[K键]
{
得到
{
返回BumpToFront(key).Value;
}
设置
{
BumpToFront(键)。值=值;
}
}
公共无效添加(K键,V值)
{
LinkedListNode newNode=m_oMainList.AddFirst(新的LruCacheNode(键,值));
m_omadiment.Add(键,新节点);
if(m_oMainList.Count>m_iMaxItems)
{
m_omain.Remove(m_oMainList.Last.Value.Key);
m_oMainList.RemoveLast();
}
}
专用LruCacheNode保险杠前部(K键)
{
LinkedListNode节点=m_omaIndicat[键];
if(m_oMainList.First!=节点)
{
m_oMainList.Remove(节点);
m_oMainList.AddFirst(节点);
}
返回节点值;
}
公共布尔包含(K键)
{
返回m_omadication.ContainsKey(键);
}
}
内密封级LruCacheNode
{
私有只读K m_密钥;
私人价值;
公共LruCacheNode(K键,V值)
{
m_Key=Key;
m_值=值;
}
公钥
{
获取{return m_Key;}
}
公共价值
{
获取{返回m_值;}
设置{m_Value=Value;}
}
}
您必须分析一些情况,看看这是否对您的环境有所改善
次要更新:public class LruCache<K, V>
{
private readonly int m_iMaxItems;
private readonly Dictionary<K, LinkedListNode<LruCacheNode<K, V>>> m_oMainDict;
private readonly LinkedList<LruCacheNode<K, V>> m_oMainList;
public LruCache(int iSize)
{
m_iMaxItems = iSize;
m_oMainDict = new Dictionary<K, LinkedListNode<LruCacheNode<K, V>>>();
m_oMainList = new LinkedList<LruCacheNode<K, V>>();
}
public V this[K key]
{
get
{
return BumpToFront(key).Value;
}
set
{
BumpToFront(key).Value = value;
}
}
public void Add(K key, V value)
{
LinkedListNode<LruCacheNode<K, V>> newNode = m_oMainList.AddFirst(new LruCacheNode<K, V>(key, value));
m_oMainDict.Add(key, newNode);
if (m_oMainList.Count > m_iMaxItems)
{
m_oMainDict.Remove(m_oMainList.Last.Value.Key);
m_oMainList.RemoveLast();
}
}
private LruCacheNode<K, V> BumpToFront(K key)
{
LinkedListNode<LruCacheNode<K, V>> node = m_oMainDict[key];
if (m_oMainList.First != node)
{
m_oMainList.Remove(node);
m_oMainList.AddFirst(node);
}
return node.Value;
}
public bool Contains(K key)
{
return m_oMainDict.ContainsKey(key);
}
}
internal sealed class LruCacheNode<K, V>
{
private readonly K m_Key;
private V m_Value;
public LruCacheNode(K key, V value)
{
m_Key = key;
m_Value = value;
}
public K Key
{
get { return m_Key; }
}
public V Value
{
get { return m_Value; }
set { m_Value = value; }
}
}