字典与元组键比嵌套字典慢。为什么?

时间:2018-02-26 10:20:27

标签: c# .net performance dictionary

我已经测试了使用(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次。我比较了平均和中位执行时间。

1 个答案:

答案 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元组有一个特殊情况。

HashHelpers.Combine

ValueTuple

Int32.GetHashCode