6

有人可以解释这个小程序发生了什么吗?

#include<stdio.h>

int main()
{
    float a=0.577;
    float b=0.921; 
    float c; 
    int i;

    for (i=0;i<100000000;i+=1){
        c=0.7*a-0.2*b;
        //a=0.145*c+2.7*b;
    }

    printf ("%.3f\n",c);
}

注意,有一行注释掉了。

我先编译它没有线,然后用线。(使用gcc -O2 ...)。并测量处理时间。我很惊讶地发现执行时间0.001s2.444s. 这没有多大意义。或者更确切地说,这背后一定有一些逻辑。

你能解释一下发生了什么以及如何缓解这个问题吗?

我在一个处理大量数据的程序上工作,在我看来,我在那里遇到了同样的性能问题。

我正在考虑从浮点数切换到整数,但似乎整数的行为相同。

编辑:最后,解决方案是微不足道和合乎逻辑的。所以我感谢所有的答案和解释!

4

5 回答 5

13

在第一种情况下,计算值是恒定的。编译器会c = 0.7 * 0.577 - 0.2 * 0.921编译时计算。它甚至可以自由地优化循环,因为它内部没有任何变化(a, b&c是不变的)。

在第二种情况下,a并且c每次迭代都不同,因此必须计算100000000时间。

于 2013-07-22T21:26:19.940 回答
3

好的优化器非常好。

由于单行计算在每次迭代中返回相同的值,因此无需在循环中重新计算任何内容,因此优化器不需要。

当您也更改时a(使用两行计算),则它必须运行循环。

因此时间上的差异。

于 2013-07-22T21:27:15.677 回答
2

如果没有注释掉的行,编译器可以优化整个循环。设置的值不会随着循环而改变。

使用注释掉的行,a每次循环开始时的值都会发生变化,因此无法优化循环。

也就是说,你的程序和这个:

int main()
{
    float a=0.577;
    float b=0.921; 
    float c; 
    int i;

    c=0.7*a-0.2*b;
    for (i=0;i<100000000;i+=1){
        //a=0.145*c+2.7*b;
    }

    printf ("%.3f\n",c);
}

当且仅当该行被注释时才会产生相同的答案。

于 2013-07-22T21:27:05.833 回答
2

这是我在启用优化的情况下编译您的示例所获得的代码:

(__TEXT,__text) section
_main:
0000000100000f20    pushq   %rbp
0000000100000f21    movq    %rsp, %rbp
0000000100000f24    leaq    61(%rip), %rdi ## literal pool for: %.3f

0000000100000f2b    movsd   45(%rip), %xmm0
0000000100000f33    movb    $1, %al
0000000100000f35    callq   0x100000f3e ## symbol stub for: _printf
0000000100000f3a    xorl    %eax, %eax
0000000100000f3c    popq    %rbp
0000000100000f3d    ret

请注意,循环甚至没有运行——编译器已经完全优化了它,因为它可以告诉唯一c重要的分配是最后一个。

相反,重新插入注释行后,循环必须运行,输出代码如下所示:

(__TEXT,__text) section
_main:
0000000100000ea0    pushq   %rbp
0000000100000ea1    movq    %rsp, %rbp
0000000100000ea4    movss   148(%rip), %xmm5
0000000100000eac    movl    $100000000, %eax
0000000100000eb1    movsd   143(%rip), %xmm1
0000000100000eb9    movsd   143(%rip), %xmm2
0000000100000ec1    movsd   143(%rip), %xmm3
0000000100000ec9    movsd   143(%rip), %xmm4
0000000100000ed1    nopw    %cs:(%rax,%rax)
0000000100000ee0    xorps   %xmm0, %xmm0
0000000100000ee3    cvtss2sd    %xmm5, %xmm0
0000000100000ee7    mulsd   %xmm1, %xmm0
0000000100000eeb    addsd   %xmm2, %xmm0
0000000100000eef    cvtsd2ss    %xmm0, %xmm0
0000000100000ef3    cvtss2sd    %xmm0, %xmm0
0000000100000ef7    movaps  %xmm0, %xmm5
0000000100000efa    mulsd   %xmm3, %xmm5
0000000100000efe    addsd   %xmm4, %xmm5
0000000100000f02    decl    %eax
0000000100000f04    cvtsd2ss    %xmm5, %xmm5
0000000100000f08    jne 0x100000ee0
0000000100000f0a    leaq    87(%rip), %rdi ## literal pool for: %.3f

0000000100000f11    movb    $1, %al
0000000100000f13    callq   0x100000f1c ## symbol stub for: _printf
0000000100000f18    xorl    %eax, %eax
0000000100000f1a    popq    %rbp
0000000100000f1b    ret

如您所见,完全不同。

于 2013-07-22T21:27:35.810 回答
2

a=0.145*c+2.7*b;注释掉该行后,循环内的唯一表达式是循环不变的。您的优化器知道这一点,因此它将计算移到循环之外。然后优化器注意到循环中没有任何内容,所以它摆脱了循环。

当您放回该行时,表达式不再是循环不变的。

于 2013-07-22T21:27:36.950 回答