我有一個方法,使用遞歸來遍歷樹和更新項目。爲什麼我的字典在C#中使用複合鍵表現不佳?
目前該方法需要相當長的時間來處理所有的項目,所以我開始優化的東西。其中包括使用字典而不是爲每個項目執行數據庫查詢。
字典定義爲
System.Collections.Generic.Dictionary<EffectivePermissionKey, MyData>
鍵類型被定義爲
private struct EffectivePermissionKey
{
// http://blog.martindoms.com/2011/01/03/c-tip-override-equals-on-value-types-for-better-performance/
public override bool Equals(object aObject)
{
if (aObject == null)
return false;
else
return aObject is EffectivePermissionKey && Equals((EffectivePermissionKey)aObject);
}
public bool Equals(EffectivePermissionKey aObject)
{
return this.ID == aObject.ID && this.OrchardUserID == aObject.OrchardUserID;
}
public override int GetHashCode()
{
// http://stackoverflow.com/a/32502294/3936440
return unchecked(ID.GetHashCode() * 23 * 23 + OrchardUserID.GetHashCode() * 23);
}
public int ID;
public int OrchardUserID;
}
當方法運行大約需要5000遞歸更新所有項目。
最初大約需要100秒沒有字典。
DB查詢的第一種方法是使用帶有int
鍵的字典替換了22秒。
現在,將DB查詢替換爲使用上面定義的字典並且正確地使用TryGetValue()
調用它需要97秒 <-WAT。
這是怎麼回事?什麼會導致這種大規模的性能下降?
編輯
起初,這似乎是一個哈希衝突的問題給我,讓我在EffectivePermissionKey.Equals()
添加一個斷點來驗證這種方法的調用,但它不叫,因此沒有哈希衝突我猜。
EDIT2
現在我很困惑。我認爲Equals()
只有當哈希代碼不匹配匹配時才被調用。在打印出我的密鑰和TryGetValue()
中使用的密鑰的哈希代碼後,我發現這些代碼是匹配的。然後我看着Dictionary<>
的源代碼,並有在FindEntry()
一條線,看起來像這樣:
if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) return i;
這意味着,在字典中的每個項目關鍵的GetHashCode()
和Equals()
被調用,因爲我處理所有項目在字典中,項目是數據庫查詢的結果,而這些結果無論如何都是在字典方法之前處理的。
我沒有看到你的'Equals'和'GetHashCode'有什麼問題(我更喜歡17/23「累積」這裏http://stackoverflow.com/questions/263400/what-is-the- best-algorithm-for-an-overridden-system-object-gethashcode,但仍然是你的「非累積」版本不應該引起太多的衝突 – xanatos
也許是因爲裝箱/拆箱?EffectivePermissionKey不實現IEquatable EffectivePermissionKey >''這意味着字典將使用'ObjectEqualityComparer' –
你沒有在你的結構中實現'IEquatable',所以你的結構將會被裝箱並且會損害性能 –