cod*_*imp 7 c optimization assembly gcc
我正在尝试通过研究使用-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;
Run Code Online (Sandbox Code Playgroud)
与for循环对应的结果汇编代码(段)如下:
.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
Run Code Online (Sandbox Code Playgroud)
我理解所有步骤,并且在计算上,所有步骤都是有道理的.但是,我不明白,gcc选择在迭代循环中加入一个步骤,在xmm0加载xmm1之后立即加载xmm1和xmm0.即
.L6
movdqa xmm0, xmm1 ; loop starts here
.L3
movdqa xmm1, xmm0 ; grrr!
Run Code Online (Sandbox Code Playgroud)
仅这一点让我质疑优化器的完整性.显然,额外的MOVDQA不会干扰数据,但从面值来看,它似乎在gcc方面显得过于疏忽.
在汇编代码(未示出)的早期,xmm0和xmm2被初始化为对于矢量化有意义的某个值,因此很明显,在循环开始时,代码必须跳过第一个MOVDQA.但是为什么gcc不会简单地重新排列,如下所示.
.L3
movdqa xmm1, xmm0 ; initialize xmm1 PRIOR to loop
.L6
movdqa xmm0, xmm1 ; loop starts here
Run Code Online (Sandbox Code Playgroud)
或者甚至更好,只需初始化xmm1而不是xmm0并完全转储MOVDQA xmm1,xmm0步骤!
我愿意相信,CPU是足够聪明跳过多余的步骤或类似的东西,但我怎么能相信GCC全面优化复杂的代码,如果它甚至能得到这个简单的代码吧?或者有人能提供一个合理的解释,让我相信gcc -O3是好东西吗?
我不是 100% 确定,但看起来你的循环xmm0
通过将其转换为 而被破坏float
,因此你需要将整数值放入xmm1
,然后复制到另一个寄存器(在本例中为xmm0
)。
虽然众所周知编译器有时会发出不必要的指令,但我无法真正看出在这种情况下是如何发生的。
如果您希望xmm0
(或xmm1
) 保持整数,则不要float
对 的第一个值进行强制转换i
。也许您想做的是:
for (i=0;i<100;i++)
a[i]= (float)(i*i);
Run Code Online (Sandbox Code Playgroud)
但另一方面,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
Run Code Online (Sandbox Code Playgroud)
clang 也没有(大约 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
Run Code Online (Sandbox Code Playgroud)
我编译的代码:
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("%f\n", a[i]);
}
Run Code Online (Sandbox Code Playgroud)
(我添加了 printf 以避免编译器删除所有代码)