在循环条件下重复计算表达式是否低效? 作为初学者,我经常看到包含函数调用和一些计算密集型表达式的循环条件,如下面(例如从C++引物,循环语句):

在循环条件下重复计算表达式是否低效? 作为初学者,我经常看到包含函数调用和一些计算密集型表达式的循环条件,如下面(例如从C++引物,循环语句):,c++,C++,我关注的是: 由于在每次迭代中都会检查条件,因此在条件内部计算表达式(这里它运行end()函数)的代价不是很高吗?首先,这实际上可能不会对性能产生可测量的影响:如果循环体花费的时间要长好几倍(对于普通代码来说很常见),或者循环作为一个整体不是瓶颈,对循环条件的任何优化都是浪费 其次,end()的常规实现是内联的完美候选。一旦它被内联,因为end()一定不会有副作用,迭代器构造会受到循环不变代码运动的影响。在这两个转换之后,“手动优化”版本实际上没有什么不同,程序员没有额外的工作 第三,即使第二点

我关注的是:
由于在每次迭代中都会检查条件,因此在条件内部计算表达式(这里它运行
end()
函数)的代价不是很高吗?

首先,这实际上可能不会对性能产生可测量的影响:如果循环体花费的时间要长好几倍(对于普通代码来说很常见),或者循环作为一个整体不是瓶颈,对循环条件的任何优化都是浪费

其次,
end()
的常规实现是内联的完美候选。一旦它被内联,因为
end()
一定不会有副作用,迭代器构造会受到循环不变代码运动的影响。在这两个转换之后,“手动优化”版本实际上没有什么不同,程序员没有额外的工作


第三,即使第二点不适用,对于大多数代码来说,更简洁的代码代价很小,特别是对于初学者。使上述优化变得微不足道的同样原因也使得人类应用相同的转换变得微不足道,如果需要的话

因为书籍是说教性的,可读性比性能更重要。对于初学者来说,如果不必弄清楚优化技巧,那么就更容易掌握算法的原理

在现实生活中,这也是有道理的:结束条件往往是复杂的,并且受每次迭代的影响。因此,让终端条件变得易懂,并隐藏实现细节,这样更安全、更易于维护

如果性能真的很重要,首先想想B.Kernighan的说法:“不要为了提高速度而欺骗代码,而是寻找更好的算法”

对于非常简单的时间关键型循环,值得一看。编写循环的方式很重要。这里有一些关于字符串搜索的大型迭代的基准:

  • 每次检查.end()的传统迭代器方法:14秒
  • 在变量的循环开始处缓存.end():10秒,比原来好30%
  • 使用计数器和索引,每次检查.size():860毫秒
  • 从最后开始倒数到0:670毫秒,好22%
  • 使用指向c_str()的指针,将整个过程简化为。。。63毫秒

但是请记住,最好的机会是你的编译器。启用优化后,计时为1274毫秒、677毫秒、42毫秒、42毫秒和33毫秒,即从50%提高到92%

我们怎么知道你的书(我们不知道的那本书,顺便说一句)的作者为什么选择了他们所做的代码示例?联系那个作者。你已经发布了一行代码,完全脱离了任何上下文,没有书中肯定包含的解释性文本,并要求我们解释作者的思维过程。我们很难做到这一点,除非书的作者碰巧经常出现在这个网站上,碰巧看到了这个问题,碰巧认识到了自己的代码示例。书不一定是完美的。它们往往有很大的缺陷。无论如何,这种微观优化几乎总是不相关的(并且可以由编译器执行)。什么书?我们不是作者。这不是一个编程问题。这是一个你使用某物的例子,它取决于编译器。他们可能会为您对此进行优化,使最终条件只评估一次。@Cyber,回答得好。轻量级问题换取轻量级答案。“整个循环的最大效率损失与循环体的成本成反比”说什么?@LightnessRacesinOrbit:显然他指的是
结束
调用的执行时间贡献,相对于整个循环,它的贡献是。。。与环体的成本成反比,当环体的成本被假定为更高时。这当然是真的,但我不认为这是一个有用或有用的指标。@LightnessRacesinOrbit Cheers说得对,我需要找到一个更好的方法来表达这个词。重点是,如果循环体所用的时间超过
end()
(对于许多有趣的代码来说,它确实如此),那么
end()
调用是考虑优化的错误地方。@delnan:当然,这也是正确的,但它仍然只是反对不必要的微优化的一个论点;这与说它实际上不是一个优化不同。我认为OP是在寻找它可以优化的确认,而不是一个人应该麻烦的确认。我认为你的第二段很好地涵盖了它的其余部分。
for(int *beg = begin(array); beg != end(array); ++beg) ;