UPDATE:
Эй, ребята, спасибо за ответы. Прошлой ночью и сегодня вечером я попробовал несколько разных подходов и придумал один, похожий на тот, что изложен ниже Джеффом (я даже уже сделал то, что он предложил в своем обновлении, и собрал мою собственную простую реализацию LL для дополнительных преимуществ). Вот код, на данный момент он больше не выглядит особенно чистым, но я неоднократно менял все, что мог, чтобы повысить производительность.
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;
}
Есть несколько частей, которые выглядят / чувствуют себя отвратительно - например, повторное использование старого узла при выполнении добавления - но я смог получить ощутимый прирост производительности из них. Я также был немного удивлен различием, которое оно имело, переключаясь с реальных свойств на узле на просто публичные переменные, но я полагаю, что так оно и есть. На данный момент приведенный выше код почти полностью ограничен по производительности операциями со словарем, поэтому я не уверен, что получу гораздо больше от его использования. Я продолжу думать об этом и рассмотрю некоторые ответы.
Объяснение из оригинального сообщения:
Всем привет.
Итак, я написал простую облегченную реализацию LRU для использования в библиотеке сжатия (я использую ее для поиска подходящих байтовых строк во входных данных на основе хеша в стиле LZW), и я ищу способы сделать это быстрее.