向量索引访问与迭代器访问的效率

时间:2012-02-29 20:22:03

标签: c++ stl vector iterator

我有问题要通过使用索引访问(使用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 更改标题,将“迭代效率”更正为“访问效率”。

5 个答案:

答案 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指针)时必须对索引和递增进行计算是正确的。