C++ 向量索引访问与迭代器访问的效率
我有一个问题要纠正我对使用索引访问(带操作符[])或使用迭代器访问向量元素的效率的理解 我的理解是“迭代器”比“索引访问”更有效。 (我还认为C++ 向量索引访问与迭代器访问的效率,c++,stl,vector,iterator,C++,Stl,Vector,Iterator,我有一个问题要纠正我对使用索引访问(带操作符[])或使用迭代器访问向量元素的效率的理解 我的理解是“迭代器”比“索引访问”更有效。 (我还认为vector::end()比vector::size()更有效) 现在我编写了示例代码measure it(在Windows7下使用Cygwin,使用g++4.5.3) 索引访问循环版本(以前标记为随机访问): intmain() { 标准:向量向量(10000000); 大小\u t值=0; 对于(大小x=0;x::迭代器iter=vec.begin()
vector::end()
比vector::size()
更有效)
现在我编写了示例代码measure it(在Windows7下使用Cygwin,使用g++4.5.3)
索引访问循环版本(以前标记为随机访问):
intmain()
{
标准:向量<大小>向量(10000000);
大小\u t值=0;
对于(大小x=0;x::迭代器iter=vec.begin();iter!=vec.end();++iter){
数值=*iter;
}
我惊讶地发现“索引访问”版本快得多。我使用time
命令“测量”。数字是:
使用g++source.cpp的结果(无优化)
索引访问
实际800毫秒
迭代器访问
实际2200ms
这些数字有意义吗?(我重复了多次)我想知道我错过了什么细节,为什么我错了
结果使用g++-O2
索引访问,实时:~200ms
迭代器访问,实时:~200ms
我在不同的平台(amd64 w/g++和power7 w xlC)上重复了测试,发现所有使用优化代码的示例程序都有类似的执行时间
编辑更改代码以添加值(value+=*iter
),而不是仅使用赋值。添加了有关编译器选项的详细信息。添加了使用-O2的新数字。
*edit2将标题更正“迭代器效率”更改为“访问效率”。通过优化,两个代码应该(接近)相同。请尝试-O2
如果没有优化和添加调试信息,您的测量结果将非常令人误解。当我使用-O2
(Linux,GCC 4.6.1)编译这两个程序时,它们的运行速度一样快
然后:您的第一个程序不是使用迭代器,而是使用索引。这些是不同的概念
您的第二个程序实际上使用随机访问迭代器,因为这就是std::vector::iterator
s。对std::vector
的限制设计为迭代器可以实现为vector
封装的动态数组的简单指针
begin
应该与size
一样快。在std::vector
的典型实现中,两者之间的唯一区别是end
可能需要计算begin()+size()
,尽管size
也可能(大致)实现为end()-begin()
。不过,编译器可能会在循环中同时优化这两个方面。而不会看到测试工具、编译器选项以及您如何使用它们
衡量时间,很难说什么。而且,一个好的编译器可能
在这种或那种情况下能够消除循环,因为循环
对返回的值没有影响。但是,取决于
在实现中,如果看到迭代器,我不会感到惊讶
比索引更快(反之亦然)
就你的“理解”而言,这并不是内在的
迭代器的类型及其性能。您可以编写前向迭代器
这是非常快,或非常慢,就像你可以写随机访问
非常快或非常慢的迭代器。全局而言,数据类型
支持随机访问迭代器的结构可能具有
比那些没有更好的地方,这可能有利于
随机访问迭代器;但这还不足以
任何合理的概括。在第一个示例中,使用value=vec[idx];
取消对每个单独项目的引用,这会导致每次访问元素时计算元素大小*索引的偏移量
由于向量由在连续内存块中排列的元素组成,因此向量迭代器通常仅作为简单指针实现,因此在向量中进行迭代(如第二个示例中所示)只需要在每次迭代后将指针向前移动一个元素
如果启用优化(请尝试-O2
或-O3
)但是,编译器可能会将第一个示例中的循环优化为与第二个示例类似的内容,使性能几乎相同。事实上,我发现迭代器更快。尝试将迭代器循环重构为以下内容,您可能也会看到:
#include <ctime>
#include <vector>
#include <iostream>
using namespace std;
int main()
{
std::vector< size_t > vec ( 1000000 );
size_t value = 0;
srand ( time(NULL) );
clock_t start,stop;
int ncycle = 10000;
start = std::clock();
for( size_t x=0; x<ncycle; ++x ) {
for ( size_t idx = 0; idx < vec.size(); ++idx )
vec[idx] += rand();
}
stop = std::clock();
cout << start << " " << stop << endl;
cout << "INDEX: " << (double((stop - start)) / CLOCKS_PER_SEC) / ncycle << " seconds per cycle" << endl;
start = std::clock();
for( size_t x=0; x<ncycle; ++x ) {
for (std::vector< size_t >::iterator iter = vec.begin(), end = vec.end(); iter != end; ++iter)
*iter += rand();
}
stop = std::clock();
cout << "ITERATOR: " << (double((stop - start)) / CLOCKS_PER_SEC) / ncycle << " seconds per cycle" << endl;
}
您应该注意,我添加了rand();这会阻止编译器优化它在编译时可以计算的内容。编译器使用内部数组似乎比使用向量容易得多,这可能会误导性地使数组优于向量
我用“icpc-fast”编译了上面的内容。slavik说得对,当使用迭代器(ala指针)时,必须对索引和递增进行计算.请确保您没有使用调试支持进行编译,尤其是在MSVC下。此外,您的第一个版本根本不使用迭代器,而在第二个版本中,您确实使用了随机访问迭代器。您是否启用了优化?您的直觉是正确的,使用优化则相反,#2要快得多。我想您有点困惑。Vect或仅具有随机访问迭代器。使用运算符[]
索引到向量不涉及迭代器。替换值=vec[idx];替换值+=vec[idx];在这两种情况下,为了避免编译器变得如此聪明,以至于发现它被覆盖了。实际上,我看到的std::vector
的实现保留了三个指针:开始、结束,以及一个指向所分配块末尾的指针。这使得vector::size()
稍微慢一点,因为它必须执行end-begin
for (std::vector< size_t >::iterator iter = vec.begin(); iter != vec.end(); ++iter) {
value = *iter;
}
#include <ctime>
#include <vector>
#include <iostream>
using namespace std;
int main()
{
std::vector< size_t > vec ( 1000000 );
size_t value = 0;
srand ( time(NULL) );
clock_t start,stop;
int ncycle = 10000;
start = std::clock();
for( size_t x=0; x<ncycle; ++x ) {
for ( size_t idx = 0; idx < vec.size(); ++idx )
vec[idx] += rand();
}
stop = std::clock();
cout << start << " " << stop << endl;
cout << "INDEX: " << (double((stop - start)) / CLOCKS_PER_SEC) / ncycle << " seconds per cycle" << endl;
start = std::clock();
for( size_t x=0; x<ncycle; ++x ) {
for (std::vector< size_t >::iterator iter = vec.begin(), end = vec.end(); iter != end; ++iter)
*iter += rand();
}
stop = std::clock();
cout << "ITERATOR: " << (double((stop - start)) / CLOCKS_PER_SEC) / ncycle << " seconds per cycle" << endl;
}
INDEX: 0.012069 seconds per cycle
ITERATOR: 0.011482 seconds per cycle