在C ++中使用数组或std ::向量,性能差距是什么?

在我们的C ++课程中,他们build议不要在新项目上使用C ++数组。 就我所知,Stroustroup自己build议不要使用数组。 但是是否有显着的性能差异?

应该避免使用new C ++数组(即使用dynamic数组)。 有问题你必须跟踪的大小,你需要手动删除它们,并做各种家务。

在堆栈中使用数组也是不鼓励的,因为你没有范围检查,传递数组会损失任何关于数组大小的信息(数组到指针的转换)。 在这种情况下,你应该使用boost::array ,它将一个C ++数组封装在一个小类中,并提供一个size函数和迭代器来迭代它。

现在, std :: vector与本地C ++数组 (取自互联网):

 // Comparison of assembly code generated for basic indexing, dereferencing, // and increment operations on vectors and arrays/pointers. // Assembly code was generated by gcc 4.1.0 invoked with g++ -O3 -S on a // x86_64-suse-linux machine. #include <vector> struct S { int padding; std::vector<int> v; int * p; std::vector<int>::iterator i; }; int pointer_index (S & s) { return sp[3]; } // movq 32(%rdi), %rax // movl 12(%rax), %eax // ret int vector_index (S & s) { return sv[3]; } // movq 8(%rdi), %rax // movl 12(%rax), %eax // ret // Conclusion: Indexing a vector is the same damn thing as indexing a pointer. int pointer_deref (S & s) { return *sp; } // movq 32(%rdi), %rax // movl (%rax), %eax // ret int iterator_deref (S & s) { return *si; } // movq 40(%rdi), %rax // movl (%rax), %eax // ret // Conclusion: Dereferencing a vector iterator is the same damn thing // as dereferencing a pointer. void pointer_increment (S & s) { ++sp; } // addq $4, 32(%rdi) // ret void iterator_increment (S & s) { ++si; } // addq $4, 40(%rdi) // ret // Conclusion: Incrementing a vector iterator is the same damn thing as // incrementing a pointer. 

注意:如果使用new分配数组,并分配非类对象(如纯int )或没有用户定义的构造函数的类, 并且不希望最初初始化元素,则使用new分配的数组可以具有性能优势,因为std::vector将构造中的所有元素初始化为默认值(例如,int为0)(用于记住我的信息给@bernie)。

微观优化人员的序言

记得:

“程序员浪费了大量的时间来思考或者担心程序中非关键部分的速度,而这些效率的尝试实际上在考虑debugging和维护时会产生很大的负面影响,我们应该忘记小的效率, 97%的时间: 不成熟的优化是万恶之源,但我们不应该把这个关键的3%放在一边“。

(感谢变态的完整报价)

不要使用C数组,而不是使用vector(或其他),因为你认为速度更快,因为它应该是低级的。 你会错的。

使用默认的向量(或根据您的需要安全的容器),然后如果您的分析器说这是一个问题,看看你是否可以优化它,要么使用更好的algorithm,或更改容器。

这说,我们可以回到原来的问题。

静态/dynamic数组?

C ++数组类比低级C数组performance得更好,因为他们知道很多关于他们自己的知识,并且可以回答C数组无法解答的问题。 他们能够自己清理。 更重要的是,它们通常是使用模板和/或内联编写的,这意味着在debugging中出现很多代码会解决很less或根本没有在发布版本中生成的代码,这意味着与其内置的不太安全的竞争没有区别。

总而言之,它分为两类:

dynamic数组

使用指向malloc-ed / new-ed数组的指针最好和std :: vector版本一样快,而且安全性也差得多(参见litb的文章 )。

所以使用一个std :: vector。

静态数组

使用静态数组将是最好的:

  • 与std :: array版本一样快
  • 而且不太安全。

所以使用一个std ::数组 。

未初始化的内存

有时候,使用vector而不是原始缓冲区会导致可见的代价,因为vector将在构build时初始化缓冲区,而代替它的代码却没有,正如他的回答中所指出的那样 。

如果是这种情况,那么你可以通过使用unique_ptr而不是vector来处理它,或者,如果你的代码行中没有例外情况,实际上可以编写一个类buffer_owner来拥有这个内存,并且给你一个简单而安全的访问它包括奖金调整(使用realloc ?),或任何你需要的。

向量是引擎盖下的数组。 性能是一样的。

一个地方,你可以遇到一个性能问题,不正确的大小vector开始。

当一个向量填充时,它将自行resize,这可能意味着一个新的数组分配,其次是n个拷贝构造函数,接下来是大约n个析构函数调用,然后是数组删除。

如果你的构build/破坏是昂贵的,那么你最好把vector做成正确的大小。

有一个简单的方法来certificate这一点。 创build一个简单的类,显示何时构造/销毁/复制/分配。 创build这些东西的vector,并开始在vector的后端推它们。 当vector填满时,随着vectorresize,将会有一连串的活动。 然后再次尝试使用大小为预期元素数的向量。 你会看到不同之处。

为了回应Mehrdad所说的话:

但是,有些情况下您仍然需要数组。 当与低级代码(即汇编)或需要数组的旧库连接时,可能无法使用向量。

根本不是真的。 如果你使用下面的方法,vector很好地分解成数组/指针:

 vector<double> vector; vector.push_back(42); double *array = &(*vector.begin()); // pass the array to whatever low-level code you have 

这适用于所有主要的STL实现。 在下一个标准中,它将被要求工作(即使今天确实很好)。

在C ++ 11中使用普通数组的原因更less。

根据它们的特性,从本质上讲有3种types的数组(从速度到最慢)(当然,即使列表中的情况3,实现的质量也可以使事情变得非常快):

  1. 编译时已知大小的静态。 — std::array<T, N>
  2. 在运行时已知大小的dynamic调整,并且从不resize 这里的典型优化是,如果数组可以直接分配在堆栈中。 – 不可用 。 也许dynarray在C ++ 14之后的C ++ TS中。 在C中有VLA
  3. 在运行时dynamicresize。 — std::vector<T>

对于具有固定数量元素的纯静态数组,在C ++ 11中使用std::array<T, N>

对于在运行时指定的2.固定大小的数组,但不会改变它们的大小,在C ++ 14中有讨论,但是它已经被移到了技术规范中,并最终由C ++ 14完成。

对于3. std::vector<T> 通常会在堆中请求内存 。 这可能会带来性能上的影响,尽pipe你可以使用std::vector<T, MyAlloc<T>>来改善自定义分配器的情况。 与T mytype[] = new MyType[n];相比的优点T mytype[] = new MyType[n]; 是你可以调整它的大小,它不会衰减到一个指针,像普通数组一样。

使用上面提到的标准库types来避免数组衰减到指针 。 如果使用相同的function集,则将节省debugging时间,性能与普通数组完全相同。

STL是一个高度优化的库。 事实上,甚至build议在需要高性能的游戏中使用STL。 数组太容易出错,无法在日常任务中使用。 今天的编译器也非常聪明,可以用STL生成优秀的代码。 如果你知道自己在做什么,STL通常可以提供必要的性能。 例如,通过将vector初始化为所需的大小(如果您从开始就知道),则基本上可以实现arrays性能。 但是,有些情况下您仍然需要数组。 当与低级代码(即汇编)或需要数组的旧库连接时,可能无法使用向量。

去STL。 没有性能损失。 algorithm非常高效,他们在处理大多数人不会想到的细节方面做得很好。

关于duli的贡献 。

结论是,整数数组比整数的vector(在我的例子中是5倍)更快。 然而,对于更复杂/不alignment的数据,数组和向量的速度是相同的。

如果以debugging模式编译软件,许多编译器将不会内联向量的访问器函数。 这将使性能问题的情况下,stl向量的执行速度要慢得多。 它还会使代码更易于debugging,因为您可以在debugging程序中看到分配了多less内存。

在优化模式下,我希望stl向量能够接近数组的效率。 这是因为许多vector方法现在被内联。

两者之间的性能差异是非常多的实现相关的 – 如果你比较不好实施的std ::向量到一个最佳的数组实现,数组会赢,但转过来,向量会赢…

只要你比较苹果和苹果(无论是数组和vector都有固定数量的元素,或者dynamicresize),我认为性能差异是微不足道的,只要你遵循STL编码实践。 不要忘记,使用标准C ++容器还允许使用属于标准C ++库的预滚动algorithm,其中大部分可能比您自己构build的相同algorithm的平均实现性能更好。

也就是说,恕我直言,这个向量在一个debuggingSTL的debugging场景中胜出,因为大多数STL实现具有适当的debugging模式,至less可以突出显示人们在使用标准容器时所犯的典型错误。

哦,不要忘记,数组和vector共享相同的内存布局,所以你可以使用向量传递数据到传统的C或C ++代码,期望基本的数组。 请记住,大多数投注都是在这种情况下,然而,你正在处理原始记忆。

如果您不需要dynamicresize,则可以节省内存开销(一个指针/ size_t)。 而已。

可能会有一些边缘情况,你可以在内联函数中使用内联函数访问向量,在这种情况下,你已经超出了编译器内联的地方,并且会强制进行函数调用。 那将是如此罕见以至于不值得担心 – 总的来说,我会同意litb 。

我很惊讶没有人提到这一点 – 不要担心性能,直到它被certificate是一个问题,然后基准。

我认为主要关心的不是性能,而是安全性。 你可以在数组中犯很多错误(例如考虑resize),一个向量会为你节省很多的痛苦。

向量使用比数组更多的内存,因为它们包含数组的大小。 他们还增加了程序的硬盘大小,可能还会增加程序的内存占用量。 这些增加是微小的,但如果你正在使用embedded式系统,这可能很重要。 虽然大多数这些差异很重要的地方是你会使用C而不是C ++的地方。

以下简单的testing:

C ++数组与vector性能testing的解释

与“为基本索引,解引用和向量和数组/指针上的增量操作生成的汇编代码比较”的结论相矛盾。

数组和向量之间必须有区别。 testing说这么…只是尝试一下,代码在那里…

有时数组确实比vector好。 如果你总是操纵一组固定长度的对象,那么数组是更好的。 考虑下面的代码片段:

 int main() { int v[3]; v[0]=1; v[1]=2;v[2]=3; int sum; int starttime=time(NULL); cout << starttime << endl; for (int i=0;i<50000;i++) for (int j=0;j<10000;j++) { X x(v); sum+=x.first(); } int endtime=time(NULL); cout << endtime << endl; cout << endtime - starttime << endl; } 

X的vector版本在哪里

 class X { vector<int> vec; public: X(const vector<int>& v) {vec = v;} int first() { return vec[0];} }; 

而X的arrays版本是:

 class X { int f[3]; public: X(int a[]) {f[0]=a[0]; f[1]=a[1];f[2]=a[2];} int first() { return f[0];} }; 

main()的数组版本会更快,因为我们在内部循环中每次都避免了“new”的开销。

(这段代码被我发布到comp.lang.c ++)。

当你需要一个未初始化的缓冲区(例如用作memcpy()目的地memcpy()时,使用std::vector和raw数组肯定会对性能产生影响。 一个std::vector将使用默认的构造函数初始化它的所有元素。 原始数组不会。

使用count参数的std:vector构造函数的c ++规范 (这是第三种forms)指出:

`从各种数据源构造一个新的容器,可选地使用用户提供的分配器alloc。

3)构造具有T的缺省插入实例的容器。不创build副本。

复杂

2-3)线性计数

原始数组不会产生这种初始化成本。

另请参见如何避免std :: vector <>初始化其所有元素?