编译器如何优化这段代码

3

考虑以下循环:

unsigned long x = 0;
for(unsigned long i = 2314543142; i > 0; i-- )
    x+=i;
std::cout << x << std::endl;

当我正常编译时,执行此循环大约需要6.5秒钟。但是当我使用-O3优化进行编译时,循环在10 ^ -6秒内执行。这怎么可能?编译器肯定不知道x的闭合表达式...


2
你尝试过输出汇编语言来查看生成的代码吗? - Mikel F
但是这里的人们会... 这将是回答你问题的关键。 - Mikel F
编译器肯定知道这里的闭式表达式。计算并不难。 - zch
3
等一下,i 是无符号的。i >= 0 将总是为真。你在这里有未定义的行为。我很惊讶在默认优化级别下循环竟然结束了。 - user2357112
1
1n的所有整数之和在数学上等于n*(n+1)/2(即可以使用加法、乘法和除法计算,而不是n次加法)。许多编译器优化器的一个特点是它们识别可以更简单地计算结果的操作序列(例如,在这种情况下是一个闭合形式),并且在编译时进行计算或发出简单的代码以在运行时执行。 - Peter
显示剩余7条评论
2个回答

4

如果开启了优化编译,编译器就能在编译期间确定x的值,这并不需要你完全了解汇编语言。

我稍微修改了你的代码,以便使用在线工具Compiler Explorer,将std::cout << x << std::endl改为extern unsigned long foo;foo = x;。虽然不是必须的,但可以使输出更加干净。

使用-O2编译:

test():
        movabs  rax, 2678554979246887653
        mov     QWORD PTR foo[rip], rax
        ret

使用-O0编译:

test():
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], 0
        mov     DWORD PTR [rbp-16], -1980424154
        mov     DWORD PTR [rbp-12], 0
        jmp     .L2
.L3:
        mov     rax, QWORD PTR [rbp-16]
        add     QWORD PTR [rbp-8], rax
        sub     QWORD PTR [rbp-16], 1
.L2:
        cmp     QWORD PTR [rbp-16], 0
        setne   al
        test    al, al
        jne     .L3
        mov     rax, QWORD PTR [rbp-8]
        mov     QWORD PTR foo[rip], rax
        leave
        ret

另外,由于代码中的i >= 0存在未定义行为,因此第一次修订后,仅输出:
test():
.L2:
        jmp     .L2

:-)


似乎编译器在编译时计算了某些东西。但是我无法在任何地方找到输出值2678554979246887653... - user3726947
@user3726947 抱歉,我不小心使用了extern unsigned foo而不是extern unsigned long foo。更新后的答案显示了正确的输出值。 - simon

3
编译器确定循环后x的值,并在输出语句中使用它。

相对于程序的长度来说,编译肯定是相当缓慢的。 - Scott Hunter
虽然我没有测量它,但编译时间似乎几乎是瞬间的。肯定不会比没有-O3优化的编译时间更长。 - user3726947

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接