【问题标题】:c++ 2d array access speed changes based on [a][b] order? [duplicate]c++ 2d数组访问速度根据[a][b]顺序变化? [复制]
【发布时间】:2012-10-17 02:04:58
【问题描述】:

可能重复:
Why is my program slow when looping over exactly 8192 elements?

我一直在修改我用来简单地对二维数组的元素求和的程序。一个错字导致了至少在我看来,一些非常奇怪的结果。

处理数组时,矩阵[SIZE][SIZE]:

for(int row = 0; row < SIZE; ++row)
    for(int col = 0; col < SIZE; ++col)
        sum1 += matrix[row][col];

运行非常快,但是上面的行 sum1... 被修改了:

sum2 += matrix[col][row]

正如我曾经在没有意识到的情况下发生的那样,我注意到我的运行时间显着增加。这是为什么呢?

【问题讨论】:

  • 缓存位置。
  • 从不将带有数组和循环的 FORTRAN 代码逐字翻译成 C/C++!

标签: c++ arrays multidimensional-array


【解决方案1】:

这是由于您的程序的缓存行为。

数组只是连续的内存块,因此当您访问 [row][column] 时,您正在按顺序访问内存。这意味着您正在访问的数据页面在同一页面上,因此访问速度要快得多。

当您执行 [column][row] 时,您不再按顺序访问该内存,因此最终会出现更多缓存未命中,因此您的程序运行速度会慢得多。

【讨论】:

    【解决方案2】:

    这是因为在更快的情况下,CPU 的内存预取实际上很有用,因为您以线性方式进行迭代。在较慢的情况下,您会在内存中跳跃,因此预取几乎没有效果,因为数据不太可能在缓存中。

    【讨论】:

      【解决方案3】:

      这取决于矩阵的排序方式。您正在以行优先或列优先访问数组。取决于它在内存中的存储方式,两者之间的速度会有所不同

      【讨论】:

        【解决方案4】:

        matrix[row][col]matrix[row][col + 1]的内存位置是相邻的。

        matrix[row][col]matrix[row + 1][col] 的内存位置由 SIZE 个项目分隔。

        计算机喜欢顺序而不是随机访问内存,因此相邻访问更快。打个比方想想硬盘的性能,顺序读/写总是比随机读/写好。这与您的 CPU 如何缓存内存并尝试预测您接下来需要什么有关。

        【讨论】:

        • 建议的类比非常令人困惑,因为 HDD 不是 RAM 而 OP 是!缓存显然是答案。
        • 机械硬盘顺序读取速度很快,因为物理读取磁头必须移动较少; RAM 顺序读取速度很快,因为 CPU 缓存未命中率较低。机械硬盘随机读取很慢,因为物理读取头必须移动更多; RAM 随机读取很慢,因为有更多的 CPU 缓存未命中。在这个类比中,比较了不完全/字面上相同的 SLOW 和 FAST 事物。 OP 可能不知道 CPU 缓存是如何工作的,因此与 HDD Sequential == FAST & Random == SLOW 进行比较可能有助于理解这个概念。希望这可以为您解释@MikeSiomkin
        • 我不同意。在 DBMS 中为主键生成 RANDOM uuid 通常比使用 SEQUENTIAL 自动增量整数计数器慢。我们是否也应该在此处添加此示例?这些情况与澄清为什么更改索引顺序会更改执行时间无关。他们只声明其他随机事物也可能比它们的顺序对应物慢。更糟糕的是,他们试图以自己(不同)的方式解释这种影响。很混乱!
        【解决方案5】:

        二维数组只是指向指针的指针。所以它看起来像

        [*p][*p][*p]
          |   |   |
          v   v   v
         [d] [d] [d]
         |a| |a| |a|
         |t| |t| |t|
         [a] [a] [a]
        

        因此,当您在非主数组(此指针所指示的内容)上调用数据时,您的操作系统会将其放入 CPU 缓存中。

        【讨论】:

        • 二维数组不是指向指针的指针。数组不是指针,它是一个数组。二维数组是数组的数组,如果您尝试将其传递给采用Type ** 的函数,它将失败,因为它会衰减为指向数组的指针,而不是指向指针的指针。
        • @chris: 好的,你能告诉我为什么你可以打电话给a[5]a+55+a5[a] 并且是一样的吗?或者当您输入动态二维数组时,您输入int** ary = new int*[size]; 并在循环中输入ary[i] = new int[size];?数组是一块内存,数组 var 是指向第一个元素的指针,那为什么我不能告诉数组是一个指针呢?
        • 您的第一组示例有效,因为它衰减为指针。 new[] 返回一个指针,因此那里没有真正的类型冲突。您可以通过一个简单的示例证明数组不是指针:int array[100]; int *pointer = new int[100]; std::cout &lt;&lt; sizeof array &lt;&lt; ' ' &lt;&lt; sizeof pointer; 您会注意到两个输出之间存在很大差异,尽管它们都有 100 个元素。
        • @chris:当你把这个数组发送给函数时会发生什么?它会起作用吗?为什么不?而且你的论点是无效的,因为 sizeof 有不同的实现。顺便说一句,为什么这种方法只适用于静态数组?因为它是在编译期间计算的。
        • 如果你有一个int array[4][5]并且你把它发送到一个函数中,它会衰减成一个int (*)[5],它不能被转换成一个int **。它是一个指针,用作数组,指向 5 个整数的数组。如果您要调用参数arg++arg 会将其向前移动一个完整的 5 个整数数组。无论如何,无论sizeof 是什么样的,它都会是一致的:liveworkspace.org/code/4604791a54d28b8a2f3e81a870d41c75。您可能会发现这很有用:stackoverflow.com/questions/4810664/how-do-i-use-arrays-in-c
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-10-01
        • 2012-03-30
        • 2015-06-12
        • 2020-07-17
        • 2023-01-27
        • 2021-10-04
        相关资源
        最近更新 更多