簡體   English   中英

用於按屬性搜索的最快C#集合

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM