用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[]
与迭代器,它取决于:
-
你如何使用
operator[]
。 -
无论您的特定CPU是否具有索引寄存器 (x86上的
ESI/EDI
)。 -
多less其他代码也使用传递给
operator[]
的相同索引。
(例如,你是否通过锁步索引多个数组?
原因如下:
-
如果你做类似的事情
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
! -
如果你的CPU有索引寄存器,那么如果使用索引寄存器释放另一个寄存器用于循环,那么你的代码可以在索引而不是迭代器中执行,甚至更好。 这不是你可以看出来的东西; 你必须分析代码和/或反汇编它。
-
如果多个数组可以共享相同的索引,那么代码只需增加一个索引而不是增加多个迭代器,这样可以减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量的非常快速的操作。
所以使用[]运算符访问,这可能会比许多已经发布的例子更快。
- 按降序排列vector
- C ++ STLvector:从索引获取迭代器?
- 有没有一个sorted_vector类,它支持插入()等?
- 将调用std :: vector :: clear()将std :: vector :: capacity()设置为零?
- 从vector中提取子vector的最佳方法是什么?
- 从c ++ std :: vector中删除所有项目
- std :: array和std :: vector有什么区别? 你什么时候使用其他的?
- 为什么是std :: vector :: operator 比std :: vector :: at()快5到10倍?
- 如何找出一个项目是否存在于std :: vector中?