为什么2D阵列访问比1D阵列访问更快?

时间:2016-11-22 17:08:40

标签: c++ arrays performance matrix vector

我有两个程序,用g ++编译并在linux上执行。程序1创建一个2D数组,然后测量访问所有元素100000次所需的时间:

#include <time.h>
#include <iostream>

int main()
{
  clock_t time;
  int i, y, x;

  int matrix[9][9]{{ 0,  1,  2,  3,  4,  5,  6,  7,  8},
                   { 9, 10, 11, 12, 13, 14, 15, 16, 17},
                   {18, 19, 20, 21, 22, 23, 24, 25, 26},
                   {27, 28, 29, 30, 31, 32, 33, 34, 35},
                   {36, 37, 38, 39, 40, 41, 42, 43, 44},
                   {45, 46, 47, 48, 49, 50, 51, 52, 53},
                   {54, 55, 56, 57, 58, 59, 60, 61, 62},
                   {63, 64, 65, 66, 67, 68, 69, 70, 71},
                   {72, 73, 74, 75, 76, 77, 78, 79, 80}};

  time = clock();

  for (i = 0; i < 100000; i++)
  {
    for (x = 0; x < 9; x++)
    {
      for (y = 0; y < 9; y++)
      {
        matrix[x][y];
      }
    }
  }

  time = clock() - time;
  std::cout << "Clicks:     " << time << std::endl;
  std::cout << "Time taken: " << (double) time / CLOCKS_PER_SEC << "s" << std::endl;
}

程序2创建一维数组,并测量访问所有元素100000次所需的时间:

#include <time.h>
#include <iostream>

int main()
{
  clock_t time;
  int i, j;

  int vector[81] = { 0,  1,  2,  3,  4,  5,  6,  7,  8,
                     9, 10, 11, 12, 13, 14, 15, 16, 17,
                    18, 19, 20, 21, 22, 23, 24, 25, 26,
                    27, 28, 29, 30, 31, 32, 33, 34, 35,
                    36, 37, 38, 39, 40, 41, 42, 43, 44,
                    45, 46, 47, 48, 49, 50, 51, 52, 53,
                    54, 55, 56, 57, 58, 59, 60, 61, 62,
                    63, 64, 65, 66, 67, 68, 69, 70, 71,
                    72, 73, 74, 75, 76, 77, 78, 79, 80};

  time = clock();

  for (i = 0; i < 100000; i++)
  {
    for (j = 0; j < 81; j++)
    {
      vector[j];
    }
  }

  time = clock() - time;
  std::cout << "Clicks:     " << time << std::endl;
  std::cout << "Time taken: " << (double) time / CLOCKS_PER_SEC << "s" << std::endl;
}

执行程序1后,我的输出是:

Clicks:     8106
Time taken: 0.008106s

执行程序2后,我的输出是:

Clicks:     15958
Time taken: 0.015958s

据我所知,1D数组存储在连续的内存块中。同样,静态2D阵列的行存储在连续的存储块中。相反,动态2d数组的行可能不会存储在连续的内存块中。如果这是真的那么程序2应该至少与程序1的速度相似,因此我的问题是为什么程序1会比程序2快得多?

2 个答案:

答案 0 :(得分:1)

编译器可能会删除循环(优化的种类),因为

  1. 你实际上没有在循环中做任何事。

  2. 可以将矩阵视为const数组。

  3. 程序1比程序2快。(:&lt;)

  4. 要查看在编译期间是否对代码进行了删除,可以将最外部循环增加100倍,并查看执行所需的时间是否显着增加(不一定是100次)。

    如果为true,你可以通过循环中的一些实际工作来防止这种优化(计算总和,不要忘记之后打印它)并对矩阵引入一些“不可预测的”更改,例如:

    srand(10);
    for (int i=0; i<9; ++i) {
      matrix[i][i] = rand()%100;
    }
    

    此外,编译器可能会对您的代码进行一些其他优化,例如,扩展您的循环,甚至是您正在访问的元素的地址(它们不再在运行时计算),您可以通过制作执行循环次数“不可预测”:

    #include <chrono>
    #include <iostream>
    #include <cstdlib>
    
    int array[100];
    int array2[10][10];
    
    int64_t Sum1D(int len) {
      int64_t sum = 0;
      for (int i=0; i<100000; ++i) {
        for (int j=0; j<len; ++j) {
            sum += array[j];
        }
      }
      return sum;
    }
    
    int64_t Sum2D(int len1, int len2) {
      int64_t sum = 0;
      for (int i=0; i<100000; ++i) {
        for (int j=0; j<len1; ++j) {
          for (int k=0; k<len2; ++k)
            sum += array2[j][k];
        }
      }
      return sum;
    }
    
    int main()
    {
      for (int i=0; i<100; ++i) {
        array[i] = rand();
        array2[i%10][i/10] = rand();
      }
    
      auto time = std::chrono::steady_clock::now();
    
      //int64_t sum = Sum1D(100);
      int64_t sum = Sum2D(10,10);
    
      auto duration = std::chrono::steady_clock::now()-time;
      std::cout << sum << "!" << duration.count() << std::endl;
    
      return 0;
    } 
    

    最终使program1比program2慢。 (:&gt;)

答案 1 :(得分:1)

这是我发现的:

  1. 如果您实际使用了该值,则运行时间几乎相同,例如,将matrix[x][y];更改为matrix[x][y] += 1;,将vector[j];更改为vector[j] += 1;

    >     Clicks:     28519
    >     Time taken: 0.028519s
    

    >     Clicks:     29941
    >     Time taken: 0.029941s
    
  2. 如果没有上述更改,请在编译时优化g++ -O3 <filename>.cpp,这会导致同一时间,两个程序的输出结果相同:

      

    $。/ a.out

    >     Clicks:     2
    >     Time taken: 2e-06s
    
  3. 所以,你指出的是编译器优化。