c# - 多维数组初始化器性能低下

标签 c# arrays performance .net-4.0 multidimensional-array

我有一些我无法解释的奇怪的性能结果。 好像是这一行

d = new double[4, 4]{{1, 0, 0, 0},
                     {0, 1, 0, 0},
                     {0, 0, 1, 0},
                     {0, 0, 0, 1},};

比这个慢4倍

d = new double[4, 4];
d[0, 0] = 1; d[0, 1] = 0; d[0, 2] = 0; d[0, 3] = 0; 
d[1, 0] = 0; d[1, 1] = 1; d[1, 2] = 0; d[1, 3] = 0;
d[2, 0] = 0; d[2, 1] = 0; d[2, 2] = 1; d[2, 3] = 0;
d[3, 0] = 0; d[3, 1] = 0; d[3, 2] = 0; d[3, 3] = 1;

(这甚至没有考虑在这个例子中我可以省略所有那些 = 0 赋值的事实)

我知道由于边界检查,在 C# 中循环遍历多维数组可能会很慢。但是这里没有循环,不需要边界检查,整个数组初始化行可以在编译时解析。

然而,第二个代码块必须首先将数组初始化为零,然后分别覆盖每个值。
那么这里的问题是什么?

如果性能有问题,初始化这个数组的最佳方法是什么?


我使用以下代码来衡量性能:

using System;
using System.Diagnostics;
class Program
{
    public static double[,] d; // global static variable to prevent the JIT optimizing it away

    static void Main(string[] args)
    {
        Stopwatch watch;
        int numIter = 10000000; // repeat all tests this often

        double[,] d2 = new double[4, 4]{{1, 0, 0, 0},
                                        {0, 1, 0, 0},
                                        {0, 0, 1, 0},
                                        {0, 0, 0, 1},};

        // ================================================================
        // use arrayInitializer: slowest
        watch = Stopwatch.StartNew();
        for (int i = 0; i < numIter; i++)
        {
            d = new double[4, 4]{{1, 0, 0, 0},
                                {0, 1, 0, 0},
                                {0, 0, 1, 0},
                                {0, 0, 0, 1},};
        }
        Console.WriteLine("ArrayInitializer: \t{0:0.##########}ms", watch.ElapsedMilliseconds * 1.0 / numIter);

        // ================================================================
        // use Array.Copy: faster
        watch = Stopwatch.StartNew();
        for (int i = 0; i < numIter; i++)
        {
            d = new double[4, 4];
            Array.Copy(d2, d, d2.Length);
        }
        Console.WriteLine("new + Array.Copy: \t{0:0.##########}ms", watch.ElapsedMilliseconds * 1.0 / numIter);

        // ================================================================
        // direct assignment: fastest
        watch = Stopwatch.StartNew();
        for (int i = 0; i < numIter; i++)
        {
            d = new double[4, 4];
            d[0, 0] = 1; d[0, 1] = 0; d[0, 2] = 0; d[0, 3] = 0; 
            d[1, 0] = 0; d[1, 1] = 1; d[1, 2] = 0; d[1, 3] = 0;
            d[2, 0] = 0; d[2, 1] = 0; d[2, 2] = 1; d[2, 3] = 0;
            d[3, 0] = 0; d[3, 1] = 0; d[3, 2] = 0; d[3, 3] = 1;
        }
        Console.WriteLine("direct assignment: \t{0:0.##########}ms", watch.ElapsedMilliseconds * 1.0 / numIter);
    }
}

结果:

ArrayInitializer:       0,0007917ms
new + Array.Copy:       0,0002739ms
direct assignment:      0,0002281ms

最佳答案

这里很好地解释了数组初始值设定项以及为什么您会看到如此不同的结果:http://bartdesmet.net/blogs/bart/archive/2008/08/21/how-c-array-initializers-work.aspx

基本上 - 数组初始化程序涉及创建自定义结构,而直接分配每个项目只是在堆栈中直接分配,但速度更快。

关于c# - 多维数组初始化器性能低下,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16019160/

相关文章:

performance - 对于新的 XPage 开发人员,您最喜欢的 3 个 XPage 性能提示是什么?

android - 如何使用 Retrofit2 将照片上传到服务器

c# - ProxyFactoryFactory 未配置

c# - 在 C# 中处理跨线程事件的最佳方法是什么

c# - ASP NET 5 本地化在单独的程序集中使用 View 组件

c# - ListBoxItem 边框宽度的拉伸(stretch)

python - 在没有循环的情况下在 Numpy 中切片一维数组

c++ - google::dense_hash_map 与 boost::unordered_map 性能问题

c - 从 double 组中删除一列

javascript - Node.js v8.0.0 - 使用关键字搜索数组