【问题标题】:iterating over std containers using standard for使用标准迭代 std 容器
【发布时间】:2013-02-23 17:57:03
【问题描述】:

根据我所学到的迭代容器的方法,如 std::vector,是使用迭代器,如下所示:

for(vector<int>::iterator it = numbers.begin(); it != numbers.end(); it++)

我的问题是为什么不使用standatd for 迭代容器,它更快,因为不需要像numbers.begin()numbers.end() 那样调用函数。
根据我的尝试,我发现使用 for 比使用迭代器快 X 30。
我写了这段代码:

vector<int> numbers;
for (int i = 0; i < 5000000; i++)
{
    numbers.push_back(i);
}

time_t t = time(0);
struct tm * now = localtime(&t);
cout << now->tm_hour << ":" << now->tm_min << ":" <<  now->tm_sec << "\n";

for(vector<int>::iterator it = numbers.begin(); it != numbers.end(); it++)
{
    *it = 7;
}

t = time(0);
now = localtime(&t);
cout << now->tm_hour << ":" << now->tm_min << ":" <<  now->tm_sec << "\n";

int size = numbers.size();
for (int i = 0; i < size; i++)
{
    numbers[i] = i;
}

t = time(0);
now = localtime(&t);
cout << now->tm_hour << ":" << now->tm_min << ":" <<  now->tm_sec;

输出是:

    19:28:25
    19:28:56
    19:28:57

【问题讨论】:

  • 您似乎认为标准容器是可索引的(即numbers[i]),这当然不是常态,而是例外
  • 在您的示例中,您在这两种情况下都使用“标准”for
  • 您是否启用了编译器优化? vector&lt;T&gt;::iterator 在调试版本和发布版本之间的行为可能存在巨大差异。
  • @K-ballo - 你是什么意思?按索引接近元素是错误的吗?效果很好!
  • @Nikos - 我的意思是没有迭代器。

标签: c++ iterator stdvector


【解决方案1】:

看看你的数字——迭代一个简单的向量需要 30 秒?这是 CPU 时间的永恒。那里有问题。

一些建议:

  • 矢量占用约 19MB。这并不多,但它可能会导致堆碎片,或者如果您的计算机上加载了许多其他应用程序,则可能会导致 VM 交换。要获得可靠的号码,请关闭尽可能多的应用并确保您的系统处于空闲状态。

  • couts 在计时器内,因此您正在测量部分 iostream 库的性能。 你采取停止时间之后,在计时部分之外做 couts。

  • 一秒钟的时钟对于性能指标来说不够精确。使用 timeval 和 gettimeofday() 获得微秒精度。

  • 只有一次向量迭代,我发现运行之间存在很大差异。要获得更多可重复的结果,请多次迭代向量(如 500 次)。 (这比使用更大的向量要好,这可能会导致交换/碎片问题。)

  • 开启优化(例如,g++ -O3)。循环将运行得更快,时间差会更小。内联优化可能对 std::vector 代码有更多帮助。

通过这些更改(如下),在我的计算机上,迭代器比没有优化的索引慢 4 倍,但使用 -O1 时略,使用 -O3 时几乎相同。

#include <iostream>
#include <sys/time.h>
#include <vector>
using namespace std;

const unsigned N_REPS = 500;
const unsigned N_ITEMS = 5000000;

int print_elapsed(timeval start)
{
    timeval stop;
    gettimeofday(&stop, NULL);
    int elapsed = (stop.tv_sec * 1000000 + stop.tv_usec) - (start.tv_sec * 1000000 + start.tv_usec);
    cout << elapsed << "us" << endl;
    return elapsed;
}

int main()
{
    vector<int> numbers;
    numbers.reserve(N_ITEMS); // avoid heap fragmentation
    for (int i = 0; i < N_ITEMS; ++i)
        numbers.push_back(i);

    timeval start;
    gettimeofday(&start, NULL);

    for (unsigned r = 0; r < N_REPS; ++r)
        for (vector<int>::iterator it = numbers.begin(); it != numbers.end(); ++it)
            *it = r;

    int elapsed0 = print_elapsed(start);
    gettimeofday(&start, NULL);

    unsigned size = numbers.size();
    for (unsigned r = 0; r < N_REPS; ++r)
        for (unsigned i = 0; i < size; ++i)
            numbers[i] = r;

    int elapsed1 = print_elapsed(start);
    cout << 100 * float(elapsed1) / elapsed0 << '%' << endl;

    return 0;
}

【讨论】:

  • 所以你总结一下更好地使用standatd,没有迭代器?
  • 使用此编译器 (gcc 4.4.6),启用优化后,使用迭代器和简单整数索引之间没有有意义的性能差异。我怀疑其他编译器也是如此。所以我的建议是启用优化。
【解决方案2】:

使用迭代器为您提供了灵活性,即使您将容器类型更改为另一种,迭代代码也将保持不变。这是因为所有标准库容器都提供了迭代器。在某种程度上,它让您有机会编写更多通用代码。

【讨论】:

  • 但是有什么更好的方法来迭代容器?
猜你喜欢
  • 2018-10-26
  • 1970-01-01
  • 2010-09-30
  • 2017-06-10
  • 2011-06-05
  • 1970-01-01
  • 1970-01-01
  • 2019-08-09
相关资源
最近更新 更多