用vector :: iterator或at()迭代STLvector的速度会更快吗?

在性能方面,什么工作会更快? 有区别吗? 它是依赖于平台吗?

//1. Using vector<string>::iterator: vector<string> vs = GetVector(); for(vector<string>::iterator it = vs.begin(); it != vs.end(); ++it) { *it = "Am I faster?"; } //2. Using size_t index: for(size_t i = 0; i < vs.size(); ++i) { //One option: vs.at(i) = "Am I faster?"; //Another option: vs[i] = "Am I faster?"; } 

为什么不写一个testing,并找出?

编辑:我的坏 – 我以为我是时机优化版本,但没有。 在我的机器上,使用g ++ -O2进行编译,迭代器版本比操作符版本稍慢 ,但可能并不那么显着。

 #include <vector> #include <iostream> #include <ctime> using namespace std; int main() { const int BIG = 20000000; vector <int> v; for ( int i = 0; i < BIG; i++ ) { v.push_back( i ); } int now = time(0); cout << "start" << endl; int n = 0; for(vector<int>::iterator it = v.begin(); it != v.end(); ++it) { n += *it; } cout << time(0) - now << endl; now = time(0); for(size_t i = 0; i < v.size(); ++i) { n += v[i]; } cout << time(0) - now << endl; return n != 0; } 

使用迭代器会导致递增指针(用于递增)和取消引用来解引用指针。
使用索引时,递增应该同样快,但查找元素需要添加(数据指针+索引)并取消指针,但差异应该是边际的。
at()也检查索引是否在边界内,所以可能会变慢。

基准testing结果为500M迭代,向量大小为10,使用gcc 4.3.3(-O3),linux 2.6.29.1 x86_64:
at() :9158ms
operator[] :4269ms
iterator :3914ms

YMMV,但是如果使用索引使代码更具可读性/可理解性,则应该这样做。

如果你不需要索引,不要使用它。 迭代器的概念是最好的。 迭代器很容易优化,而直接访问需要一些额外的知识。

索引是为了直接访问。 括号和at方法做到这一点。 随意的,不像[] ,检查越界索引,所以它会更慢。

信条是:不要问你不需要什么。 那么编译器将不会收取您不使用的内容。

既然你正在寻找效率,你应该认识到下面的变化可能更有效率:

 //1. Using vector<string>::iterator: vector<string> vs = GetVector(); for(vector<string>::iterator it = vs.begin(), end = vs.end(); it != end; ++it) { //... } //2. Using size_t index: vector<string> vs = GetVector(); for(size_t i = 0, size = vs.size(); i != size; ++i) { //... } 

因为end / size函数只被调用一次而不是每次循环。 编译器很可能会内联这些函数,但这种方式确保。

正如其他人所说的那样,做基准。

话虽如此,我认为迭代器会更快,因为at()也会进行范围检查,即如果索引超出范围,它将抛出一个out_of_rangeexception。 这种检查本身可能会招致一些开销。

我猜想第一个变种是更快。

但是它依赖于实现。 要确定你应该分析你自己的代码。

为什么configuration自己的代码?

因为这些因素都会改变结果:

  • 哪个操作系统
  • 哪个编译器
  • STL的哪个实现正在被使用
  • 优化开启了吗?
  • …(其他因素)

第一个在debugging模式下会更快,因为索引访问会在场景后面创build迭代器,但是在释放模式下,所有内容都应该内联,差异应该可以忽略或者为空

您可以使用此testing代码并比较结果! 迪奥它!

 #include <vector> #include <iostream> #include <ctime> using namespace std;; struct AAA{ int n; string str; }; int main() { const int BIG = 5000000; vector <AAA> v; for ( int i = 0; i < BIG; i++ ) { AAA a = {i, "aaa"}; v.push_back( a ); } clock_t now; cout << "start" << endl; int n = 0; now = clock(); for(vector<AAA>::iterator it = v.begin(); it != v.end(); ++it) { n += it->n; } cout << clock() - now << endl; n = 0; now = clock(); for(size_t i = 0; i < v.size(); ++i) { n += v[i].n; } cout << clock() - now << endl; getchar(); return n != 0; } 

这真的取决于你在做什么,但是如果你不得不重新声明迭代器,迭代器会变成MARGINALLY SLOWER。 在我的testing中,最快的迭代将是向你的vector数组声明一个简单的*并迭代。

例如:

向量迭代并且每次传递两个函数。

 vector<MyTpe> avector(128); vector<MyTpe>::iterator B=avector.begin(); vector<MyTpe>::iterator E=avector.end()-1; for(int i=0; i<1024; ++i){ B=avector.begin(); while(B!=E) { float t=B->GetVal(Val1,12,Val2); float h=B->GetVal(Val1,12,Val2); ++B; }} 

向量取了90点击(0.090000秒)

但是如果你用指针做了…

 for(int i=0; i<1024; ++i){ MyTpe *P=&(avector[0]); for(int i=0; i<avector.size(); ++i) { float t=P->GetVal(Val1,12,Val2); float h=P->GetVal(Val1,12,Val2); }} 

vector点击18次(0.018000秒)

这大致相当于…

 MyTpe Array[128]; for(int i=0; i<1024; ++i) { for(int p=0; p<128; ++p){ float t=Array[p].GetVal(Val1, 12, Val2); float h=Array[p].GetVal(Val2,12,Val2); }} 

arrays接受了15次点击(0.015000秒)。

如果消除对avector.size()的调用,时间将变得相同。

最后,用[]

 for(int i=0; i<1024; ++i){ for(int i=0; i<avector.size(); ++i){ float t=avector[i].GetVal(Val1,12,Val2); float h=avector[i].GetVal(Val1,12,Val2); }} 

向量花了33次点击(0.033000秒)

与时钟()

这取决于。

答案比现有的答案显得微妙得多。

at总是比迭代器或operator[]慢。
但对于operator[]与迭代器,它取决于:

  1. 你如何使用operator[]

  2. 无论您的特定CPU是否具有索引寄存器 (x86上的ESI/EDI )。

  3. 多less其他代码也使用传递给operator[]的相同索引。
    (例如,你是否通过锁步索引多个数组?

原因如下:

  1. 如果你做类似的事情

     std::vector<unsigned char> a, b; for (size_t i = 0; i < n; ++i) { a[13 * i] = b[37 * i]; } 

    那么这个代码可能会比迭代器版本慢得多,因为它在循环的每次迭代中执行乘法操作

    同样,如果你做了这样的事情:

     struct T { unsigned char a[37]; }; std::vector<T> a; for (size_t i = 0; i < n; ++i) { a[i] = foo(i); } 

    那么这可能也会比迭代器版本慢,因为sizeof(T) 不是2的幂 ,因此每次循环时(再次)乘以37

  2. 如果你的CPU有索引寄存器,那么如果使用索引寄存器释放另一个寄存器用于循环,那么你的代码可以在索引而不是迭代器中执行,甚至更好。 这不是你可以看出来的东西; 你必须分析代码和/或反汇编它。

  3. 如果多个数组可以共享相同的索引,那么代码只需增加一个索引而不是增加多个迭代器,这样可以减less对内存的写入,从而提高性能。 但是,如果只是迭代单个数组,那么迭代器可能会更快,因为它避免了将偏移量添加到现有基指针的需要。

一般来说,你应该更喜欢迭代器指数和指数指针,直到和除非你面临瓶颈分析表明它将有利于切换,因为迭代器是通用的,并且可能是最快的方法; 他们不要求数据是随机可寻址的,这样可以在必要时交换容器。 指数是下一个首选的工具,因为它们仍然不需要直接访问数据 – 它们的失效频率较低,您可以例如用一个deque代替一个vector没有任何问题。 指针应该是最后的手段,只有迭代器在释放模式下不会退化为potiners,它们才会被certificate是有益的。

如果您正在使用VisualStudio 2005或2008,为了从vector中获得最佳性能,您需要定义_SECURE_SCL = 0

默认情况下,_SECURE_SCL是在其上迭代一个包含显着较慢的包含。 也就是说,在debugging版本中,它将使得更容易追踪任何错误。 值得注意的是,由于macros改变了迭代器和容器的大小,所以在共享stl容器的所有编译单元中必须保持一致。

我认为唯一的答案可能是在你的平台上进行testing。 通常,STL中唯一标准化的东西是集合提供的迭代器types和algorithm的复杂性。

我会说,这两个版本之间没有(没有什么区别) – 唯一的区别是我可以想到的是,当代码需要计算一个数组的长度时,代码必须迭代整个集合。不知道长度是否存储在向量中的variables,那么开销并不重要)

使用“at”访问元素应比使用[]直接访问要花费一点点时间,因为它检查你是否在vector的范围内,如果你超出界限则抛出一个exception(看起来[]通常只是使用指针算术 – 所以它应该更快)

当我尝试优化我的OpenGL代码时,我发现这个线程,并且想要分享我的结果,即使线程已经老化了。

背景:我有4个向量,大小从6到12不等。在代码的开始部分只发生一次,每隔0.1毫秒向每个向量中的元素读取

以下是首先使用的代码的精简版本:

 for(vector<T>::iterator it = someVector.begin(); it < someVector.end(); it++) { T a = *it; // Various other operations } 

使用这种方法的帧速率大约是每秒7帧(fps)。

但是,当我将代码更改为以下内容时,帧速率几乎翻倍至15fps。

 for(size_t index = 0; index < someVector.size(); ++index) { T a = someVector[index]; // Various other operations } 

差异应该可以忽略不计。 std :: vector保证其元素在内存中连续地布局。 因此,大多数stl实现将迭代器作为普通指针实现为std :: vector。 有了这个心态,两个版本之间的唯一区别应该是第一个版本增加一个指针,并在第二个版本中增加一个索引,然后添加到一个指针。 所以我的猜测是第二个也许是一个极快的(循环方面)机器指令更多。

尝试并检查编译器生成的机器码。

但是,一般来说,build议是分析是否真的很重要。 过早地思考这样的问题通常不会给你太多的回报。 通常,你的代码的热点将在其他地方,你可能不会一见钟情。

这里是我编写的代码,使用默认的mingw编译器在Code :: Blocks v12.11中编译。 这创build了一个巨大的向量,然后通过迭代器,()和索引访问每个元素。 每个循环通过调用最后一个元素来循环一次,并且通过将最后一个元素保存到临时内存来进行一次循环。

时间是使用GetTickCount完成的。

 #include <iostream> #include <windows.h> #include <vector> using namespace std; int main() { cout << "~~ Vector access speed test ~~" << endl << endl; cout << "~ Initialization ~" << endl; long long t; int a; vector <int> test (0); for (int i = 0; i < 100000000; i++) { test.push_back(i); } cout << "~ Initialization complete ~" << endl << endl; cout << " iterator test: "; t = GetTickCount(); for (vector<int>::iterator it = test.begin(); it < test.end(); it++) { a = *it; } cout << GetTickCount() - t << endl; cout << "Optimised iterator: "; t=GetTickCount(); vector<int>::iterator endofv = test.end(); for (vector<int>::iterator it = test.begin(); it < endofv; it++) { a = *it; } cout << GetTickCount() - t << endl; cout << " At: "; t=GetTickCount(); for (int i = 0; i < test.size(); i++) { a = test.at(i); } cout << GetTickCount() - t << endl; cout << " Optimised at: "; t = GetTickCount(); int endof = test.size(); for (int i = 0; i < endof; i++) { a = test.at(i); } cout << GetTickCount() - t << endl; cout << " Index: "; t=GetTickCount(); for (int i = 0; i < test.size(); i++) { a = test[i]; } cout << GetTickCount() - t << endl; cout << " Optimised Index: "; t = GetTickCount(); int endofvec = test.size(); for (int i = 0; i < endofvec; i++) { a = test[i]; } cout << GetTickCount() - t << endl; cin.ignore(); } 

基于此,我个人认为“优化”版本比“非优化”迭代器要快于vector.at(),比直接索引器要慢。

我build议你自己编译并运行代码。

编辑 :这个代码写回来,当我有较less的经验与C / C + +。 进一步的testing用例应该是使用前缀增量运算符而不是后缀。 这应该更好的运行时间。

只是略微跟原来的问题相切,但最快的循环是

for( size_t i=size() ; i-- ; ) { ... }

这当然会倒数。 如果循环中有大量的迭代,这确实可以节省大量的时间,但是它只包含less量的非常快速的操作。

所以使用[]运算符访问,这可能会比许多已经发布的例子更快。