在C ++中使用std :: vector会有什么性能损失?

时间:2013-08-20 18:33:24

标签: c++ performance c++11 vector scientific-computing

通常,我很想知道标准模板库是否会在数值/科学计算的代码中产生性能/速度开销。

例如。 将数组声明为

double 2dmatrix [10][10]

会给我更多的表现而不是

std::vector<std::vector<double> > 2dmatrix(10,std::vector<double>(10,0.0))

我也很欣赏一些一般性的想法,关于C在科学计算方面是否具有比C ++更好的性能。我使用STL以非常面向对象的方式编写了我的代码,并且使用了C ++ 11。我开始考虑是否应该开始研究纯C,如果它会更快运行。

欢迎任何有关此事的想法。

7 个答案:

答案 0 :(得分:14)

鉴于它提供的抽象,C ++ std::vector效率最高:堆栈上有3个指针,动态分配的数据平均每个元素重新分配1个线性增长情景(因为调整大小使容量扩大比例,比例为1.5到2)。

使用malloc()realloc()的C等价物将至少同样昂贵,并且更加繁琐(手动调整大小等)。此外,std::vector允许用户定义的性能调优special allocators(基于池,堆栈分配等),这在C ++ 11中并不那么难使用在C ++ 98中的原样。

如果不需要动态调整大小,可以在C和C ++中编写静态数组(或C ++中的std::array)。

通常,对于高性能计算C++ has more potential for optimization,特别是通过使用可以内联的函数对象(与常规C函数指针相反)。 canonical example is sorting

int comp( const void* a, const void* b ) {
    return /* your comparison here */;
}

// C style sorting
qsort( arr, LARGE_SIZE, sizeof( int ), comp ); 
                                       ^^^^ <---- no-inlining through function pointer

// C++11 style sorting (use hand-made function object for C++98
std::sort(std::begin(arr), std::end(arr), [](auto a, auto b) { 
    return comp(&a, &b);
           ^^^^ <----- C++11 lambdas can be fully inlined
});

答案 1 :(得分:8)

std :: vector的开销是:

  • 堆栈上的3个指针
  • 动态分配(懒惰,即直到需要时才分配任何东西)

在某些情况下(对于少量数据),堆栈分配的数组可能更快。为此,您可以使用std::array<T, Length>

如果你需要一个二维网格,我会在一个向量中分配数据:std::vector<T>(width * height);。然后你可以编写一些辅助函数来通过x和y坐标获取元素。 (或者你可以写一个wrapper class。)

答案 2 :(得分:3)

如果您事先知道大小并且性能是瓶颈 - 请使用C ++ 11中的std::array。它的性能与C风格的数组完全相同,因为它内部看起来像

template<typename T, int N>
struct array {
  T _data[N];
};

这是在现代C ++中使用堆栈分配的数组的一种优先方式。如果您有现代编译器,切勿使用C风格的数组。

答案 3 :(得分:2)

如果您没有理由调整数组的大小,并且在编译期间知道它的大小(就像您在第一个示例中所做的那样),那么STL模板的更好选择是std::array模板。它为您提供C风格阵列的所有相同优点。

double 2dmatrix[10][10];

// would become

std::array<std::array<double, 10>, 10> 2dmatrix;

答案 4 :(得分:2)

人们会说“这取决于你在做什么”。

他们是对的。

有一个例子here,其中使用std::vector的传统设计程序通过一系列六个阶段进行了性能调整,其执行时间从每单位工作2700微秒减少到3.7,加速因子为730x。

首先要做的是注意到很大一部分时间用于增长数组并从中删除元素。 因此使用了不同的数组类,这大大减少了时间。

第二件事是注意到很大一部分时间仍在进行与数组相关的活动。 因此,阵列完全被删除,而使用链接列表,产生了另一个大的加速。

然后其他事情使用了大部分剩余时间,例如newdelete对象。 然后这些对象在自由列表中被回收,产生了另一个大的加速。 经过几个阶段之后,我们决定停止尝试,因为找到改进的东西变得越来越困难,加速被认为是足够的。

关键是,不要只是选择一些强烈推荐的东西,然后希望最好。 而是以某种方式构建它然后执行性能调整,如this,并愿意根据您看到的大部分时间花费在数据结构设计上进行重大更改。 迭代它。 您可以将存储方案从A更改为B,稍后从B更改为C. 那完全没问题。

答案 5 :(得分:1)

在科学计算中,错误和次优代码特别令人沮丧,因为大量数据处理不当,浪费了宝贵的时间。

std::vector可能是您的瓶颈或最佳表现者,具体取决于您对其内部运作的了解。特别注意reserve()insert()erase();如果您的程序是线程化的,请考虑学习对齐和处理器缓存。

考虑一下您必须花费多少时间来确保一致性 - 以及以后寻找错误 - 如果您尝试自己进行所有内存管理,尤其是在逐步向软件添加功能时。在一天结束时,std :: vector的开销将是您遇到的最少问题。

答案 6 :(得分:0)

对于科学计算,使用专用的C ++矩阵库(例如Armadillo)要好得多。这不仅可以为您提供快速的阵列处理,还可以进行彻底调试的许多线性代数运算。

除了性能原因之外,使用专用的C ++矩阵库还可以大大降低代码的冗长度,减少错误,从而加快开发速度。一个例子是使用C ++矩阵库,您不必担心内存管理。

最后,如果你真的需要进入低级别(即直接通过指针使用内存),C ++允许你“下降”到C级别。在Armadillo中,这是通过.memptr()成员函数完成的。