【问题标题】:Faster alternative than Dictionary<Type, X>?比 Dictionary<Type, X> 更快的替代方案?
【发布时间】:2012-05-21 16:08:32
【问题描述】:

我正在创建一个用于性能测试的库。在其中我生成一个Dictionary&lt;Type, X&gt; 一次。这些项目当前以随机顺序插入。字典在应用程序生命周期内保持不变。

然后它经常用于查找项目。查找是库中较大的瓶颈之一。

是的,我正在进行微优化,但要学习。我想知道是否有更好的方法来获得查找性能?

更新

我使用 dotTrace 来衡量性能。报告 + dotTrace 在我家的电脑里,所以我这里没有报告(本来可以在其他地方上传的)。

我使用了这里找到的测试: https://github.com/danielpalme/IocPerformance

字典定义见这里:https://github.com/jgauffin/Griffin.Container/blob/master/Source/Griffin.Container/ContainerBase.cs

(我上周五创建了容器,不要期望太多)

更新2

Dictionary.TryGetValue 总共占用了 Resolve 的 101 毫秒(总共 251 毫秒),如果我正确解释了数字,则为 40.2%。

【问题讨论】:

  • a:数据中有多少类型/对,b:是什么让您认为它是瓶颈? (即你是如何测量的?我们可以看到任何查找代码吗?)
  • 和 c: 调用者是使用静态类型(即int 等),还是由于反射而使用Type 对象?如果调用者静态知道类型,则可以使用一些技巧
  • @MarcGravell :您能否对这些技巧(答案或链接)进行一些扩展,因为它是一个有趣的主题。谢谢
  • @mathieu 特别是,我在想一个通用的静态类型,即SomeCache&lt;int&gt;.Whatever(...),带有一个静态初始化器 (.cctor) 来准备每个 T 的元数据。
  • @MarcGravell:阅读我的更新。

标签: c# performance dictionary inversion-of-control


【解决方案1】:

IoC 容器的性能基准来自 Daniel Palme(但其他人也一样)有点误导,因为基准从容器中解析了非常浅的对象图(尽管它确实清楚地表明容器之间的性能差异很大)。这是不现实的,因为大多数应用程序(正确使用 DI)将具有包含对象数量的对象图。执行此操作时,只需要解析根对象,并且当容器编写正确时,这意味着在大多数情况下(或最多只有几个)每个(网络)请求您只需调用一次 Dictionary&lt;T,V&gt;.TryGetValue .因此,Dictionary&lt;T, V&gt; 的性能并不是真正的问题。

我认为Dictionary&lt;T,V&gt; 的最大性能成本(其中TKeySystem.Type)与为给定Type 生成哈希码的性能成本有关。每次拨打TryGetValue,都必须拨打Type.GetHashCode()。 GetHashCode 是虚拟的(不能内联),并且该方法调用 3 个其他虚拟方法。最后对RuntimeHelpers.GetHashCode(key)进行静态(外部)调用。

换句话说,您将能够优化性能以编写使用Type 作为键的特定(非通用)字典类,而不是调用Type.GetHashCode(),而应调用RuntimeHelpers.GetHashCode(key)

更新(2013-04-05):

几个月前,我尝试提高我维护的 DI 容器的性能,并尝试优化字典部分。我编写了自己的字典,直接调用RuntimeHelpers.GetHashCode(key)(并跳过了虚拟调用),但最终性能提升如此之小(在 Palme 的基准测试中约为 1%),我决定恢复这些代码更改。所以我目前的理解是Dictionary&lt;Type, X&gt; 的真正性能成本实际上是RuntimeHelpers.GetHashCode(key) 内部发生的一切。

【讨论】:

  • 使用 Dictionary 并将 Type.FullName 保存为键不是更好的方法吗?这样就不会调用 Type.GetHashCode()。
  • 这是一个有根据的猜测,但我假设使用 String 作为键要慢得多,因为 how hashes are calculated 在字符串中,并且因为 String.GetHashCode() 的性能与长度成线性关系字符串。
  • 实际上调用Type.FullName 也有开销。
  • 谢谢史蒂文!所以我会继续使用 Dictionary :)
【解决方案2】:

如果类型是编译时定义的,你可以尝试这样实现:

static class ValueFor<T>
{
  // you get another field per type T
  public static X X { get; private set; }

  internal static SetUpValue(X value) { X = value; }
}

使用这个访问即可。

var myIntX = ValueFor<int>.X;

【讨论】:

  • 很遗憾没有。映射由用户/开发人员在运行时创建(对于所有 IoC 容器)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-12-11
  • 2012-07-05
  • 2012-01-23
  • 2013-07-13
  • 2011-02-27
  • 2010-09-22
相关资源
最近更新 更多