簡體   English   中英

HashSet的 <T> GetHashcode優化

[英]HashSet<T> GetHashcode optimization

我在C#中具有以下結構來表示圖形邊緣:

struct Edge
{
    public Edge(int leftA, int leftB, int leftC, int leftD, int rightA, int rightB, int rightC, int rightD)
    {
        LeftIdA = leftA;
        LeftIdB = leftB;
        LeftIdC = leftC;
        LeftIdD = leftD;

        RightIdA = rightA;
        RightIdB = rightB;
        RightIdC = rightC;
        RightIdD = rightD;
    }

    public readonly int LeftIdA;
    public readonly int LeftIdB;
    public readonly int LeftIdC;
    public readonly int LeftIdD;

    public readonly int RightIdA;
    public readonly int RightIdB;
    public readonly int RightIdC;
    public readonly int RightIdD;
}

並且需要在HashSet中存儲很多(大約500萬個),因此沒有重復項。 什么是GetHashCode的良好實現,以便針對速度進行優化?

我試圖將每個id的4位存儲在返回的整數中,如下所示:

    public override int GetHashCode()
    {
        int A = LeftIdA & 0xF;
        int B = LeftIdB & 0xF;
        int C = LeftIdC & 0xF;
        int D = LeftIdD & 0xF;

        int E = RightIdA & 0xF;
        int F = RightIdB & 0xF;
        int G = RightIdC & 0xF;
        int H = RightIdD & 0xF;

        int result = A;
        result = (result << 4) | B;
        result = (result << 4) | C;
        result = (result << 4) | D;
        result = (result << 4) | E;
        result = (result << 4) | F;
        result = (result << 4) | G;
        result = (result << 4) | H;

        return result;
    }

但這比將項目添加到列表慢80%。

什么是GetHashCode的良好實現,以便針對速度進行優化?

由於所有字段都是只讀的,因此最好的選擇是預先計算構造函數中的哈希碼,然后從GetHashCode返回該哈希碼。

要預先計算哈希碼,可以使用Guffa答案中的公式。

添加到HashSet中將花費更長的時間,這並不是因為GetHashCode()實現中存在任何錯誤的策略。 實際上,此實現看起來不錯。 HashSet必須在內部進行各種瘋狂的廢話,例如設置存儲桶並將其放入其中。

性能提升在於找到哈希集中的元素。 嘗試將500萬個不同的項目添加到列表和哈希集,然后查看哪個容器能夠更快地告訴您是否包含特定Edge。 您可能會願意花不到兩倍的設置時間。

為了達到最佳效果,哈希碼應提供盡可能少的沖突,即產生盡可能多的哈希碼。

嘗試生成哈希碼,以便使用來自所有成員的所有數據:

public override int GetHashCode() {
  return
    LeftIdA ^ LeftIdB ^ LeftIdC ^ LeftIdD ^
    RightIdA ^ RightIdB ^ RightIdC ^ RightIdD;
}

與質數相乘可以得到很好的分布,因此您應該測試一下在這種情況下是否可以提供更好的性能:

public override int GetHashCode() {
  return
    ((((((LeftIdA * 251 + LeftIdB) * 251 + LeftIdC) * 251 +
    LeftIdD) * 251 + RightIdA) * 251 + RightIdB) * 251 +
    RightIdC) * 251 + RightIdD;
}

注意:確保還為該結構提供了優化的相等比較。 默認實現將使用反射來確定要比較的所有成員,因此非常慢。

編輯:

我進行了一些測試,使用第二種實現,我可以在大約兩秒鍾內將500萬個項目添加到HashSet中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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