【问题标题】:Help with C#.NET generic collections performance and optimization帮助 C#.NET 泛型集合性能和优化
【发布时间】:2010-12-04 02:39:27
【问题描述】:

我正在尝试优化一段 .NET 2.0 C# 代码,如下所示:

Dictionary<myType, string> myDictionary = new Dictionary<myType, string>();
// some other stuff
// inside a loop check if key is there and if not add element
if(!myDictionary.ContainsKey(currentKey))
{
   myDictionary.Add(currentKey, "");
}

看起来字典已经被编写这段代码的人使用了,即使不需要(只有键被用于存储唯一值列表),因为它比用于搜索的 myType 对象列表更快。 仅作为字典的键,这似乎显然是错误的,但我试图了解修复它的最佳方法是什么。

问题:

1) 我似乎明白,即使只使用 .NET 3.5 HashSet,我也会获得良好的性能提升。这是正确的吗?

2) 在 .NET 2.0 中优化上述代码的最佳方法是什么?为什么?

编辑: 这是我正在尝试优化的现有代码,它正在循环数以万计的项目,并且每个项目都在调用 ContainsKey。必须有更好的方法(即使在 .NET 2.0 中)! :)

【问题讨论】:

  • 您应该进一步完善您的编辑。你为什么要遍历成千上万的项目?你能并行化它吗?
  • 我正在从一堆文件中获取可能存在欺骗的对象列表 - 然后遍历这些项目并将它们添加到字典中丢弃欺骗

标签: c# .net performance generics dictionary


【解决方案1】:

scottm 提到的可能的性能下降不是用于进行简单的查找。它用于计算 2 个集合之间的交集。 HashSet 确实的查找速度比 Dictionary 稍快。不过,正如每个人所说的那样,性能差异确实会非常小——查找需要大部分时间,而创建 KeyValuePair 需要的时间很少。

对于 2.0,您可以将“值”对象设为以下之一:

public struct Empty {}

它可能比“”做得更好。

或者您可以尝试在您的 2.0 项目中引用 System.Core.dll,这样您就可以使用 HashSet。

此外,请确保 GetHashCode 和 Equals 对 MyType 尽可能有效。在 GetHashCode 非常慢的东西上使用字典时,我被咬了(我相信我们试图使用委托作为键或类似的东西。)

【讨论】:

    【解决方案2】:

    我可以看到的明显错误(如果我们讨论性能)是调用 ContainsKey 然后添加键值对时完成的双重工作。当使用 Add 方法添加该对时,将再次在内部检查密钥是否存在。整个 if 块可以安全地替换为:

    ... 我的字典 [currentKey] = ""; ...

    如果该键已经存在,则该值将被替换,不会抛出异常。此外,如果根本不使用该值,我个人会使用空值来填充它。看不出在那里使用任何字符串常量的理由。

    【讨论】:

      【解决方案3】:
      1. 根据您的密钥大小,您实际上可能会see performance degrade.

      2. 2.0 中的一种方法是尝试插入并捕获异常(当然,这取决于您计划拥有多少重复键:

        李>
      foreach(string key in keysToAdd)
      {
        try
        {
          dictionary.Add(key, "myvalue");
        }
        catch(ArgumentException) 
        {
          // do something about extra key
        }
      }
      

      【讨论】:

      • @divo,这取决于您要添加的密钥数量以及您是否计划复制。 ContainsKey() 方法每次都会迭代字典中的每个项目,因此随着字典的增长,“简单的 if”可能会很有吸引力。如果您不打算有重复项,那么您就没有将异常用于控制流,因为您不希望抛出异常。在这种情况下,它可能比 if 便宜。
      • 我期待被骗,我需要将它们过滤掉 - 捕获异常,即使使用它进行流量控制是错误的,听起来比每次调用 ContainsKey 更好(大多数项目不会'不是骗子),但它会提高性能吗?
      • 您应该听从 JaredPar 关于该问题的建议。如果这部分代码已经明显影响性能,您可以尝试更改,看看是否有任何收获。
      【解决方案4】:

      1) 不。字典对键进行哈希处理,因此您的查找应该是 O(1)。不过,Hashset 应该会减少所需的内存。但老实说,您真正看到的性能提升并没有那么多。

      2) 向我们提供有关您要完成的任务的更多详细信息。您发布的代码非常简单。你量过了吗?您是否看到这种方法很慢?不要忘记“我们应该忘记小的效率,比如说大约 97% 的时间:过早的优化是万恶之源。” ——唐纳德·克努斯

      【讨论】:

      • 谢谢 - 我知道过早的优化很糟糕。这是我正在尝试优化的现有代码,它正在循环数以千计的项目,并且每个项目都在调用 ContainsKey。必须有更好的方法来做到这一点! :)
      【解决方案5】:

      我认为您需要将其分解为 2 个问题

      Dictionary&lt;myType,string&gt; 是这种场景的最佳可用类型

      没有。根据您的细分,HashSet&lt;myType&gt; 显然是更好的选择,因为它的使用模式更适合场景

      切换到 Hashset&lt;myType&gt; 会提高性能吗?

      这真的很主观,只有分析器才能给你这个问题的答案。您可能会看到集合中每个元素的内存大小改进非常小。但就原始计算能力而言,我怀疑你会看到巨大的差异。只有分析器才能告诉您是否有。

      在您对代码进行性能相关的更改之前,请记住黄金法则。

      在分析器准确地告诉您代码有什么问题之前,不要进行任何与性能相关的更改。

      做出违反此规则的更改只是猜测。分析器是衡量性能修复成功与否的唯一方法。

      【讨论】:

      • @Rex,我把它放大了一点:)
      • +1 同意。最后一句是帖子最重要的部分
      • 如果这是 .NET 2.0,那么 Dictionary&lt;TKey, TValue&gt; 可能是最佳选择,因为 HashSet&lt;T&gt; 仅在 .NET 3.5 中添加。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-12
      • 1970-01-01
      • 2010-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多