我有问题要通过使用索引访问(使用operator [])或使用迭代器来纠正我对访问向量元素的效率的理解。
我的理解是“迭代器”比“索引访问”更有效。
(我认为vector::end()
比vector::size()
更有效率。
现在我编写了示例代码测量它(在Windows 7下使用Cygwin,使用g ++ 4.5.3)
索引访问循环版本(以前标记为随机访问):
int main()
{
std::vector< size_t > vec ( 10000000 );
size_t value = 0;
for( size_t x=0; x<10; ++x )
{
for ( size_t idx = 0; idx < vec.size(); ++idx )
{
value += vec[idx];
}
return value;
}
}
迭代器循环代码是这样的:
for (std::vector< size_t >::iterator iter = vec.begin(); iter != vec.end(); ++iter) {
value = *iter;
}
我很惊讶地发现“索引访问”版本要快得多。我使用time
命令来“测量”。数字是:
使用
g++ source.cpp
的结果(无优化) 索引访问真正的800毫秒
迭代器访问
真正的2200毫秒
这些数字有意义吗? (我多次重复跑步)我想知道我错过了什么细节以及为什么我错了......
使用g ++ -O2的结果 索引访问,时间真实:~200ms
迭代器访问,时间真实:~200ms
我在不同平台上重复测试(amd64 w / g ++和power7 w xlC)并且看到我一直使用优化代码,示例程序的执行时间也相似。
修改更改了代码以添加值(value += *iter
),而不仅仅是使用赋值。添加了有关编译器选项添加了使用-O2的新数字。
* edit2 更改标题,将“迭代效率”更正为“访问效率”。
答案 0 :(得分:6)
没有看到测试工具,编译器选项,以及你如何 衡量时间,很难说什么。另外,一个好的编译器可能 因为循环具有,所以能够在一种情况下消除循环 对返回的值没有影响。仍然,取决于 实现,看到迭代器显着不会让我感到惊讶 比索引更快(反之亦然)。
关于你的“理解”,没有什么固有的 迭代器的类型及其性能。您可以编写前向迭代器 这是非常快,或非常慢,就像你可以写随机访问 迭代器非常快或非常慢。全球范围内的数据类型 将支持随机访问迭代器的结构可能具有 比那些没有的地方更好的地方,这可能有利于 随机访问迭代器;但这真的不足以做到 任何合理的概括。
答案 1 :(得分:4)
当我使用-O2
(Linux,GCC 4.6.1)编译这两个程序时,它们运行速度相同。
然后:你的第一个程序不使用迭代器,它使用 indices 。这些是不同的概念。
你的第二个程序实际上是使用随机访问迭代器,因为那是std::vector<T>::iterator
的。对std::vector
的限制是这样设计的,即迭代器可以实现为vector
封装的动态数组的简单指针。
begin
应该与size
一样快。 std::vector
的典型实现中两者之间的唯一区别是end
可能需要计算begin() + size()
,尽管size
也可能实现为({粗略){{1} }}。但是,编译器可能会在循环中优化两者。
答案 2 :(得分:2)
通过优化,两个代码应该(接近)相同。试试-O2
。
如果没有优化并添加调试信息,您的测量结果会非常误导。
答案 3 :(得分:0)
在第一个示例中,您使用value = vec[idx];
取消引用每个单独的项目,这会导致每次访问元素时计算element_size * index
的偏移量。
由于向量由连续的内存块中的元素组成,因此向量迭代器通常只是作为一个简单的指针实现,因此迭代向量(如第二个示例中所示)只需要将指针前进一个元素每次迭代。
但是,如果启用优化(尝试-O2
或-O3
),编译器可能会在第一个示例中优化您的循环,使其与第二个示例类似,从而使性能几乎完全相同。 / p>
答案 4 :(得分:0)
实际上,我发现迭代器更快。尝试将迭代器循环重构为类似下面的内容,您也可以看到:
#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
你应该注意我使用了一个rand();这可以防止编译器优化它可以在编译时计算的内容。使用内部数组而不是使用向量,编译器似乎更容易这样做,这可能会误导数组优于向量。
我用“icpc -fast”编译了上面的内容。 slavik关于在使用迭代器(ala指针)时必须对索引和递增进行计算是正确的。