我有以下简单类:
public class MyClass{
public long StartRange { get; set; }
public long EndRange { get; set; }
public int Id { get; set; }
}
我需要在内存缓存中存储许多,10 ^ 5到10 ^ 6。在应用程序启动时会有一次写入此缓存并进行多次读取。这个缓存将在ASP.NET环境中访问,因此有很多线程。
我需要在此缓存中查找一行,其中我的值介于StartRange和EndRange之间。范围不重叠,但可能稀疏。我发现这样做的最简单方法如下:
public MyClass Lookup(long value){
return _set.FirstOrDefault(d => value >= d.StartRange && value <= d.EndRange);
}
我已尝试将该设置存储为IOrderedEnumerable<T>
和SortedSet<T>
。 SortedSet的速度提高了一个数量级。不知怎的,HashSet<T>
比SortedSet快一点。对于使用最有效的集合类或更好的查找的任何建议都将非常受欢迎。
答案 0 :(得分:3)
范围不重叠,但可能很稀疏。
如果我理解正确,这意味着如果您通过StartRange对它们进行排序,然后使用value >= d.StartRange
识别第一个项目,您可以立即知道您已找到您的项目(如果{{1 }},或者没有匹配,对吧?
所以你可以通过这样做来缩短你的时间:
value <= d.EndRange
而且,由于可以在public MyClass Lookup(long value){
var candidate = _set.FirstOrDefault(d => value >= d.StartRange);
if(candidate != null && value <= candidate.EndRange)
{
return candidate;
}
return null;
}
时间内轻松搜索已排序的集合,因此只需二进制搜索即可获得显着的性能提升。这里有一些示例代码可以让您走上正确的轨道。
O(log n)
答案 1 :(得分:2)
你能不能只按StartRange
排序,使用Array.BinarySearch
找到最近的一个(仍然更小),因为你的范围很稀疏,只需一次检查就知道了(Endrange
如果你找到一个或错过了,那么是否大于x)?
您需要做的就是实现IComparable<T>
并以StartRange
作为关键,这很容易。
答案 2 :(得分:0)
可能不是你真正需要的,但你有没有看过NoSQL? 有些实现就像你想要的那样,有些实现了你可以从内存运行的缓存,所以应该很快。
如果我的记忆为我服务,Redis可能就是你想要研究的那个。 (这里是关于Redis的article)。
K,有些人挖掘了我的东西:NoSQL DB comparisson,你可以看到有什么主要口味和哪些用例有利。从我所看到的情况来看,他们在一些实现中使用B-Trees来提高速度。如果你想重新创建轮子,你可能想要做类似的事情。
答案 3 :(得分:0)
如果您的范围现在或将来可能会重叠,您可能会考虑使用interval tree。
但如果您确定自己的范围没有重叠,请将class
更改为struct
并执行以下操作。将class
更改为struct
的原因有两方面:
使用数组保存内存。引用类型数组是对堆上各个对象的引用数组。
可能更重要的是,它有助于保持参考的位置。你将在一块连续的内存块中跳来跳去,而不是在整个堆中随机跳转。这应该有助于减少分页。
以下是代码:
class MyClassMap
{
MyClass[] backingStore ; // ordered by StartRange, then EndRange
public MyClassMap( IEnumerable<MyClass> source )
{
backingStore.OrderBy( x => x.StartRange ).ThenBy( x => x.EndRange ) ;
}
public int? GetIdFromValue( long value )
{
int lo = 0 ;
int hi = backingStore.Length ;
int? ix = null ;
while ( lo <= hi && !ix.HasValue )
{
int mid = lo + ((hi-lo)>>1) ;
MyClass current = backingStore[mid] ;
if ( value > current.EndRange ) { lo = mid+1 ; }
else if ( value < current.StartRange ) { hi = mid-1 ; }
else { ix = current.Id ; }
}
return ix ;
}
}