比Dictionary<Type, X>更快的替代方案是什么?

15
我正在创建一个库,对其进行性能测试。我生成了一个 Dictionary<Type, X>。当前条目是以随机顺序插入的,这个字典在应用程序生命周期内保持不变。
它经常用于查找项,但查找是该库中较大的瓶颈之一。
是的,我在微观优化,但为了学习。我想知道是否有更好的方法来提高查找性能?
更新:
我使用 dotTrace 测量性能。报告和 dotTrace 在我的家用电脑上,所以我没有报告(否则可以上传到其他地方)。
我使用了这里找到的测试: https://github.com/danielpalme/IocPerformance 字典定义在这里: https://github.com/jgauffin/Griffin.Container/blob/master/Source/Griffin.Container/ContainerBase.cs 我在上周五创建了容器,不要期望太高。
更新2

Performance breakdown

Dictionary.TryGetValueResolve的总时间(251毫秒)中占用了101毫秒,如果我正确解释了这些数字,那么它占据了40.2%的时间。


4
a: 数据中有多少种类型/对? b: 你认为这是一个瓶颈的原因是什么?(即你是如何测量的?我们可以看到查找代码吗?) - Marc Gravell
2
调用者使用静态类型(比如 int 等)还是因为反射而使用 Type 对象?如果调用者在静态上下文中知道类型,那么有一些技巧可以使用。 - Marc Gravell
2
特别是@mathieu,我在考虑一个通用的静态类型,即SomeCache<int>.Whatever(...),带有静态初始化程序(.cctor)来准备每个T的元数据。 - Marc Gravell
@MarcGravell:请阅读我的更新。 - jgauffin
我刚刚进行了一个测试,使用字典和哈希表,无论哪种情况下,在200种查找类型的情况下,可以在< 100毫秒内完成2M个查找。这相当快。我不确定这是否是一个真正的问题……? - Marc Gravell
显示剩余6条评论
2个回答

2
IoC容器的性能基准来自Daniel Palme(以及其他人),但是这个基准解决了容器中非常浅的对象图(尽管它清楚地显示了容器之间的性能差异很大)。这是不现实的,因为大多数应用程序(正确使用DI的)将具有包含许多对象的对象图。在执行此操作时,只需要解析根对象,并且当容器编写正确时,这意味着您在大多数情况下(或者最多仅有一些情况下)每个(Web)请求只需调用一次Dictionary<T,V>.TryGetValue。因此,Dictionary<T, V>的性能并不是真正的问题。
我认为,TKeySystem.TypeDictionary<T,V>的性能成本的最大部分与为给定的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<Type, X>的实际性能成本实际上是发生在RuntimeHelpers.GetHashCode(key)内部的所有内容。

使用Dictionary<string, x>并将Type.FullName保存为键不是更好的方法吗?这样就不会调用Type.GetHashCode()了。 - gwt
这只是一个有根据的猜测,但我认为使用String作为键会慢得多,因为字符串的哈希计算方式以及String.GetHashCode()的性能与字符串长度成线性关系。 - Steven
调用 Type.FullName 实际上也会产生开销。 - Steven
谢谢Steven!那我会继续使用Dictionary<Type,x> :) - gwt

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容器一样)。 - jgauffin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接