C#中是否有现成的LFU缓存?
答案 0 :(得分:7)
Java有大量的LFU缓存实现,应该很容易移植到C#,例如参见:http://faq.javaranch.com/view?CachingStrategies
此商业.NET库执行LFU http://www.kellermansoftware.com/pc-38-2-net-caching-library.aspx
这个其他商业.NET库也支持LFU:http://www.sharedcache.com/cms/
我相信还有其他人。
这是一个基本的LFU实现老化的C#我刚刚被淘汰,它不完美但是一个很好的起点: 注意:此实现不是线程安全的。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LFU {
class LFUCache<TKey,TValue> {
Dictionary<TKey, LinkedListNode<CacheNode>> cache = new Dictionary<TKey, LinkedListNode<CacheNode>>();
LinkedList<CacheNode> lfuList = new LinkedList<CacheNode>();
class CacheNode {
public TKey Key { get; set; }
public TValue Data { get; set; }
public int UseCount { get; set; }
}
int size;
int age = 0;
int agePolicy;
public LFUCache(int size) : this(size, 1000) {
}
/// <summary>
///
/// </summary>
/// <param name="size"></param>
/// <param name="agePolicy">after this number of gets the cache will take 1 off all UseCounts, forcing old stuff to expire.</param>
public LFUCache(int size, int agePolicy) {
this.agePolicy = 1000;
this.size = size;
}
public void Add(TKey key, TValue val) {
TValue existing;
if (!TryGet(key, out existing)) {
var node = new CacheNode() {Key = key, Data = val, UseCount = 1};
if (lfuList.Count == size) {
cache.Remove(lfuList.First.Value.Key);
lfuList.RemoveFirst();
}
var insertionPoint = Nodes.LastOrDefault(n => n.Value.UseCount < 2);
LinkedListNode<CacheNode> inserted;
if (insertionPoint == null) {
inserted = lfuList.AddFirst(node);
} else {
inserted = lfuList.AddAfter(insertionPoint, node);
}
cache[key] = inserted;
}
}
IEnumerable<LinkedListNode<CacheNode>> Nodes {
get {
var node = lfuList.First;
while (node != null) {
yield return node;
node = node.Next;
}
}
}
IEnumerable<LinkedListNode<CacheNode>> IterateFrom(LinkedListNode<CacheNode> node) {
while (node != null) {
yield return node;
node = node.Next;
}
}
public TValue GetOrDefault(TKey key) {
TValue val;
TryGet(key, out val);
return val;
}
public bool TryGet(TKey key, out TValue val) {
age++;
if (age > agePolicy) {
age = 0;
foreach (var node in cache.Values) {
var v = node.Value;
v.UseCount--;
}
}
LinkedListNode<CacheNode> data;
bool success = false;
if (cache.TryGetValue(key, out data)) {
var cacheNode = data.Value;
val = cacheNode.Data;
cacheNode.UseCount++;
var insertionPoint = IterateFrom(data).Last(n => n.Value.UseCount <= cacheNode.UseCount);
if (insertionPoint != data) {
lfuList.Remove(data);
lfuList.AddAfter(insertionPoint, data);
}
} else {
val = default(TValue);
}
return success;
}
}
class Program {
public static void Assert(bool condition, string message) {
if (!condition) {
Console.WriteLine("Assert failed : " + message);
throw new ApplicationException("Test Failed");
}
}
public static void RunAllTests(Program prog){
foreach (var method in prog.GetType().GetMethods()) {
if (method.Name.StartsWith("Test")) {
try {
method.Invoke(prog, null);
Console.WriteLine("Test Passed: " + method.Name);
} catch (Exception) {
Console.WriteLine("Test Failed : " + method.Name);
}
}
}
}
public void TestItemShouldBeThereOnceInserted() {
var cache = new LFUCache<string, int>(3);
cache.Add("a", 1);
cache.Add("b", 2);
cache.Add("c", 3);
cache.Add("d", 4);
Assert(cache.GetOrDefault("a") == 0, "should get 0");
Assert(cache.GetOrDefault("b") == 2, "should get 2");
Assert(cache.GetOrDefault("c") == 3, "should get 3");
Assert(cache.GetOrDefault("d") == 4, "should get 4");
}
public void TestLFUShouldWorkAsExpected() {
var cache = new LFUCache<string, int>(3);
cache.Add("a", 1);
cache.Add("b", 2);
cache.Add("c", 3);
cache.GetOrDefault("a");
cache.GetOrDefault("a");
cache.GetOrDefault("b");
cache.Add("d", 4);
cache.Add("e", 5);
Assert(cache.GetOrDefault("a") == 1, "should get 1");
Assert(cache.GetOrDefault("b") == 2, "should get 0");
Assert(cache.GetOrDefault("c") == 0, "should get 0");
Assert(cache.GetOrDefault("d") == 0, "should get 4");
Assert(cache.GetOrDefault("e") == 5, "should get 5");
}
static void Main(string[] args) {
RunAllTests(new Program());
Console.ReadKey();
}
}
}
答案 1 :(得分:5)
.NET 3.5中的框架中没有这样的集。 Ayende已创建Least Recently Used集。这可能是一个好的开始(code)。
答案 2 :(得分:0)
您可以查看MS "Velocity"。我不确定他们提供什么样的策略,但值得一看。此外,您可以扩展Caching Application Block提供的策略。
答案 3 :(得分:0)
我制作了我的,FWIW:
https://github.com/ysharplanguage/GenericMemoryCache
(它不仅支持LFU,还支持LRU / MRU,并且可以扩展到其他驱逐和替换策略,如果有人愿意的话)
&#39;希望这有帮助,
答案 4 :(得分:0)
尝试CacheCrow,它是一个简单的LFU,基于时间的缓存。