繁体   English   中英

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

[英]Efficiency of vector index access vs iterator access

我有问题通过使用索引访问(使用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 (无优化)索引访问

真正的800ms

迭代器访问

真正的2200ms

这些数字有意义吗? (我多次重复跑步)我想知道我错过了什么细节以及为什么我错了......

结果使用g ++ -O2索引访问,时间实际:~200ms

迭代器访问,时间真实:~200ms

我在不同平台上重复测试(amd64 w / g ++和power7 w xlC)并且看到我一直使用优化代码,示例程序具有相似的执行时间。

编辑更改的代码以添加值( value += *iter )而不是仅使用赋值。 添加了有关编译器选项 添加了使用-O2的新数字。 * edit2更改了标题,将“迭代器效率”更正为“访问效率”。

如果没有看到测试工具,编译器选项以及如何测量时间,就很难说出来。 此外,一个好的编译器可能能够在一种情况下消除循环,因为循环对返回的值没有影响。 尽管如此,根据实现情况,看到迭代器明显快于索引(反之亦然)并不会让我感到惊讶。

关于你的“理解”,迭代器的类型及其性能并不是固有的。 您可以编写非常快或非常慢的正向迭代器,就像您可以编写非常快或非常慢的随机访问迭代器一样。 在全球范围内,支持随机访问迭代器的数据结构类型可能比不支持随机访问迭代器的数据结构具有更好的局部性,这可能有利于随机访问迭代器; 但这真的不足以做出任何合理的概括。

当我使用-O2 (Linux,GCC 4.6.1)编译这两个程序时,它们运行速度相同。

然后:你的第一个程序没有使用迭代器,它正在使用索引 这些是不同的概念。

你的第二个程序实际上是使用随机访问迭代器,因为这就是std::vector<T>::iterator std::vector的限制是这样设计的,迭代器可以实现为vector封装的动态数组的简单指针。

begin应该和size一样快。 std::vector的典型实现中,两者之间的唯一区别是end可能需要计算begin() + size() ,尽管size也可以实现为(粗略地) end() - begin() 但是,编译器可能会在循环中优化两者。

通过优化,两个代码应该(接近)相同。 试试-O2

如果没有优化并添加调试信息,您的测量将会产生误导。

实际上,我发现迭代器更快。 尝试将迭代器循环重构为以下内容,您也可以看到:

#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”编译了上面的内容。 当使用迭代器(ala指针)时,slavik对于索引与递增的计算是正确的。

在第一个示例中,使用value = vec[idx];取消引用每个单独的项目value = vec[idx]; ,这会导致每次访问元素时计算element_size * index的偏移量。

由于向量由连续的内存块中排列的元素组成,因此向量迭代器通常只是作为简单指针实现,因此迭代向量(如第二个示例中所示)只需要在每次迭代后将指针前进一个元素。

但是,如果启用优化(尝试-O2-O3 ),编译器可能会在第一个示例中优化您的循环,使其与第二个示例类似,从而使性能几乎相同。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM