免责声明:我不是编译专家。我只是好奇,并寻求启示。

我已经看到,人们声称 - 对于效率 - 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()”功能是奇怪的和腥味 - 可能会等待能够看到它继续下去,然后关闭。所以“效率”有点脱掉桌子。

但是,总的来说,对于大多数事情,效率主要是关于它可以编码的速度以及(更重要的是)据了解有多迅速。

许可以下: CC-BY-SA归因
不隶属于 cs.stackexchange
scroll top