C++ 编译器是否处理诸如建筑物是向量之类的情况:
for (int i = 0; i < buildings.size(); i++) {}
也就是说,它是否在循环中注意到建筑物是否被修改,然后 基于此不评估它每次迭代?或者也许我应该自己做这件事 不是那么漂亮,但是:
int n = buildings.size();
for (int i = 0; i < n; i++) {}
buildings.size()
可能会被编译器内联以直接访问 vector<T>
类上的私有大小字段。所以你不应该将调用分开到size
。无论如何,这种微观优化是您不想担心的事情(除非您处于某个通过分析确定为瓶颈的非常紧密的循环中)。
不要通过考虑性能来决定选择其中之一;您的编译器可能会或可能不会内联调用 - 并且
std::vector::size()
也具有恒定的复杂性。
您真正应该考虑的是正确性,因为如果您在迭代时添加或删除元素,两个版本的行为将会非常不同。
如果在循环中不以任何方式修改向量,请坚持使用以前的版本以避免出现一点状态(
n
变量)。
如果编译器可以确定
buildings
在循环内没有发生变化(例如,如果它是一个简单的循环,没有可能产生副作用的函数调用),它可能会优化计算。但计算向量的大小无论如何都是一次减法,这也应该相当便宜。
以明显的方式编写代码(
size
在循环内),并且只有当分析显示代码太慢时,您才应该考虑替代机制。
我这样写循环:
for (int i = 0, maxI = buildings.size(); i < maxI; ++i)
同时处理许多问题:建议预先修复 max,不再考虑性能损失,合并类型。如果计算位于中间表达式中,则表明循环更改了集合大小。
太糟糕的语言不允许合理使用 const,否则它将是 const maxI。
OTOH 对于越来越多的情况,我宁愿使用一些算法,lambda 甚至允许让它看起来几乎像传统代码。
假设
size()
函数是基本模板的内联函数,我们也可以假设它的开销非常小。它与 C 语言中的 strlen()
有很大不同,后者可能会产生很大的开销。
使用
int n = buildings.size();
可能仍然更快 - 因为编译器可以看到 n
在循环内没有改变,因此将其加载到寄存器中,而不是间接获取向量大小。但这是非常边缘的,只有真正紧密的、高度优化的循环才需要这种处理(并且只有在分析并发现这是一个好处之后),因为在这方面事情并不总是像您期望的那样工作。
只有在确实是性能问题时才开始手动优化此类内容。然后测量差异。否则,您将产生大量难以维护的丑陋代码,这些代码更难以调试并且使用效率较低。如果循环内的大小没有改变,大多数领先的编译器可能会优化它。
但即使它没有被优化掉,那么它也可能会被内联(因为模板默认是内联的)并且几乎不需要任何成本。