【问题标题】:Horizontal Vs Vertical browsing of array数组的水平与垂直浏览
【发布时间】:2010-02-12 12:22:17
【问题描述】:

让我们考虑两个几乎相同的代码:

第一个

for (int k=0;k<1000;k++)
{
    for (int i=0;i<600;i++)
    {
         for (int j=0;j<600;j++)
         {
              tab[i][j] = i *j;
         }
    }
 }

第二

for (int k=0;k<1000;k++)
{
    for (int i=0;i<600;i++)
    {
         for (int j=0;j<600;j++)
         {
              tab[j][i] = i *j;
         }
    }
 }

在第二个而不是 tab[i][j] 中,我们有 tab[j][i]。
第一个代码要快得多。

问题
为什么第一个代码要快得多?

我的直觉
是不是因为当程序试图访问一个单元格时,首先包含这个单元格的整个块被移动到缓存中,然后通过缓存访问它。由于内存中的数组由连续的单元格表示,因此在第一种情况下,在第一种情况下对内存的访问比在第二种情况下要少得多。

【问题讨论】:

    标签: arrays memory caching


    【解决方案1】:

    这是因为缓存局部性。处理器缓存行可以同时保存多个数组元素,但只能来自相邻地址。

    在第一种情况下,您有更多的缓存命中 - 当您遍历第二个数组索引时,您会访问相邻的元素。您访问某个元素,处理器将它及其邻居加载到缓存行中,下一个相邻的访问会产生缓存命中 - 您不再需要访问内存来处理它们。

    在第二种情况下,当您遍历加载某个元素的第一个索引时,会填充一行缓存,但下一次访问的是不在同一行中的元素。 Thie 使处理器将另一行加载到缓存中。如果缓存不能一次保存所有行,它必须丢弃以前加载的行并稍后重新加载它们。这大大增加了内存访问次数,从而增加了执行时间

    【讨论】:

      【解决方案2】:

      除了其他响应中正确识别的问题外,还有一个次要问题,即大多数现代 CPU 具有自动预取功能。当从顺序地址加载一定数量的高速缓存行时,将启动自动预取,并推测性地加载更多高速缓存行。如果因此消除了 DRAM 延迟的影响,这可能是一个巨大的性能提升。如果您不按顺序访问内存,那么您不会获得此好处,而且如果预取加载随后不需要的缓存行,它甚至可能适得其反。

      【讨论】:

        【解决方案3】:

        是的,你的理论是正确的。

        当访问整个数组的单个元素时,由于整个数组太大而无法放入缓存,因此必须将内存切换到缓存中。

        当您按顺序访问元素时,每个内存块只需进出缓存一次。此外,由于您只使用缓存中的最后一个块,因此可以在最方便的时候将之前的块写回内存。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-09-19
          • 2019-11-28
          • 2013-08-12
          • 1970-01-01
          • 2014-04-19
          • 2013-06-19
          • 1970-01-01
          相关资源
          最近更新 更多