我正在用C语言写一个循环,我只是想知道如何优化它。这里并不重要,因为我只是在练习,但为了进一步了解,我想知道:
在循环中,例如以下代码段:
int i = 0;
while (i < 10) {
printf("%d\n", i);
i++;
}
处理器是否每次迭代都检查(i < 10)
和(i == 10)
?或者它只是检查(i < 10)
,如果是真的,继续吗?
如果同时检查两者,则不会:
int i = 0;
while (i != 10) {
printf("%d\n", i);
i++;
}
效率更高?
谢谢!
答案 0 :(得分:10)
两者都将在单个汇编指令中翻译。对于EQUAL和NOT EQUAL,大多数CPU都有比LESS THAN更少或者等于的比较指令。
答案 1 :(得分:5)
关于这些优化问题的一个有趣的事情是,它们经常说明为什么在编写这些操作的性能影响之前应该编写清晰度/正确性的代码(这通常没有任何区别)。
您的2个示例循环不具有相同的行为:
int i = 0;
/* this will print 11 lines (0..10) */
while (i <= 10) {
printf("%d\n", i);
i++;
}
和
int i = 0;
/* This will print 10 lines (0..9) */
while (i != 10) {
printf("%d\n", i);
i++;
}
要回答你的问题,几乎可以肯定这两个结构的性能是相同的(假设你修复了问题,所以循环计数是相同的)。例如,如果您的处理器只能在两个单独的步骤(这将是一个非常不寻常的处理器)中检查相等以及一个值是否小于另一个值,那么编译器可能会将(i <= 10)
转换为{{1测试 - 或者可能是(i < 11)
测试。
答案 2 :(得分:3)
这是早期优化的一个明显例子....恕我直言,这是程序员对他们的手艺不熟悉的方式,容易担心。如果您必须担心它,请学会对代码进行基准测试和分析,以便您的担忧基于证据而不是假设。
说出你的具体问题。首先,<=
未实现为在我职业生涯中遇到的任何C编译器中分别测试<
和==
的两个操作。这包括一些非常愚蠢的编译器。请注意,对于整数,a <= 5
与a < 6
的条件相同,如果目标体系结构要求仅使用<
,则代码生成器将执行此操作。
你的第二个担忧,即while (i != 10)
可能更有效率会引发一个有趣的防御性编程问题。首先,在任何合理的目标架构中都没有任何效率。但是,它可能会导致小错误导致更大的失败。考虑一下:如果循环体内的某些代码行修改了i
,比如说它大于10,可能会发生什么?循环结束需要多长时间,并且会出现错误的其他后果吗?
最后,当想知道这种事情时,通常有必要找出你正在使用的编译器实际生成的代码。大多数编译器都提供了执行此操作的机制。对于GCC,请了解-S
选项,该选项将导致它直接生成汇编代码而不是生成目标文件。
答案 3 :(得分:0)
运算符&lt; =和&lt;在汇编中是单指令,应该没有性能差异。 请注意,在某些处理器上测试0可能比测试任何其他常量要快一些,因此循环运行是合理的:
int i = 10;
while (i != 0)
{
printf("%d\n", i);
i--;
}
请注意,像这样的微优化通常只能获得更多的性能,更好地利用您的时间来使用高效的算法。
答案 4 :(得分:0)
取决于架构和编译器。在大多数体系结构中,只有<=
或相反的指令,可以否定,因此如果将其转换为循环,则比较很可能只是一条指令。 (在x86或x86_64上是一条指令)
编译器可能会将循环展开为十次i++
的序列,当只涉及常量表达式时,它甚至会优化++
并仅保留常量。
而Ira是对的,如果涉及printf
,则比较确实会消失,执行时间可能是数百万个时钟周期。
答案 5 :(得分:0)
处理器是否每次迭代都检查(i&lt; 10)和(i == 10)?或者它只是检查(i&lt; 10),如果是真的,继续吗?
两者都不会检查(i&lt; 11)。 <= 10
只是为了让您的代码具有更好的意义,因为11
是magic number,实际上意味着(10+1)
。
答案 6 :(得分:0)
// Case I
int i = 0;
while (i < 10) {
printf("%d\n", i);
i++;
printf("%d\n", i);
i++;
}
// Case II
int i = 0;
while (i < 10) {
printf("%d\n", i);
i++;
}
案例I代码占用更多空间但速度更快,Case II代码占用的空间更少,但与Case I代码相比速度较慢。 因为在编程空间中复杂性和时间复杂度总是相互成正比的。这意味着你必须妥协空间或时间 因此,通过这种方式,您可以优化时间复杂度或空间复杂度,但不能同时优化两者。
你的两个代码都是一样的。
答案 7 :(得分:0)
我正在用C语言写一个循环,我只是想知道如何优化它。
如果在启用优化的情况下进行编译,则最大的优化将来自展开该循环。
用-O2来分析代码是很困难的,因为对于琐碎的函数,编译器将展开循环,你将无法对比较中的实际差异进行基准测试。在分析使用常量的测试用例时,您应该小心,这些常量可能会在编译器优化时使代码变得微不足道。
答案 8 :(得分:0)
拆卸。根据处理器,优化和许多事情,这个简单的示例代码实际上展开或做的事情不能反映您的真实问题。使用gcc -O1进行编译虽然您提供的两个示例循环都导致相同的汇编程序(用于arm)。
如果大于或等于循环的远端,则经常会在C代码中变成分支。如果你的处理器没有大于或等于它可能有一个分支如果大于和一个分支如果相等,两个指令。
通常会有一个寄存器持有i。将有一个增加i的指令。然后用一条指令将i与10进行比较,然后等于,大于或等于,小于一般指令,这样你通常不会看到差异。