I have a method that uses recursion to traverse a tree and update the items.
Currently the method takes pretty long to process all the items, so i started optimizing things. Among those things is the use of a dictionary instead of executing a DB query for each item.
The dictionary is defined as
System.Collections.Generic.Dictionary<EffectivePermissionKey, MyData>
The key type is defined as
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;
}
When the method runs it takes around 5000 recursions to update all items.
Initially it took around 100 seconds without the dictionary.
A first approach with DB queries replaced by the use of a dictionary with int
keys took 22 seconds.
Now, with DB queries replaced by the use of the dictionary defined above and proper TryGetValue()
calls it takes 97 seconds <- WAT.
What is going on here? What could cause this massive performance drop?
Edit
At first, it seemed like a hash collision issue to me, so i added a breakpoint in EffectivePermissionKey.Equals()
to verify that this method is called but it's not called, therefore no hash collision i guess.
Edit2
Now i'm confused. I thought Equals()
only gets called when the hash code does not match. After printing out the hash codes of my keys and the keys used in TryGetValue()
i see that these codes match. Then i looked at the source code of Dictionary<>
and there's a line in FindEntry()
that looks like this:
if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) return i;
This means that for each item key in the dictionary the GetHashCode()
and Equals()
gets called because i process all items in the dictionary as the items are the result of the DB query whereas these results where processed before the dictionary approach anyway.