向量索引访问与迭代器访问的效率
Efficiency of vector index access vs iterator access
我有问题要纠正我对使用索引访问(带运算符[])或迭代器访问向量元素的效率的理解。
我的理解是"迭代器"比"索引访问"更有效率。(我也认为vector::end()
比vector::size()
更有效率)。
现在我写了测量它的示例代码(在Windows7下使用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毫秒
迭代器访问
实际2200ms
这些数字有意义吗?(我重复了多次跑步)我想知道我错过了什么细节,为什么我错了。。。
使用g++-O2的结果索引访问,实时:~200ms
迭代器访问,实时:~200ms
我在不同的平台(amd64 w/g++和power7 w xlC)上重复测试,发现在我使用优化代码的所有时间里,示例程序的执行时间都相似。
edit更改代码以添加值(value += *iter
),而不是仅使用赋值。添加了有关编译器选项的详细信息。添加了使用-O2的新编号。*edit2更改标题,将"迭代器效率"更正为"访问效率"。
没有看到测试工具、编译器选项以及如何以时间来衡量,很难说什么。此外,一个好的编译器可能能够消除这种或那种情况下的循环,因为循环对返回的值没有影响。不过,取决于实现中,看到迭代器的显著性并不让我感到惊讶比索引更快(反之亦然)。
关于你的"理解"迭代器的类型及其性能。您可以编写前向迭代器它们非常快,或者非常慢,就像你可以写随机访问一样非常快或非常慢的迭代器。在全球范围内,数据类型支持随机访问迭代器的结构可能具有比那些没有的地方更好,这可能有利于随机访问迭代器;但这还远远不够任何合理的概括。
当我用-O2
(Linux,GCC 4.6.1)编译这两个程序时,它们运行得同样快。
然后:您的第一个程序是使用迭代器的而不是,它使用的是索引。这些是不同的概念。
您的第二个程序实际上使用了随机访问迭代器,因为std::vector<T>::iterator
就是这样。对std::vector
的限制是这样设计的,即迭代器可以实现为vector
封装的动态数组的简单指针。
CCD_ 10应该和CCD_ 11一样快。在std::vector
的典型实现中,两者之间的唯一区别是end
可能需要计算begin() + size()
,尽管size
也可能实现为(大致)end() - begin()
。不过,编译器可能会在循环中对两者进行优化。
通过优化,两个代码应该(接近)相同。尝试-O2
。
如果没有优化和添加调试信息,您的测量结果将非常具有误导性。
在第一个示例中,使用value = vec[idx];
取消引用每个单独的项,这会导致每次访问元素时计算element_size * index
的偏移量。
由于向量由在连续内存块中排列的元素组成,因此向量迭代器通常只是作为一个简单的指针来实现,因此遍历向量(就像第二个例子中一样)只需要在每次迭代后将指针前移一个元素。
但是,如果启用优化(尝试-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;
}
在我的电脑上,结果如下,表明迭代器略有领先:
INDEX: 0.012069 seconds per cycle
ITERATOR: 0.011482 seconds per cycle
你应该注意到,我使用了rand()的加法;这会阻止编译器在编译时优化出可以计算的内容。编译器在处理固有数组时似乎比在处理向量时容易得多,这可能会误导数组相对于向量的优势。
我用"icpc-fast"编译了上面的内容。slavik在使用迭代器(ala指针)时必须对索引进行计算,而不是递增。
- 通过方法访问结构
- 使用不带参数的函数访问结构元素
- 如果我只是不访问queue_front节点的子节点,而是将它们推到队列中呢?还是BFS吗
- 用于访问容器<T>数据成员的正确 API
- 访问者访问变体并返回不同类型时出错
- 尝试通过多个向量访问变量时,向量下标超出范围
- 无法访问嵌套类.类的使用无效
- 写入位置0x0000000C时发生访问冲突
- 我们可以访问一个不存在的联盟的成员吗
- C++从另一个类访问公共静态向量的正确方法是什么
- 我的简单if-else语句是如何无法访问的代码
- 从C++dll访问C#中的一行主要参数
- 概念TS检查忽略私有访问修饰符
- 访问被拒绝后,c++中的故障保护代码
- 在c++中访问int到类对象的映射时出错
- 我想访问std::unique_ptr中的一个特定元素
- 为什么示例代码访问IUnknown中已删除的内存
- 访问文件中的单个字符效率低下?(C++)
- 静态与堆栈动态变量的访问效率
- 向量索引访问与迭代器访问的效率