为什么没有编译器能够优化此代码?

时间:2016-04-02 16:52:17

标签: c optimization

考虑以下C代码(假设80位long double)(注意,我知道memcmp,这只是一个实验):

enum { sizeOfFloat80=10 }; // NOTE: sizeof(long double) != sizeOfFloat80
_Bool sameBits1(long double x, long double y)
{
    for(int i=0;i<sizeOfFloat80;++i)
        if(((char*)&x)[i]!=((char*)&y)[i])
            return 0;
    return 1;
}

我检查过的所有编译器(gcc,clang,gc.godbolt.org上的icc)生成类似的代码,这里是带有选项-O3 -std=c11 -fomit-frame-pointer -m32的gcc的示例:

sameBits1:
        movzx   eax, BYTE PTR [esp+16]
        cmp     BYTE PTR [esp+4], al
        jne     .L11
        movzx   eax, BYTE PTR [esp+17]
        cmp     BYTE PTR [esp+5], al
        jne     .L11
        movzx   eax, BYTE PTR [esp+18]
        cmp     BYTE PTR [esp+6], al
        jne     .L11
        movzx   eax, BYTE PTR [esp+19]
        cmp     BYTE PTR [esp+7], al
        jne     .L11
        movzx   eax, BYTE PTR [esp+20]
        cmp     BYTE PTR [esp+8], al
        jne     .L11
        movzx   eax, BYTE PTR [esp+21]
        cmp     BYTE PTR [esp+9], al
        jne     .L11
        movzx   eax, BYTE PTR [esp+22]
        cmp     BYTE PTR [esp+10], al
        jne     .L11
        movzx   eax, BYTE PTR [esp+23]
        cmp     BYTE PTR [esp+11], al
        jne     .L11
        movzx   eax, BYTE PTR [esp+24]
        cmp     BYTE PTR [esp+12], al
        jne     .L11
        movzx   eax, BYTE PTR [esp+25]
        cmp     BYTE PTR [esp+13], al
        sete    al
        ret
.L11:
        xor     eax, eax
        ret

这看起来很丑陋,每个字节都有分支,实际上似乎根本没有优化(但至少循环是展开的)。很容易看出,这可以针对与以下内容等效的代码进行优化(通常对于较大的数据使用更大的步幅):

#include <string.h>
_Bool sameBits2(long double x, long double y)
{
    long long X=0; memcpy(&X,&x,sizeof x);
    long long Y=0; memcpy(&Y,&y,sizeof y);
    short Xhi=0; memcpy(&Xhi,sizeof x+(char*)&x,sizeof Xhi);
    short Yhi=0; memcpy(&Yhi,sizeof y+(char*)&y,sizeof Yhi);
    return X==Y && Xhi==Yhi;
}

此代码现在可以获得更好的编译结果:

sameBits2:
        sub     esp, 20
        mov     edx, DWORD PTR [esp+36]
        mov     eax, DWORD PTR [esp+40]
        xor     edx, DWORD PTR [esp+24]
        xor     eax, DWORD PTR [esp+28]
        or      edx, eax
        movzx   eax, WORD PTR [esp+48]
        sete    dl
        cmp     WORD PTR [esp+36], ax
        sete    al
        add     esp, 20
        and     eax, edx
        ret

所以我的问题是:为什么三个编译器中没有一个能够进行这种优化?它在C代码中看到的非常罕见吗?

1 个答案:

答案 0 :(得分:9)

首先,它无法进行此优化,因为您通过使用过多的内存重新解释来重载代码,从而完全模糊了代码的含义。像这样的代码只是让编译器做出反应,我不知道这是什么,但如果那是你想要的,那就是你得到的东西和&#39; s #34 ;.为什么你期望编译器甚至懒得将内存重新解释转换为另一种内存重新解释(!)对我来说完全不清楚。

其次,它可能在理论上可以做到,但它的优先级列表可能不是很高。请记住,代码优化通常通过模式匹配算法完成,而不是通过某种A.I.这不是它认可的模式之一。

大多数情况下,您的手动尝试执行代码的低级优化会使编译器无法完成同样的工作。如果你想自己优化它,那就一直走。不要期望能够启动,然后将其交给编译器为您完成工作。

可以非常轻松地比较两个long doublexyx == y。如果你想进行比特到内存的比较,你可能只需要在编译器中使用memcmp来简化编译器的工作,该编译器本身就知道memcmp是什么(内置,内在功能)。