【问题标题】:Another 2 Dimensional Array Allocation Problem c#另一个二维数组分配问题 c#
【发布时间】:2011-05-22 15:12:08
【问题描述】:

我想弄清楚为什么“选择 A”比“选择 B”表现更好。我的测试显示类似 228 与 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();
        }
    }
}

【问题讨论】:

  • 我认为检测代码有点可疑,但我希望 A 性能更好,因为它对缓存更友好。 stackoverflow.com/questions/763262/cache-efficient-code/…
  • 在我的机器上运行相同的代码示例。选项 B 仅比选项 A 稍慢。选项 A = 1563 毫秒 选项 B = 2121 毫秒
  • 哦,伙计,我几年前就看到过这个问题,但我不记得答案了。我认为这是因为第二个在每次迭代时都会创建一个新维度,而第一个已经有了第一个维度。
  • @Joseph:这两个选项之间的差异幅度可能与处理器高度相关。
  • @Phill:this 可能是您正在寻找的类似问题吗?和this 博客条目作为该帖子的反映。尽管在我看来,该示例表明选择 B 应该更快...

标签: c# arrays


【解决方案1】:

据推测,缓存效果将是这里的主要影响。

一个二维数组在内存中的布局如下:

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

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

【讨论】:

  • 尺寸是通过指定 [SIZE, SIZE] 来预先确定的,不是吗?
  • 但是缓存的大小从一个 CPU 变化到另一个。 IE。我的 1Mb 缓存差异约为 8 倍(350 与 2888 毫秒)。
【解决方案2】:

啊,我想我记得了。

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

[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

在行优先存储中,一个 线性多维数组 内存被访问,使得行 一个接一个地存储。

因此,当一次迭代一行时,它不会在内存中跳转,寻找下一行以将值分配给该列,它拥有该行,分配所有列,然后跳转到内存中的下一行。

【讨论】:

    【解决方案3】:

    扩展缓存答案:

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

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

    【讨论】:

      猜你喜欢
      • 2018-09-03
      • 1970-01-01
      • 2022-11-27
      • 1970-01-01
      • 2012-10-29
      • 2021-01-02
      • 1970-01-01
      • 2017-09-28
      相关资源
      最近更新 更多