优化C代码中的汇编代码冗余

时间:2015-02-24 21:39:19

标签: c gcc optimization assembly

我正在尝试通过使用-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循环的结果汇编代码(段)如下:

.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 是好东西吗?

1 个答案:

答案 0 :(得分:4)

我不是100%肯定,但看起来你的循环通过将其转换为xmm0来销毁float,所以你要在xmm1中获得整数值然后复制到另一个寄存器(在这种情况下为xmm0)。

虽然众所周知编译器会发出不必要的指令,但在这种情况下,我无法真正看到这种情况。

如果您希望xmm0(或xmm1)保持整数,则不要为float的第一个值投射i。也许你想要做的是:

 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

也不是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

我编译的代码:

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]);
}

(我添加了printf以避免编译器删除所有代码)