动态分配的数组或std :: vector

时间:2009-07-01 22:29:39

标签: c++ performance optimization stl

我正在尝试优化我的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;
}

10 个答案:

答案 0 :(得分:21)

在对C ++容器进行基准测试时,启用大多数编译器优化非常重要。我在SO上的一些答案已经违背了这一点 - 例如,当没有内联运算符[]时,函数调用开销非常重要。

答案 1 :(得分:5)

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

答案 2 :(得分:5)

关于标准库类的问题,例如std::vector,是的,天真地,它比原始数组要多得多。但是所有这些都可以通过编译器简单地进行内联,这意味着如果启用了优化,它实际上就像使用原始数组一样相同的代码。然后速度差异不可忽略但不存在。所有开销都在编译时删除。

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

答案 3 :(得分:3)

我想象你为什么发现迭代并添加到std :: vector比普通数组慢3倍的原因是迭代向量和进行分配的成本的组合。

修改

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

我无法看到std :: vector如何比vanilla数组副本更快(当然不是快3倍) - 我认为有一些优化应用于std :: vector编译代码,这是没有发生的数组版本。

原始基准测试结果:

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

    $ ./array 阵列: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

答案 4 :(得分:1)

我同意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)
  }

答案 5 :(得分:1)

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

即使您的应用程序确实正在使用那么多数据,无论您使用该数据实际上做什么,都可能比迭代数组花费更多时间。只需使用您喜欢的任何数据结构,并将时间集中在代码的其余部分上。

为了证明我的观点,这里的代码有一处改变:i对数组项的赋值被替换为sqrt(i)的赋值。在我的机器上使用-O2,执行时间从.02到.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;
}

答案 6 :(得分:0)

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

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

答案 7 :(得分:0)

您的数组迭代更快的原因是迭代次数是常量,编译器能够展开循环。尝试使用rand生成一个数字,并将其多个为您想要的大数字,以便编译器无法在编译时弄清楚它。然后再试一次,你会看到类似的运行时结果。

答案 8 :(得分:0)

你的代码可能执行效果不一样的一个原因是因为在你的std :: vector版本中,你正在推出两个值,整数i和std :: vector :: iterator {{ 1}}。要真正相同,你可以重构

vecIt

答案 9 :(得分:0)

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

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

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