我已经测试了使用(int,int,string)元组作为键检索,更新和删除字典中的值的速度与使用嵌套字典:Dictionary>>。
我的测试显示元组字典要慢很多(58%用于检索,69%用于更新,200%用于删除)。我没想到的是。嵌套字典需要进行更多查找,那么为什么元组字典要慢得多呢?
我的测试代码:
public static object TupleDic_RemoveValue(object[] param)
{
var dic = param[0] as Dictionary<(int did, int eid, string name), string>;
var keysToRetrieve = param[2] as List<(int did, int eid, string name)>;
foreach (var key in keysToRetrieve)
{
dic.Remove(key);
}
return dic;
}
public static object NestedDic_RemoveValue(object[] param)
{
var dic = param[1] as Dictionary<int, Dictionary<int, Dictionary<string, string>>>;
var keysToRetrieve = param[2] as List<(int did, int eid, string name)>;
foreach (var key in keysToRetrieve)
{
if (dic.TryGetValue(key.did, out var elementMap) && elementMap.TryGetValue(key.eid, out var propertyMap))
propertyMap.Remove(key.name);
}
return dic;
}
测试的额外信息: 该词典共包含10 000个条目。键递增:([0-100],[0-100],&#34;属性[0-100]&#34;)。 在单个测试中,检索100个密钥(其中10%不存在于字典中),100个值被更新(其中10%是新的)或100个密钥被删除(其中10%不在字典中以开始用)。检索,更新和删除是3个单独的测试。每个测试执行1000次。我比较了平均和中位执行时间。
答案 0 :(得分:5)
Dictionary
中的查找依赖于两件事。第一个是项目的哈希码,用于将项目分成桶。两个不同的键可以具有相同的哈希码,因此一旦找到潜在匹配,就会针对每个项目(使用该哈希码)调用Equals
,直到找到完全匹配。
ValueTuple
的哈希码实现(对于arity-2 + *)将元组中每个项的Equality Comparer.Default<T>.GetHashCode
结果传递给内部方法ValueTuple.CombineHashCodes
,后者又调用System.Numerics.Hashing.HashHelpers.Combine
。元组中的项目越多,对Combine
两种方法的嵌套调用就越多。将其与普通int
的{{1}}进行比较,后者只是直接返回值。
对我来说,后一个例子会更快。正如评论中指出的那样,您也在削减必要的数据以搜索较小的分区。每次查询都必须调用GetHashCode
并在找到潜在匹配后GetHashCode
。在我看来,第一种情况下哈希冲突的可能性更高,这意味着对Equals
的更多调用(在这种情况下,对于元组中的每个项目只调用Equals
)
最后归结为分析(更确切地说,分析正确 - 发布模式,调整调用,足够的迭代等)以及您的特定用例。
如果性能确实在您的用例中很重要(例如,在紧密循环中查找),或许最好使用您自己的类型和哈希代码/等于实现而不是EqualityComparer<T>.Default.Equals
。但同样,它归结为剖析。
*请注意,1-arity元组有一个特殊情况。