有人可以用这个小程序来解释这里发生了什么吗?
#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
。这没有多大意义。或者更确切地说,这背后必定有一些逻辑。
您能否解释一下发生了什么以及如何缓解这个问题?
我处理一个处理大量数据的程序,在我看来,我遇到了同样的性能问题。
我正在考虑从浮点数转换为整数但似乎整数表示它的行为相同。
编辑:最后解决方案是微不足道和合乎逻辑的。所以我感谢所有答案和解释!
答案 0 :(得分:13)
在第一个例子中,计算值是恒定的。编译器将在编译时计算c = 0.7 * 0.577 - 0.2 * 0.921
。它甚至可以自由地优化循环,因为它内部没有任何变化(a
,b
&amp; c
是不变的。)
在第二个实例中,a
和c
每次迭代都有所不同,因此必须计算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;
后,循环中唯一的表达式是循环不变的。您的优化器知道这一点,因此它将计算移出循环。然后优化器注意到循环中没有任何东西,所以它摆脱了循环。
当您放回行时,表达式不再是循环不变的。