【问题标题】:High Runtime for Dictionary.Add for a large amount of items字典的高运行时间。为大量项目添加
【发布时间】:2010-05-05 11:07:51
【问题描述】:

我有一个 C# 应用程序,它将来自 TextFile 的数据存储在 Dictionary-Object 中。要存储的数据量可能相当大,因此插入条目需要大量时间。由于字典中的许多项目,它变得更糟,因为内部数组的大小调整,存储字典的数据。 所以我用将要添加的项目数量来初始化 Dictionary,但这对速度没有影响。

这是我的功能:

private Dictionary<IdPair, Edge> AddEdgesToExistingNodes(HashSet<NodeConnection> connections)
{
  Dictionary<IdPair, Edge> resultSet = new Dictionary<IdPair, Edge>(connections.Count);

  foreach (NodeConnection con in connections)
  {
    ...
    resultSet.Add(nodeIdPair, newEdge);
  }

  return resultSet;
}

在我的测试中,我插入了大约 30 万个项目。 我使用 ANTS Performance Profiler 检查了运行时间,发现当我使用所需大小初始化 Dictionary 时,resultSet.Add(...) 的平均时间没有改变。这和我用 new Dictionary(); 初始化 Dictionary 时一样。 (每个添加平均约 0.256 毫秒)。 这肯定是由字典中的数据量引起的(尽管我用所需的大小对其进行了初始化)。对于前 20k 项,添加的平均时间为每项 0.03 毫秒。

任何想法,如何使添加操作更快?

提前致谢, 弗兰克

这是我的 IdPair-Struct:

public struct IdPair
{
  public int id1;
  public int id2;

  public IdPair(int oneId, int anotherId)
  {
    if (oneId > anotherId)
    {
      id1 = anotherId;
      id2 = oneId;
    }
    else if (anotherId > oneId)
    {
      id1 = oneId;
      id2 = anotherId;
    }
    else
      throw new ArgumentException("The two Ids of the IdPair can't have the same value.");
  }
}

【问题讨论】:

  • 您是否在您的IdPair 类中覆盖EqualsGetHashCode?如果是这样,您的GetHashCode 算法是否会产生良好的散列分布?
  • IdPair 只是一个带有构造函数的结构。我将其添加到我的问题中

标签: c# dictionary


【解决方案1】:

由于你有一个结构,你会得到 Equals() 和 GetHashCode() 的默认实现。正如其他人指出的那样,这不是很有效,因为它使用反射,但我认为反射不是问题。

我的猜测是,默认 GetHashCode() 会导致您的哈希码分布不均,例如,如果默认实现返回所有成员的简单 XOR(在这种情况下 hash(a, b) ==哈希(b,a))。我找不到任何有关如何实现 ValueType.GetHashCode() 的文档,但请尝试添加

public override int GetHashCode() {
    return oneId << 16 | (anotherId & 0xffff);
}

这可能会更好。

【讨论】:

  • 完美的猜测!您的小哈希函数将每个 Add 的操作时间平均缩短到 ~ 0.02 毫秒。
【解决方案2】:

IdPair 是一个struct,并且您还没有覆盖EqualsGetHashCode。这意味着将使用这些方法的默认实现。

对于值类型,EqualsGetHashCode 的默认实现使用反射,这可能会导致性能不佳。尝试提供您自己的方法实现,看看是否有帮助。

我建议的实现,它可能不是您需要/想要的:

public struct IdPair : IEquatable<IdPair>
{
    // ...

    public override bool Equals(object obj)
    {
        if (obj is IdPair)
            return Equals((IdPair)obj);

        return false;
    }

    public bool Equals(IdPair other)
    {
        return id1.Equals(other.id1)
            && id2.Equals(other.id2);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            int hash = 269;
            hash = (hash * 19) + id1.GetHashCode();
            hash = (hash * 19) + id2.GetHashCode();
            return hash;
        }
    }
}

【讨论】:

  • 非常感谢,卢克。 (标准)散列函数是问题所在。使用您的解决方案,我将每个 Add 的平均操作时间缩短到 ~0.03 ms。这比 erikkallens 解决方案慢一点,但比以前好得多。值得注意的是,事先设置 Dictionary 的大小似乎根本没有(时间)影响。
猜你喜欢
  • 1970-01-01
  • 2012-11-16
  • 1970-01-01
  • 1970-01-01
  • 2019-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多