是不是调用`list <t> :: end()`效率不高?</t>

时间:2012-08-28 19:41:15

标签: c++ iterator

在C ++编程手册中,我看到了std::list迭代器的以下内容:

for (iterator = list.start(); iterator != list.end(); iterator++)

始终致电list.end()效率不高吗?将结尾保存到另一个变量或者C ++编译器(即g ++)会自动处理这个变量会更好吗?

8 个答案:

答案 0 :(得分:8)

list::end()应该具有恒定的时间复杂度,特别是链接列表意味着它可能非常有效。

如果您的算法允许,那么存储该值可能稍微有点效率(同样,对于特别是链接列表,差异可能不大)。

哦,并且确实阅读了Steve Jessop关于自己测试效率的答案!

答案 1 :(得分:4)

这不太可能有任何区别。

标准容器函数内联,因此应该没有明显的函数调用开销。剩下的是优化器是否足够智能以避免不必要的开销,而这些开销并不是执行比较所必需的。例如:它是否实际创建了一个临时list::iterator对象,填充其当前位置字段,然后再读取该字段,或者比较最终只是来自iterator的值之间的指针比较和列表头部的值?

即使存在一些不必要的开销,与增加迭代器相比,它可能可以忽略不计,与循环体相比甚至可以忽略不计。

你可以测试它,这比猜测更可靠。请记住启用优化 - 没有优化的测试性能就像是说Blake必须比Bolt快,如果Blake从预热轨道走得更快到公共汽车。

答案 2 :(得分:4)

一般来说,不,它效率不高。 end()通常是内联函数,编译器将生成良好的代码来执行任何操作。更重要的是,相比什么效率低?是的,你可以添加代码来创建一个变量来保存结果,这可能会或者可能不会比简单地调用end()快一点。这样的改变似乎不太可能产生足够大的速度差异,将程序太慢转变为符合要求的程序。

答案 3 :(得分:4)

std::list<T>::end()的调用不太可能是一个很大的效率问题,可能只是读取一个值。但是,您可以通过将编译器存储为变量来为编译器提供一个暗示,它不会改变。对于其他容器,除了读取更多涉及的基址之外,还可能涉及计算。仍然没有任何戏剧性,但可能值得避免。

但是,请注意,它也可能会改变循环的语义:如果循环体可能附加元素,则前端可能会移动。有趣的是,我没有在标准中找到任何具体要求,说明在将元素插入容器时std::list<T>::end()是否可能发生变化(我可以想象它确实发生变化的实现以及它不变的情况;很可能是它虽然不会改变。如果您想在修改列表时获得有保证的行为,您可以在每次迭代中调用list.end()

顺便说一下,关于使用iterator++代替++iterator,我有一个更大的性能问题,特别是这正是作者在本书中使用的内容。尽管如此,这是一个微观优化,比如存储list.end()的结果,但要做一个便宜的事。

答案 4 :(得分:2)

实际上,对于STL容器,container::end()非常便宜。实际上,C ++标准强制要求几种类的几种方法的算法复杂度(如果不是全部),container::end()总是恒定时间。

此外,编译器可以自由地内联这些方法,从而基本上消除了它可能带来的任何开销。我认为没有其他方法可以在一个固定时间内结束列表,而不是存储它,因此你的list.end()调用可能最终成为一个字段访问,这在x86平台上并不比存储它更昂贵。叠加。

您的里程可能因其他系列而异,但可以肯定list.end()不会成为您的瓶颈。

答案 5 :(得分:1)

如果你需要进行微观优化,是的。

通常,调用list.end()不会对性能造成重大影响,并且可能不会成为问题。它可以在每次调用时返回相同的值,可以内联,等等。虽然不是,但确实需要一些时间。

如果你绝对需要速度,你想使用for (iterator = list.start(), end = list.end; iteration != end; ++iterator)。这会缓存结束迭代器(并执行pre-inc),并且不应重复调用。

第二种类型通常是不必要的,但如果.end()价格昂贵或者循环非常大,则可能有用。

答案 6 :(得分:1)

虽然过早的优化是邪恶的,但良好的习惯却不是。如果您不希望您的循环终止条件发生变化,即您没有更改容器,则可以使用此模式:

for (mylist::iterator it = alist.begin(), finish = alist.end();  it != finish;  ++it)

如果无法确定容器没有更改,编译器不太可能为您进行此优化。

请注意,这不太可能产生可衡量的时间差异,但它不会受到伤害。

答案 7 :(得分:0)

不在end()上缓存std::list的一个好理由是它可以防止您犯下以下错误:

for (iterator = list.rstart(), end = list.rend(); iterator != end; iterator++) {
    // modify list

std::list中进行修改时,没有迭代器会失效,但rend不是哨兵(它指向下层列表的第一个元素),这意味着它将不再是如果您追加到反向列表的末尾(也就是前置到未反转列表的开头),则列表的末尾。