for循环是否在每次迭代中重新评估其体内的函数?

时间:2017-04-14 19:55:29

标签: java c++ c for-loop

当我像这样写一个简单的for循环时

for (int i = 0; i < myList.size(); i++){}

在Java,C和C ++中,它是在每次迭代中重新评估myList.size()还是在循环开始时重新评估一次?如果重新评估,是否要预先评估大小,即

int n = myList.size();
for (int i = 0; i < n; i++){}

在性能方面给了我们一些重要的东西?

6 个答案:

答案 0 :(得分:7)

对于Java:

for (int i=0; i<myList.size(); i++){}

每次循环迭代期间评估条件;因此,通过将该调用移至循环前的size(),可以获得轻微的性能提升。

但在Java中你应该更喜欢for-each

for (Whatever thingy : list)

符号无论如何(如果可能)。

您可以在Java语言规范第14.14.1章中阅读。

答案 1 :(得分:5)

在C和C ++中,控制表达式在循环的每个迭代中进行评估。

来自C standard的第6.8.5.3节:

  

声明

for (clause-1; expression-2; expression-3) statement
     

表现如下:表达式expression-2是   控制在每次执行之前计算的表达式   循环体。表达式expression-3被评估为   每次执行循环体后的void表达式。如果   clause-1是一个声明,它声明的任何标识符的范围   是声明的剩余部分和整个循环,包括   其他两个表达;它是按照执行顺序到达的   控制表达的第一次评估。如果clause-1是   表达式,它在第一个之前被计算为void表达式   评价控制表达。

C++ standard的第6.5.3节包含类似的措辞。

因此,如果您的控制表达式涉及函数调用或其他可能在循环体中不会改变的潜在昂贵的计算 ,您应该事先对其进行评估,将其存储在变量中,然后使用控制表达式中的变量。

答案 2 :(得分:2)

逻辑在每次迭代时评估条件。

您可以通过查看示例来说明这一点,因为表达式中i的值每次都会更改。

在实践中,它可能依赖于语言或实现。编译器或运行时可能能够缓存或优化表达式的一部分。你无法单独查看代码。

答案 3 :(得分:2)

取决于是否在for循环中修改myList。此外,编译器(特别是在C中)尝试通过在循环外部移动评估来优化此类代码。优化还取决于对象是否在循环内修改。

答案 4 :(得分:0)

在C和C ++中,标准中没有要求优化函数调用(例如,只调用一次)。

编译器的优化引擎需要知道size的返回类型以及值是否会更改。艰难的任务。

如果希望调用函数一次,请在循环之前将结果赋值给常量变量:

const size_t size = myList.size();
for (size_t i = 0; i < size; ++i)
{
  // ...
}

如果在循环内更改myList,则保证关闭。每次迭代都需要调用函数size

答案 5 :(得分:0)

我只熟悉C / C ++,正确的答案是:有时会对其进行评估,有时则不进行评估。编译器保证它将在每次迭代中评估一个汇编代码,它 THINKS IS EQUIVALENT 与原始代码(但实际上并不是你想要的)。如果您在调试模式下编译代码(没有编译器优化)或者您的变量在其定义中具有关键字volatile,或者您更改了列表的大小,那么每次迭代都会确实评估条件(列表的大小)。但是,如果您使用速度优化进行编译,则不要在循环中更改列表,那么现代编译器足够智能,可以将列表的大小存储在寄存器中,而不是每次迭代都对其进行评估。此外,函数list.size()将被内联,并且实际上不会调用昂贵的函数调用。

请注意,在多线程编程中,这是灾难性的。一个线程可以将元素附加到列表,而遍历列表的另一个线程将永远不会看到新元素。要强制进行评估,列表的大小必须定义为 volatile (或者,如果您熟悉程序集,则可以使用内存障碍)。无论如何,当您编译生产应用程序时,请务必启用编译器优化,并且每次迭代中所谓的函数调用的运行时间代价将被取消。

另一个问题:一些非常积极的编译器优化甚至可能展开你的循环。例如,如果列表的大小为403个元素,则编译器可以执行100次迭代,每个迭代使用您的原始代码重复4次+ 3次迭代另一个循环。因此总共有103次迭代而不是403次(所以不可能争论代码是否在每次迭代中执行,因为很难定义什么是迭代)。 此类积极优化的示例:复制长字符串或内存缓冲区。如果你复制X字节,在64位机器上实际上更快的是进行X / 8次迭代(一次复制64位)+另外一个循环中剩余的0..7个字节。有些处理器甚至在单个操作中支持128和512字节的单元

最后建议:编写一个易于理解的代码非常重要,所以在这种情况下我会在上面的答案中使用好的建议(关于java列表迭代器)或故意将list.size()保存到时间变量中

const in list_size = list.size()

只是为了更容易阅读/调试