C# 与平面数组相比,.Net中的简单字典查找速度较慢

C# 与平面数组相比,.Net中的简单字典查找速度较慢,c#,performance,C#,Performance,我发现与平面阵列访问相比,字典查找可能非常慢。知道为什么吗?我正在使用Ants Profiler进行性能测试。下面是重现问题的示例函数: private static void NodeDisplace() { var nodeDisplacement = new Dictionary<double, double[]>(); var times = new List<double>(); for (int

我发现与平面阵列访问相比,字典查找可能非常慢。知道为什么吗?我正在使用Ants Profiler进行性能测试。下面是重现问题的示例函数:

    private static void NodeDisplace()
    {
        var nodeDisplacement = new Dictionary<double, double[]>();

        var times = new List<double>();
        for (int i = 0; i < 6000; i++)
        {
            times.Add(i * 0.02);
        }
        foreach (var time in times)
        {
            nodeDisplacement.Add(time, new double[6]);
        }

        var five = 5;
        var six = 6;
        int modes = 10;
        var arrayList = new double[times.Count*6];
        for (int i = 0; i < modes; i++)
        {
            int k=0;
            foreach (var time in times)
            {
                for (int j = 0; j < 6; j++)
                {

                    var simpelCompute = five * six;  // 0.027 sec
                    nodeDisplacement[time][j] = simpelCompute;  //0.403 sec
                    arrayList[6*k+j] = simpelCompute;  //0.0278 sec
                }

                k++;
            }
        }
    }
private static void NodeDisplace()
{
var nodeDisplacement=新字典();
var times=新列表();
对于(int i=0;i<6000;i++)
{
加上(i*0.02);
}
foreach(以时间为单位的var时间)
{
nodeDisplacement.Add(时间,新的双精度[6]);
}
变量5=5;
var 6=6;
int模式=10;
var arrayList=new double[times.Count*6];
对于(int i=0;i
注意平面数组访问和字典访问之间的相对大小?考虑到数组索引操作(
6*k+j
),平面数组的速度大约是字典访问速度(0.403/0.0278)的20倍


听起来很奇怪,但字典查找占用了我大部分时间,我必须优化它

是的,我一点也不惊讶。字典的意义在于它们被用来查找任意键。考虑单个数组引用的发生:

  • 检查边界
  • 将索引乘以元素大小
  • 向指针添加索引
非常非常快。现在进行字典查找(非常粗略;取决于实现):

  • 可能会检查密钥是否为空
  • 获取密钥的哈希代码
  • 为该哈希代码找到正确的插槽(可能是“mod prime”操作)
  • 可能取消对数组元素的引用以查找该插槽的信息
  • 比较散列码
  • 如果哈希代码匹配,比较是否相等(并可能继续进行下一个哈希代码匹配)
如果您有“键”,可以很容易地用作数组索引(例如,连续整数,或者可以很容易地映射到连续整数),那么这将是非常非常快的。这不是哈希表的主要用例。它们适用于无法通过这种方式轻松映射的情况-例如,通过字符串或任意
double
值查找(而不是等距的双精度,因此可以轻松映射为整数)

我想说你的标题有误导性——不是字典查找速度慢,而是当数组是一种更合适的方法时,它们的速度快得离谱。

此外,我想补充一点,你的内部循环做得不多,通常,您在内部循环中至少多做一些工作,然后字典的相对性能损失就会有所降低

如果查看Reflector中的
Double.GetHashCode()
代码,您会发现它正在执行4行代码(假设Double不是0),只是这比内部循环的主体要多
Dictionary.Insert()
(由集合索引器调用)是更多的代码,几乎满屏

与平面数组相比,Dictionary的优点在于,当键不密集(就像您的情况一样)并且读写类似于~O(1)数组(但具有更高的常数)时,不会浪费太多内存

作为旁注,您可以使用多维数组而不是
6*k+j
技巧。
这样说吧

var arrayList = new double[times.Count, 6];
arrayList[k ,j] = simpelCompute;
这样使用它

var arrayList = new double[times.Count, 6];
arrayList[k ,j] = simpelCompute;

它不会更快,但更易于阅读。

但索引访问通常比哈希查找更快。。。“你说奇怪是什么意思?”米奇用蚂蚁量了量Profiler@Nick,但有20倍的不同?@Ngu:当然。还有很多工作要做。阵列访问是CPU的绝对天赋——它几乎不需要做任何事情。你不是在拿苹果和桔子做比较吗?用于字典和数组访问的索引不同。当然,使用整数索引访问数组比使用任意类型(
double
)访问字典要快。与在其他代码中只进行一次数组访问相比,您的字典访问会进行额外的数组访问。因子20听起来不错,因为数组访问基本上不需要任何成本。我使用的是均匀间隔的
double
(参见上面的代码),但它仍然很慢。@Ngu:这是我的观点-您使用的范围很容易映射到整数,所以使用数组代替。为作业使用最合适的数据结构—在本例中为数组。