【问题标题】:Abysmal performance with Dictionary that has a KeyValuePair as key (C# .NET)以 KeyValuePair 为键的 Dictionary 性能极差(C# .NET)
【发布时间】:2016-02-15 05:38:57
【问题描述】:

在我正在编写的应用程序中,我需要将两组潜在的大型数据相互映射。一个是从 Web 服务返回的 List,一个是 DataTable。我需要为列表中的每个项目获取 ANSI(或 ISO)编号,并找到包含该 ANSI 编号的 DataTable 行,然后对其进行处理。

由于 DataTable.Select 非常慢,我必须对 List 中的每个项目都这样做,所以我尝试了更快的替代方案。请记住,DataTable 对象没有数据库。所以我不能利用任何 SQL 功能或类似的东西。

我认为最快的方法可能是使用 KeyValuePair(A:Ansi 编号或 I:Iso 编号)创建字典并将其用作键。该值将是该行的其余部分。创建该字典显然需要一点处理时间,但随后我可以利用字典极快的搜索时间来查找我需要的每一行,然后将这些行添加回表中。因此,在 foreach 循环中,我只会使用字典而不是 O(n) 或 DataTable.Select 具有的 O(1) 复杂度。

令我惊讶的是,字典的速度非常慢。我不知道为什么,直到我发现使用字符串(只是 ANSI 数字)而不是 KeyValuePair 可以显着提高性能。我说话的速度快了几百倍。这怎么可能?这是我的测试方法:

我生成了一个模拟 Web 服务输出的列表。我基于该列表创建一个字典,其中包含一个键(字符串或 KeyValuePair)和 DataRow 作为值。我遍历该列表的 foreach 循环,并在我的字典中搜索该列表中的每个项目,然后为返回的 DataRow 分配一个值。就是这样。

如果我使用 KeyValuePair 作为访问字典的键,则访问 1,000 个项目需要几秒钟,如果我修改字典以仅将字符串作为键,则访问 10,000 个项目需要几毫秒。仅供参考:我设计了测试,以便总是有命中,所以总能找到所有键。

这是我正在测量时间的代码块:

foreach(ProductList.Products item in pList.Output.Products)
{
   //KeyValuePair<string, string> kv = new KeyValuePair<string, string>("A", item.Ansi);
   DataRow row = dict[item.Ansi];
   for (int i = 0; i < 10; i++)
   {
      row["Material"] = item.Material + "a"; //Do stuff just for debugging
   }
   hits++;
}

那么,如果我使用 Dictionary(KeyValuePair,DataRow) 而不是 Dictionary(String,DataRow),执行时间怎么可能突然变长数百倍?

【问题讨论】:

  • SO 上的人过去不那么挑剔。我敢打赌,对这个问题投反对票的人在按下不赞成按钮之前没有阅读过它。见鬼的人,看在上帝的份上,不要再这样做了……
  • 不同的问题,但相同的答案,请看stackoverflow.com/a/251619/18797

标签: c# .net dictionary


【解决方案1】:

KeyValuePair&lt;TKey, TValue&gt; 没有实现GetHashCode() 方法。这意味着有意义地组织字典的唯一方法已经不复存在,剩下的就是低效的线性搜索。

这不足为奇,因为它不是 KeyValuePair&lt;TKey, TValue&gt; 的设计目的 - 它是字典使用的内部结构,而不是键。 .NET 对象不需要是有用的键,从所有 GetHashCode() 调用返回 0 是完全有效的。

如果您不想使用自己的结构,请使用Tuple。但我真的会为任何类型的持久性创建自己的结构,真的。

作为旁注,DataTable.Select 实际上对于它的设计目的来说非常快 - 过滤数据以输出。但是,它并不是真正为在循环中被调用数百次而设计的——开销占主导地位。当然,这假设您有适当的索引。在您的情况下,我认为每次调用Select 时都会重新生成索引,这有点慢:)

【讨论】:

  • 元组的哈希值也很差。
  • 确实,创建您自己的结构并对其进行散列(通常只需返回最独特属性的散列就足够了)。
  • @Frisbee [需要引用]。当然,如果您知道哪些偏差对您有用,则有更好的哈希方法,但对于非结构化数据,Tuple 效果很好。
  • @Frisbee Tuple 的性能不佳是因为当与值类型一起使用时,它会在每次 GetHashCodeEquals 调用时将它们装箱。一些天才决定使用EqualityComparer&lt;object&gt;.Default 而不是EqualityComparer&lt;T&gt;.Default。见stackoverflow.com/questions/21084412/…
  • @Lukazoid 我不是在编造这个。元组可以很差地散列。 stackoverflow.com/questions/12657348/…
【解决方案2】:

您可能会遇到大量与键值对的哈希冲突。您可以使用 GetHashCode 进行测试。

下面的链接是元组,但我高度怀疑你对键值对有同样的事情。 gethashcode-high-rate-of-duplicates 我会标记为重复,但你们很多人还有其他事情要做。

在此link 中,Microsoft 建议不要对键使用值类型。 KVP 的 GetHashCode 继承自值类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-05-21
    • 2011-08-13
    • 1970-01-01
    • 1970-01-01
    • 2016-05-07
    • 1970-01-01
    • 2019-07-31
    • 1970-01-01
    相关资源
    最近更新 更多