题
免责声明:我不是编译专家。我只是好奇,并寻求启示。
我已经看到,人们声称 - 对于效率 - for
环路通常应该使用零比较来终止。所以而不是:
void blink1(int n) {
for (int i=0; i<n; i++) {
blink_led();
}
}
.
你应该写:
void blink2(int n) {
for (int i=n; i>0; i--) {
blink_led();
}
}
.
我认为这有点愚蠢:为什么如果编译器可以将两种情况解释为“blink_led()n次”?
但使用先生govebolt的编译器资源管理器,我现在觉得我错了。对于我尝试的所有编译器,“零零比较”始终产生更短的循环。例如,具有-O3优化的X86-64 GCC 10.2产生了以下内部环:
blink1:
...
.L3:
xor eax, eax
add ebx, 1
call blink_led
cmp ebp, ebx
jne .L3
.
vs
blink2:
...
.L12:
xor eax, eax
call blink_led
sub ebx, 1
jne .L12
.
所以这是问题
这似乎是这样一个常见的情况。
为什么无法(或为什么不)编译器注意,for
循环的效果只是“执行此操作n次” - 无论是计数还是计数 - 并优化它?
解决方案
您读取的是完全废话,除非是编译器最重要的。首先,与整数的比较是快速的,可能比与常数的比较快。其次,一个很好的优化编译器将采用使用一些常见模式编写的循环,并将其转换为最佳代码;它可能无法识别您的混淆模式,并为其产生更好的代码。
最后,除非实际需要它,否则不应该用不可读的代码替换可读。如果您花了一个小时进行更改,则需要至少保存20小时的CPU时间。当您在那个级别时,更好的算法很可能会节省更好的节省。
其他提示
我想我大多同意@ gnasher729,但那些人担心“效率”是什么批评使用for循环 - “i”变量不会添加任何东西...
为什么不:
void blink3(int n) {
while (n-- > 0) {
blink_led();
}
}
.
我已经添加了“> 0”几个原因:1)以防某人要求负量眨眼,不想向后滚动20亿次。2)对集结(也许)有点明显。
顺便说一下,在创意的例子中,“blink_led()”功能是奇怪的和腥味 - 可能会等待能够看到它继续下去,然后关闭。所以“效率”有点脱掉桌子。 但是,总的来说,对于大多数事情,效率主要是关于它可以编码的速度以及(更重要的是)据了解有多迅速。不隶属于 cs.stackexchange