C++迭代器和循环优化

Qua*_*um7 46 c++ compiler-construction optimization iterator coding-style

我看到很多c ++代码看起来像这样:

for( const_iterator it = list.begin(),
     const_iterator ite = list.end();
     it != ite; ++it)
Run Code Online (Sandbox Code Playgroud)

与更简洁的版本相反:

for( const_iterator it = list.begin();
     it != list.end(); ++it)
Run Code Online (Sandbox Code Playgroud)

这两个约定之间的速度会有什么不同吗?由于list.end()只被调用一次,因此第一个会稍快一些.但由于迭代器是const,似乎编译器会将此测试从循环中拉出来,为两者生成等效的汇编.

new*_*cct 43

但这两个版本并不相同.在第二个版本中,它list.end()每次都会比较迭代器,并且list.end()评估的内容可能会在循环过程中发生变化.当然,你无法list通过const_iterator进行修改it; 但是没有什么能阻止循环中的代码list直接调用方法并对其进行变异,这可能(取决于什么类型的数据结构list)改变结束迭代器.因此在某些情况下预先存储结束迭代器可能是不正确的,因为到达它时它可能不再是正确的结束迭代器.

  • 例如,如果list是std :: vector,则在循环内更改它将使所有迭代器无效,从而使两个循环都不正确. (21认同)

j_r*_*ker 29

我就提备案的C++,调用标准要求begin(),并end()任何容器类型(可能是vector,list,map等)必须采取唯一不变的时间.实际上,如果在启用优化的情况下进行编译,则几乎肯定会将这些调用内联到单个指针比较中.

请注意,此保证不一定适用于其他供应商提供的"容器",这些容器实际上不符合标准第23章(例如单链表slist)中规定的容器的形式要求.

  • FWIW可以使用gcc的[`-flto`标志](http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-flto-934)恢复翻译单元中函数内联的丢失,以启用'链接时优化'.Clang有[类似功能](http://llvm.org/docs/GoldPlugin.html). (2认同)

Ada*_*eld 11

第一个可能几乎总是更快,但如果你认为这将有所作为,总是首先查看哪个更快,多少.

end()在两种情况下,编译器都可能能够内联调用,尽管如果end()它足够复杂,它可能选择不内联它.但是,关键的优化是编译器是否可以执行循环不变的代码运动.我认为在大多数情况下,编译器不能确定end()在迭代循环期间值不会改变,在这种情况下,它只能end()在每次迭代后调用.

  • 定时两种方法都是一个好主意.你*不知道第一个会更快,因为每次调用end()几乎肯定会被内联到一个指针比较中.此外,C++标准保证在任何容器上调用end()都是一个常量操作,因此它永远不会"足够复杂". (3认同)

Gre*_*ill 8

我会选择最简洁和可读的选项.不要试图再次猜测编译器及其可能执行的优化.请记住,绝大多数代码对整体性能没有任何影响,因此,只有在性能关键的代码段中,您才应该花时间对其进行分析并选择适当有效的源代表.

具体参考您的示例,第一个版本生成迭代器的副本end(),调用为迭代器对象的复制构造函数运行的任何代码.STL容器通常包含内联end()函数,因此编译器有很多机会优化第二个版本,即使您没有尝试帮助它.哪一个最好?测量它们.


Mar*_*som 6

您可以使第一个版本更简洁,并充分利用两者:

for( const_iterator it = list.begin(), ite = list.end();
     it != ite; ++it)
Run Code Online (Sandbox Code Playgroud)

PS迭代器不是const,它们是const引用的迭代器.有很大的不同.


Shi*_*Yip 6

考虑这个例子:

for (const_iterator it = list.begin(); it != list.end(); ++list)
{
    if (moonFull())
        it = insert_stuff(list);
    else
        it = erase_stuff(list);
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,你需要在循环中调用list.end(),编译器不会优化它.

在编译器可以证明end()总是返回相同值的其他情况下,可以进行优化.

如果我们讨论的是STL容器,那么我认为任何好的编译器都可以在编程逻辑不需要多次end()调用时优化多个end()调用.但是,如果您有自定义容器并且end()的实现不在同一个转换单元中,那么优化必须在链接时进行.我对链接时间优化知之甚少,但我敢打赌,大多数链接器都不会进行这样的优化.