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