算术运算的不成比例处理时间[C]

时间:2013-07-22 21:24:23

标签: c performance gcc

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

#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.001s而不是2.444s。这没有多大意义。或者更确切地说,这背后必定有一些逻辑。

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

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

我正在考虑从浮点数转换为整数但似乎整数表示它的行为相同。

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

5 个答案:

答案 0 :(得分:13)

在第一个例子中,计算值是恒定的。编译器将在编译时计算c = 0.7 * 0.577 - 0.2 * 0.921。它甚至可以自由地优化循环,因为它内部没有任何变化(ab&amp; c是不变的。)

在第二个实例中,ac每次迭代都有所不同,因此必须计算100000000次。

答案 1 :(得分:3)

好的优化者非常好。

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

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

因此时间上的差异。

答案 2 :(得分: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);
}
当且仅当该行被评论时,

产生相同的答案。

答案 3 :(得分: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

完全不同,正如你所看到的那样。

答案 4 :(得分:2)

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

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