优化C代码中的汇编代码冗余
我试图通过学习使用-O3优化编译成gcc的简单C代码来了解矢量化。 更具体地说,编译器的矢量化程度如何。 这是通过更复杂的计算来验证gcc -O3性能的个人旅程。 我明白,传统的观点认为编制者比人更好,但我从不认为这种看法是理所当然的。
然而,在我的第一个简单测试中,我发现gcc做出的一些选择非常奇怪,并且在优化方面非常严重地疏忽了。 我愿意假设编译器是有目的的,并且知道一些关于CPU的情况(在这种情况下是Intel i5-2557M),我不知道。 但我需要一些有见识的人的确认。
我的简单测试代码(段)是:
int i;
float a[100];
for (i=0;i<100;i++) a[i]= (float) i*i;
生成的与for-loop对应的汇编代码(段)如下所示:
.L6: ; loop starts here
movdqa xmm0, xmm1 ; copy packed integers in xmm1 to xmm0
.L3:
movdqa xmm1, xmm0 ; wait, what!? WHY!? this is redundant.
cvtdq2ps xmm0, xmm0 ; convert integers to float
add rax, 16 ; increment memory pointer for next iteration
mulps xmm0, xmm0 ; pack square all integers in xmm0
paddd xmm1, xmm2 ; pack increment all integers by 4
movaps XMMWORD PTR [rax-16], xmm0 ; store result
cmp rax, rdx ; test loop termination
jne .L6
我理解所有的步骤,并从计算上来说,这一切都是有道理的。 我不明白,不过,是gcc选择在迭代循环纳入一个步骤XMM0装有XMM1后XMM0正确加载XMM1。 即
.L6
movdqa xmm0, xmm1 ; loop starts here
.L3
movdqa xmm1, xmm0 ; grrr!
这一点让我质疑优化器的理智。 显然,额外的MOVDQA不会干扰数据,但是在面值上,它似乎在gcc方面严重疏忽。
之前在汇编代码中(未显示), xmm0和xmm2被初始化为对矢量化有意义的某个值,所以显然,在循环开始时,代码必须跳过第一个MOVDQA。 但为什么gcc不重新排列,如下所示。
.L3
movdqa xmm1, xmm0 ; initialize xmm1 PRIOR to loop
.L6
movdqa xmm0, xmm1 ; loop starts here
或者甚至更好,只需初始化xmm1而不是xmm0,并完全转储MOVDQA xmm1 , xmm0步骤!
我准备相信CPU足够聪明,可以跳过冗余步骤或类似的事情,但是如何让gcc完全优化复杂的代码,如果它甚至可以使这个简单的代码正确呢? 或者可以有人提供一个合理的解释,让我相信gcc -O3是好东西?
我不是100%确定的,但它看起来像你的循环通过将其转换为float
来破坏xmm0
,所以你需要在xmm1
有整数值,然后复制到另一个寄存器(在这种情况下是xmm0
)。
虽然编译器有时会发出不必要的指令,但在这种情况下我无法真正了解情况如何。
如果你想xmm0
(或者xmm1
)保持为整数,那么不要为i
的第一个值创建一个float
。 也许你想要做的是:
for (i=0;i<100;i++)
a[i]= (float)(i*i);
但另一方面,gcc 4.9.2似乎没有这样做:
g++ -S -O3 floop.cpp
.L2:
cvtdq2ps %xmm1, %xmm0
mulps %xmm0, %xmm0
addq $16, %rax
paddd %xmm2, %xmm1
movaps %xmm0, -16(%rax)
cmpq %rbp, %rax
jne .L2
铿锵(3个星期前的3.7.0)
clang++ -S -O3 floop.cpp
movdqa .LCPI0_0(%rip), %xmm0 # xmm0 = [0,1,2,3]
xorl %eax, %eax
.align 16, 0x90
.LBB0_1: # %vector.body
# =>This Inner Loop Header: Depth=1
movd %eax, %xmm1
pshufd $0, %xmm1, %xmm1 # xmm1 = xmm1[0,0,0,0]
paddd %xmm0, %xmm1
cvtdq2ps %xmm1, %xmm1
mulps %xmm1, %xmm1
movaps %xmm1, (%rsp,%rax,4)
addq $4, %rax
cmpq $100, %rax
jne .LBB0_1
我已编译的代码:
extern int printf(const char *, ...);
int main()
{
int i;
float a[100];
for (i=0;i<100;i++)
a[i]= (float) i*i;
for (i=0; i < 100; i++)
printf("%fn", a[i]);
}
(我添加了printf以避免编译器摆脱所有的代码)
链接地址: http://www.djcxy.com/p/60381.html