有点奇怪:前一段时间我被一位朋友告知我重新安排了这个例子for
循环:
for(int i = 0; i < constant; ++i) {
// code...
}
为:
for(int i = 0; constant > i; ++i) {
// code...
}
会稍微提高C ++的性能。我没有看到如何将常量值与变量进行比较比反之亦然,并且我运行的一些基本测试没有显示两种实现之间的速度差异。测试这个Python while
循环也是如此:
while i < constant:
# code...
i += 1
VS
while constant > i:
# code...
i += 1
我错了吗?我的简单测试不足以确定速度变化吗?这是否适用于其他语言?或者这只是一种新的最佳实践?
答案 0 :(得分:45)
更多的是在C ++民间传说中,手工微优化在特定版本的特定编译器上运行一次,并且随着某种传说将拥有者与普通群体区分开来传递下去。这是垃圾。剖析是真理。
答案 1 :(得分:17)
可能不是,但如果确实如此,编译器可能会自动为您进行优化。所以无论如何都要使你的代码最具可读性。
答案 2 :(得分:10)
我的怀疑是你的朋友100%错了。但我不相信我的意见,而不是相信你的朋友。事实上,如果存在性能问题,那么只有一个人应该信任。
这是唯一的方式,您可以声称任何一种方式比另一种更快或更快的权威。
答案 3 :(得分:8)
你给出的例子在C ++中绝对没有性能差异,我怀疑他们在Python中也会有所不同。
也许你会将它与其他优化混淆:
for (int i = 0; i < variable; ++i)
// ...vs...
for (int i = variable; i ; --i)
后者在某些体系结构中更快,因为递减变量的行为将设置零标志,然后可以在跳转 - 如果不是零指令中检查,给出循环迭代和条件一次性。前一个示例需要执行显式比较或减法来设置标志,然后根据它进行跳转。
然而,大多数情况下编译器可以将第一种情况优化为第二种情况(特别是如果它看到该变量实际上是一个常量),并且在一些编译器/体系结构组合指令上可能是生成,使第一个方法更像第二个方法。这样的事情只值得尝试,如果你有一个严密的内循环,你的探查器告诉你是昂贵的,但你永远不会注意到差异,否则,如果有一个。
答案 4 :(得分:5)
假设short-circuit evaluation,唯一一次这会产生很大影响的是你在循环中调用慢速函数。例如,如果您有一个从数据库查询值并返回它的函数,那么这个:
while(bContinue && QueryStatusFromDatabase==1){
} //while
会比以下快得多:
while(QueryStatusFromDatabase==1 && bContinue){
} //while
即使它们在逻辑上相同。
这是因为第一个可以在一个简单的布尔值为FALSE时立即停止 - 只有在布尔值为TRUE时才运行查询,但第二个将总是运行查询。
除非您需要将所有可能的CPU周期从循环中挤出,否则这些极端情况可能是唯一值得花时间的情况。可以这样想:为了弥补你花时间问这个问题可能需要几十亿次循环。
最糟糕的是当你有一个函数作为一个条件,并且该函数具有代码中某些其他位置秘密期望的副作用。因此,当您进行小优化时,副作用只会发生某些时间,并且您的代码会以奇怪的方式中断。但这有点像切线。对你的问题的简短回答是“有时,但通常没关系。”
答案 5 :(得分:4)
虽然分析是最好的,但它不是唯一方式。
您可以比较每个选项创建的程序集,这对于像这样的微优化不应该是不可能的。对硬件平台命令进行一些研究可以让你有一个好主意,如果这个变化完全不同,以及它可能有不同的表现。我假设您将计算移动次数并比较命令。
如果您的调试器允许您在源代码和反汇编视图之间切换,那么踩到它应该非常简单。
答案 6 :(得分:3)
更好的做法是不要偏离这样的优化调整,这会给你带来微不足道的好处(假设 是一个调整)。
答案 7 :(得分:2)
任何理智的编译器都将以相同的方式实现。如果某个体系结构上的一个比另一个体系结构更快,编译器将以这种方式对其进行优化。
答案 8 :(得分:1)
与0比较非常快,所以这实际上会稍快一点:
for (int i = constant; i > 0; --i)
{
//yo
}
我认为最好在任何情况下都使用!=
,因为它更易于检测一个错误,并且是使用具有非连续数据结构的迭代器的唯一方法,例如链表。
答案 9 :(得分:0)
今天,在一个好的编译器上,根本没有。
首先,操作数顺序对我看到的指令集没有任何影响。其次,如果有一个,任何体面的优化器都会选择更好的优化器。
但是,我们不应盲目地放弃表现。响应性仍然很重要,计算时间也是如此。特别是在编写库代码时,您不知道何时连续调用200万次。此外,并非所有平台都是平等的。嵌入式平台通常在低(呃)处理能力和实时处理要求的基础上受到低于标准的优化器的影响。
在桌面/服务器平台上,权重已经转移到封装良好的复杂性,从而实现更好的缩放算法。
微观优化只有当他们伤害其他内容时才会变坏,例如可读性,复杂性或可维护性。当其他条件相同时,为什么不选择更快的?
有一段时间在x86上结束循环为零(例如通过倒计时)实际上可以给紧密循环带来显着的改进,因为DEC CX/JCXNZ
更快(它仍然可能,因为它可以节省比较的寄存器/存储器访问;编译器执行优化通常超出现在的范围)。你朋友听到的内容可能是错误的版本。
答案 10 :(得分:0)
我谦卑地建议,在某些架构上的某些编译器上,以下内容可以比变体更有效地降低:
i = constant - 1
while (--i) {
}
获取常量迭代。
正如许多评论所暗示的那样,编译器会很好地为您优化循环(编译器优化人们花了很多时间考虑它)。合法的代码可能更有价值,但是YMMV!
如果您真的希望优化超出您认为的编译器可能做的,我建议查看高级语言生成的程序集,并考虑从那里进一步优化。
在高级别上,您还可以通过使用OpenMP获得更高的性能,或者通过向量指令集(例如MMX)在较低级别上获得显着更高的性能,以在单个指令中进行多次计算。这有点超出了问题的范围,你必须提供更多关于循环在做什么的有用建议的信息。
希望有帮助&amp;欢呼声。
答案 11 :(得分:0)
提供的优化只会为给定的编译器(可能)优化更多。抽象地说,它应该生成相同的代码。
如果您正在进行微优化 - 假设满足微优化的要求 - 您的第一步应该是查看生成的装配,然后查看您的架构的装配手册。
例如,i ++可能比i + 1更快。要看。在天真的CPU中,等于0比低于等于快得多。如果您的编译器/ CPU不支持指令重新排序,您可能会发现通过计算进行散布分配会加快代码的速度。 (某些计算可能会导致管道停顿)但是,您必须明确确定您的编译器/体系结构组合。
坦率地说,我不打算做这个级别的优化,除非我绝对需要来自我的处理器的每一个周期。传统上,图形或科学计算是你需要这种东西的地方[*]。
*我知道一个程序,经过几个月的优化和在现代机器上,仍然需要几个月的时间来处理数据。单个数据集的运行时间在周范围内。有很多数据可供使用....
答案 12 :(得分:-1)
这绝对是微优化的一个例子,真的不需要做。
确实(特别是)在C ++中,后增量操作和预增量操作之间的性能差异很小,但今天的编译器中的差异通常可以忽略不计。更改条件顺序的原因是由于从后增量到预增量的变化。