另一个二维数组分配问题c#

时间:2010-12-09 03:49:11

标签: c# arrays

我想弄清楚为什么“选择A”表现得更好“选择B”。我的测试显示了228 vs 830或类似的东西,它就像是4倍的差异。看着IL,未经训练的眼睛不会在两次呼叫之间巧妙地挑选出来。

谢谢你, 斯蒂芬

const int SIZE = 10000;
void Main()
{
    var sw = Stopwatch.StartNew();

    int[,]A = new int[SIZE, SIZE];
    int total, x, y;

    // Choice A
    total = 0;
    for (x = 0; x < SIZE; x++)
    {
        for (y = 0; y < SIZE; y++)
        {
            total += A[x, y];
        }
    }

    Console.WriteLine(sw.ElapsedMilliseconds);

    sw.Reset();
    sw.Start();

    // Choice B
    total = 0;
    for (y = 0; y < SIZE; y++)
    {
        for (x = 0; x < SIZE; x++)
        {
            total += A[x, y];
        }
    }

    Console.WriteLine(sw.ElapsedMilliseconds);
}

// Define other methods and classes here

好吧,我把它打破了,以便它们可以彼此独立运行并减轻任何缓存和/或诊断......并且B总是在A背后进入

namespace ConsoleApplication1
{
    class ProgramA
    {
        const int SIZE = 10000;
        static void Main(string[] args)
        {
            var sw = Stopwatch.StartNew();

            int[,] A = new int[SIZE, SIZE];
            int total, x, y;

            // Choice A
            total = 0;
            for (x = 0; x < SIZE; x++)
            {
                for (y = 0; y < SIZE; y++)
                {
                    total += A[x, y];
                }
            }

            Console.WriteLine(sw.ElapsedMilliseconds);
            Console.ReadLine();
        }
    }

    class ProgramB
    {
        const int SIZE = 10000;
        static void Main(string[] args)
        {
            var sw = Stopwatch.StartNew();

            int[,] A = new int[SIZE, SIZE];
            int total, x, y;

            // Choice B
            total = 0;
            for (y = 0; y < SIZE; y++)
            {
                for (x = 0; x < SIZE; x++)
                {
                    total += A[x, y];
                }
            }

            Console.WriteLine(sw.ElapsedMilliseconds);
            Console.ReadLine();
        }
    }
}

3 个答案:

答案 0 :(得分:6)

猜测,缓存效果在这里会很重要。

二维数组如下所示放在内存中:

(0, 0) (0, 1) (0, 2) (0, 3) (1, 0) (1, 1) (1, 2) ...

在选项A中,您正在访问内存中的连续元素 - 这意味着当CPU获取缓存行时,它会获得几个连续的元素。选项B通过内存跳转。因此,一旦数组变得大于缓存大小,选项B就需要更多的内存访问。

答案 1 :(得分:1)

啊,我想我记得了。

如果您将2d数组视为内存中的表,则第一个值是行,第二个值是列。

[0,0] [0,1] [0,2] [0,3] ...... [1,0] [1,1] [1,2] [1,3] ...

当你迭代它时,第一个循环是行,第二个循环是列。通过执行foreach行迭代更快,分配每列。

在第二种情况下,它的值被指定为

[0,0] [1,0] [2,0] [3,0] ...... [0,1] [1,1] [2,1] [3,1] ......

所以这比较慢,因为你的循环,你正在分配foreach列,foreach行。您只为每一行分配第一列。

这有意义吗?

编辑:这是我要找的东西之一:

http://en.wikipedia.org/wiki/Row-major_order

  

在行主存储中,a   线性多维数组   访问内存使得行是   一个接一个地存储。

因此,当一次迭代一行时,它不会跳过内存寻找下一行来为列分配值,它有行,分配所有列,然后跳转到内存中的下一行。 / p>

答案 2 :(得分:0)

扩展缓存答案:

有问题的值是每个4字节,IIRC当前内存架构从内存中读取16字节行,假设主板已正确填充。 (我不知道DDR3,它的三芯片特性表明读取更大。)因此,当你读取一行内存时,你会得到4个值。

执行此操作时,首先使用所有这些值,然后再返回到下一行的内存。完成第二种方式,你只使用其中一种,然后在它再次被调用之前很久就从片上缓存中刷新。