【问题标题】:Dynamically allocated arrays or std::vector动态分配的数组或 std::vector
【发布时间】:2009-07-02 12:28:51
【问题描述】:

我正在尝试优化我的 C++ 代码。我在互联网上搜索了使用动态分配的 C++ 数组与使用 std::vector 的对比,并且通常看到有利于 std::vector 的建议,并且两者之间的性能差异可以忽略不计。例如这里 - Using arrays or std::vectors in C++, what's the performance gap?

但是,我编写了一些代码来测试遍历数组/向量并将值分配给元素的性能,我通常发现使用动态分配的数组比使用向量快近 3 倍(我确实为事先的向量)。我用的是g++-4.3.2。

但是我觉得我的测试可能忽略了我不知道的问题,所以我很感激关于这个问题的任何建议。

谢谢

使用的代码 -

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

using namespace std;

int main() {
  clock_t start,end;
  std::vector<int> vec(9999999);
  std::vector<int>::iterator vecIt = vec.begin();
  std::vector<int>::iterator vecEnd = vec.end();

  start = clock();
  for (int i = 0; vecIt != vecEnd; i++) {
    *(vecIt++) = i;
  }
  end = clock();
  cout<<"vector: "<<(double)(end-start)/CLOCKS_PER_SEC<<endl;

  int* arr = new int[9999999];
  start = clock();
  for (int i = 0; i < 9999999; i++) {
    arr[i] = i;
  }
  end = clock();
  cout<<"array: "<<(double)(end-start)/CLOCKS_PER_SEC<<endl;
}

【问题讨论】:

  • 是的,让我们看看你的基准代码。通常,STL 容器的性能问题是由于使用造成的。
  • 并确保在关闭调试和优化的情况下编译基准
  • 我刚刚运行了您的测试代码并得到:$ ./array vector: 0.021851 array: 0.059796 所以我看到矢量版本更快!
  • 编译时优化未开启。 std::vector 现在更快了。感谢您的帮助。
  • @Neil,我认为您的评论应该是公认的答案。也许你应该把它作为一个答案,这样我们就可以投票给它,Sid 可以接受它。 8v)

标签: c++ performance optimization stl


【解决方案1】:

在对 C++ 容器进行基准测试时,启用大多数编译器优化非常重要。我自己关于 SO 的几个答案都违反了这一点 - 例如,当像 operator[] 这样的东西没有被内联时,函数调用开销可能非常重要。

【讨论】:

  • 为什么下标运算符不尽可能内联?
  • @Catskul 由于优化被关闭。
  • 更不用说剩下的所有调试迭代器检查等
【解决方案2】:

只是为了好玩,尝试使用指针而不是整数索引对普通数组进行迭代(代码应该看起来像向量迭代,因为 STL 迭代器的点看起来像大多数操作的指针算术)。我敢打赌,在这种情况下速度将完全相等。这当然意味着您应该选择向量,因为它可以让您免于手动管理数组的麻烦。

【讨论】:

    【解决方案3】:

    std::vector 等标准库类的问题在于,是的,天真地,它比原始数组要多得多的代码。但是所有这些都可以被编译器简单地内联,这意味着如果启用优化,它基本上变成了相同的代码,就好像你使用了一个原始数组一样。那么速度差异不是可以忽略的,而是不存在的。所有开销都在编译时被移除。

    但这需要启用编译器优化。

    【讨论】:

      【解决方案4】:

      我想您发现迭代和添加到 std::vector 比普通数组慢 3 倍的原因是迭代向量和执行分配的成本的组合。

      编辑:

      这是我在测试用例之前的初步假设;但是运行测试用例(使用-O3 编译)显示相反 - std::vector 实际上快 3 倍,这让我感到惊讶。

      我看不出 std::vector 如何比普通数组副本更快(当然不是快 3 倍) - 我认为对 std::vector 编译的代码进行了一些优化,而这并没有发生数组版本。

      原始基准测试结果:

      $ ./array
      array:  0.059375
      vector: 0.021209
      
      • std::vector 快 3 倍。再次进行相同的基准测试,除了添加一个额外的外部循环来运行测试迭代器循环 1000 次:

        $ ./数组 数组:21.7129 矢量:21.6413

      • std::vector 现在 ~ 与数组的速度相同。

      编辑 2

      找到了!因此,您的测试用例的问题在于,在向量情况下,保存数据的内存似乎已经在 CPU 缓存中 - 无论是通过初始化的方式,还是由于对 vec.end() 的调用。如果我在每次计时测试之前“预热” CPU 缓存,我会得到相同的数组和向量数字:

      #include <time.h>
      #include <iostream>
      #include <vector>
      
      int main() {
        clock_t start,end;
        std::vector<int> vec(9999999);
        std::vector<int>::iterator vecIt = vec.begin();
        std::vector<int>::iterator vecEnd = vec.end();
      
        // get vec into CPU cache.
        for (int i = 0; vecIt != vecEnd; i++) { *(vecIt++) = i; }
        vecIt = vec.begin();
        start = clock();
        for (int i = 0; vecIt != vecEnd; i++) {
          *(vecIt++) = i;
        }
        end = clock();
        std::cout<<"vector: "<<(double)(end-start)/CLOCKS_PER_SEC<<std::endl;
      
        int* arr = new int[9999999];
      
        // get arr into CPU cache.
        for (int i = 0; i < 9999999; i++) { arr[i] = i; }
        start = clock();
        for (int i = 0; i < 9999999; i++) {
          arr[i] = i;
        }
        end = clock();
        std::cout<<"array: "<<(double)(end-start)/CLOCKS_PER_SEC<<std::endl;
      }
      

      这给了我以下结果:

      $ ./array
      vector: 0.020875
      array: 0.020695
      

      【讨论】:

      • 它也不安全。它默认不做边界检查。
      • 也许它正在缓存 CPU 寄存器中的数组地址,这就是为什么进一步的运行速度要快得多?我尝试循环 2 次而不是 1000 次,第一次运行耗时 0.06 秒,第二次运行耗时 0.25 秒
      • 当然这并不能解释为什么向量应该从一开始就以最佳速度运行。
      • @jalf:没有意识到 - 表明最近我在愤怒中使用了 std::vector!我已更新帖子以消除错误。
      【解决方案5】:

      我同意 rmeador,

        for (int i = 0; vecIt != vecEnd; i++) {
          *(vecIt++) = i; // <-- quick offset calculation
        }
        end = clock();
        cout<<"vector: "<<(double)(end-start)/CLOCKS_PER_SEC<<endl;
      
        int* arr = new int[9999999];
        start = clock();
        for (int i = 0; i < 9999999; i++) {
          arr[i] = i; // <-- not fair play :) - offset = arr + i*size(int)
        }
      

      【讨论】:

        【解决方案6】:

        我认为这里的答案很明显:没关系。就像 jalf 所说,代码最终会大致相同,但即使不是,请查看数字。您发布的代码创建了一个包含 1000 万个项目的庞大数组,但迭代整个数组只需要百分之几秒。

        即使您的应用程序确实在处理如此多的数据,无论您实际对这些数据做什么,都可能比遍历数组花费更多的时间。只需使用您喜欢的任何数据结构,然后将时间集中在其余代码上。

        为了证明我的观点,这里的代码有一个变化:i 对数组项的赋值被替换为 sqrt(i) 的赋值。在我使用 -O2 的机器上,执行时间从 0.02 秒增加到 0.06 秒。

        #include <time.h>
        #include <iostream>
        #include <vector>
        #include <math.h>
        
        using namespace std;
        
        int main() {
          clock_t start,end;
          std::vector<int> vec(9999999);
          std::vector<int>::iterator vecIt = vec.begin();
          std::vector<int>::iterator vecEnd = vec.end();
        
          start = clock();
          for (int i = 0; vecIt != vecEnd; i++) {
            *(vecIt++) = sqrt(i);
          }
          end = clock();
          cout<<"vector: "<<(double)(end-start)/CLOCKS_PER_SEC<<endl;
        
          int* arr = new int[9999999];
          start = clock();
          for (int i = 0; i < 9999999; i++) {
            arr[i] = i;
          }
          end = clock();
          cout<<"array: "<<(double)(end-start)/CLOCKS_PER_SEC<<endl;
        }
        

        【讨论】:

          【解决方案7】:

          问题似乎是您在关闭优化的情况下编译了代码。在我的机器上,OS X 10.5.7 和 g++ 4.0.1 我实际上看到向量比原始数组快 2.5 倍。

          使用 gcc 尝试将-O2 传递给编译器,看看是否有任何改进。

          【讨论】:

            【解决方案8】:

            您的数组迭代速度更快的原因是迭代次数是恒定的,并且编译器能够展开循环。尝试使用 rand 生成一个数字,并将其乘以一个你想要的大数字,这样编译器就无法在编译时计算出来。然后再试一次,你会看到类似的运行时结果。

            【讨论】:

              【解决方案9】:

              您的代码可能执行不完全相同的一个原因是因为在您的 std::vector 版本上,您正在增加两个值,整数 i 和 std::vector::iterator vecIt。要真正等效,您可以重构为

              start = clock();
              for (int i = 0; i < vec.size(); i++) {
                vec[i] = i;
              }
              end = clock();
              cout<<"vector: "<<(double)(end-start)/CLOCKS_PER_SEC<<endl;
              

              【讨论】:

                【解决方案10】:

                您的代码在这两种情况下提供了不公平的比较,因为您在向量测试中所做的工作比数组测试要多得多。

                使用向量,您可以同时递增迭代器 (vecIT) 和一个单独的变量 (i) 以生成赋值。

                使用数组,您只是增加变量 i 并将其用于双重目的。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2017-08-04
                  • 2017-03-04
                  • 2011-07-23
                  • 1970-01-01
                  • 1970-01-01
                  • 2014-12-12
                  • 1970-01-01
                  相关资源
                  最近更新 更多