[英]Fastest C# collection for searching by property
我有以下簡單的類:
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。 關於最有效的集合類使用或更好的查找的任何建議將是最受歡迎的。
范圍不重疊,但可能稀疏。
如果我理解正確,這意味着如果你通過StartRange對它們進行排序,然后識別value >= d.StartRange
的第一個項目,你就可以立即知道你找到了你的項目(如果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)
時間內輕松完成,因此只需二進制搜索就可以獲得顯着的性能提升。 這里有一些示例代碼可以讓您走上正確的軌道。
List<MyClass> _set = new[]{
new MyClass{StartRange = 18, EndRange = 18},
new MyClass{StartRange = 10, EndRange = 15},
new MyClass{StartRange = 20, EndRange = 21}
}.OrderBy(m => m.StartRange).ToList();
public class StartRangeComparer : IComparer<MyClass>
{
public int Compare(MyClass first, MyClass second)
{
return first.StartRange.CompareTo(second.StartRange);
}
}
StartRangeComparer startRangeComparer = new StartRangeComparer();
public MyClass Lookup(long value){
var index = _set.BinarySearch(new MyClass{StartRange = value}, startRangeComparer);
int candidateIndex = index >= 0 ? index : (~index) - 1;
if(candidateIndex < 0)
{
// the given value is before any start-ranges in the list
return null;
}
MyClass candidate = _set[candidateIndex];
if(candidate.EndRange >= value)
{
return candidate;
}
else
{
return null;
};
}
你可以不只是按StartRange
排序,使用Array.BinarySearch
找到最近的一個(仍然更小),因為你的范圍很稀疏,如果你找到一個或錯過了一個檢查(如果Endrange
大於x)知道嗎?
你需要做的就是使用StartRange
作為鍵實現IComparable<T>
,這很容易。
可能不是你真正需要的,但你有沒有看過NoSQL? 有些實現就像你想要的那樣,有些實現了一個可以從內存運行的緩存,所以應該很快。
如果我的記憶為我服務,Redis可能就是你想要研究的那個。 (這是一篇關於Redis的文章 )。
K,一些挖掘我的東西產生了: NoSQL DB comparisson ,你可以看到有哪些主要口味和哪些用例是好的。
從我所看到的,他們在一些實現中使用B-Trees來提高速度。 如果你想重新創建輪子我可能想要做類似的事情。
如果您的范圍現在或將來可能重疊,您可能會考慮使用間隔樹 。
但是,如果您確定您的范圍沒有重疊,請將您的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 ;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.