从汇编代码的角度观察switch与if...else,以及乘除与移位的性能差别。

来源:互联网 发布:php获取js变量的方法 编辑:程序博客网 时间:2024/04/28 20:02

Switch与if…else

有以下两个函数,功能基本相同,sum5()是switch版本,sum6()是if else版本。

    public int sum5(int x,int y){        switch(x){        case 1:            y+=1;            break;        case 2:            y+=2;            break;        case 3:            y+=3;            break;        case 4:            y+=4;            break;        case 5:            y+=5;            break;        case 6:            y+=6;            break;        case 7:            y+=7;            break;        case 8:            y+=8;            break;        case 9:            y+=9;            break;        default:            y=0;            break;        }        return y;    }
public int sum6(int x,int y){    if(x==1){        y+=3;    }    else if(x==2){        y+=4;    }    else if(x==3){        y+=5;    }    else if(x==4){        y+=6;    }    else if(x==5){        y+=7;    }    else if(x==6){        y+=8;    }    else if(x==7){        y+=9;    }    else if(x==8){        y+=10;    }    else if(x==9){        y+=11;    }    else{        y=0;    }    return y;}

}

用monodevelop的aot编译器反编译后,它们的汇编代码是这样的:

_AlgorithmTest_sum5_int_int:00000b40    pushl   %ebp00000b41    movl    %esp, %ebp00000b43    pushl   %ebx00000b44    pushl   %edi00000b45    pushl   %esi00000b46    subl    $0xc, %esp00000b49    movl    0xc(%ebp), %esi00000b4c    movl    0x10(%ebp), %edi00000b4f    calll   0xb54   00000b54    popl    %ebx00000b55    addl    $0x4ac, %ebx            ## imm = 0x4AC00000b5b    decl    %esi            00000b5c    cmpl    $0x9, %esi      //00000b5f    jae 0xb9d               //00000b61    movl    %esi, %ecx00000b63    shll    $0x2, %ecx00000b66    movl    0x14(%ebx), %eax00000b6c    addl    %ecx, %eax00000b6e    movl    (%eax), %eax00000b70    jmpl    *%eax       ///00000b72    incl    %edi        00000b73    jmp 0xb9f00000b75    addl    $0x2, %edi00000b78    jmp 0xb9f00000b7a    addl    $0x3, %edi00000b7d    jmp 0xb9f00000b7f    addl    $0x4, %edi00000b82    jmp 0xb9f00000b84    addl    $0x5, %edi00000b87    jmp 0xb9f00000b89    addl    $0x6, %edi00000b8c    jmp 0xb9f00000b8e    addl    $0x7, %edi00000b91    jmp 0xb9f00000b93    addl    $0x8, %edi00000b96    jmp 0xb9f00000b98    addl    $0x9, %edi00000b9b    jmp 0xb9f00000b9d    xorl    %edi, %edi00000b9f    movl    %edi, %eax00000ba1    leal    -0xc(%ebp), %esp00000ba4    leal    -0xc(%ebp), %esp00000ba7    popl    %esi00000ba8    popl    %edi00000ba9    popl    %ebx00000baa    leave00000bab    retl00000bac    nopl    (%eax)
_AlgorithmTest_sum6_int_int:00000bb0    pushl   %ebp00000bb1    movl    %esp, %ebp00000bb3    pushl   %ebx00000bb4    pushl   %edi00000bb5    pushl   %esi00000bb6    subl    $0xc, %esp00000bb9    movl    0xc(%ebp), %esi00000bbc    movl    0x10(%ebp), %edi00000bbf    calll   0xbc400000bc4    popl    %ebx00000bc5    addl    $0x43c, %ebx            ## imm = 0x43C00000bcb    cmpl    $0x1, %esi //比较1与x00000bce    jne 0xbd8           //如果不等于1,跳转到行bd800000bd0    addl    $0x3, %edi //如果等于1,则加300000bd3    jmp 0xc2f           //赋值后跳转到函数c2f行,跳出if else区域。00000bd8    cmpl    $0x2, %esi //比较2与x00000bdb    jne 0xbe500000bdd    addl    $0x4, %edi00000be0    jmp 0xc2f00000be5    cmpl    $0x3, %esi00000be8    jne 0xbf200000bea    addl    $0x5, %edi00000bed    jmp 0xc2f00000bf2    cmpl    $0x4, %esi00000bf5    jne 0xbfc00000bf7    addl    $0x6, %edi00000bfa    jmp 0xc2f00000bfc    cmpl    $0x5, %esi00000bff    jne 0xc0600000c01    addl    $0x7, %edi00000c04    jmp 0xc2f00000c06    cmpl    $0x6, %esi00000c09    jne 0xc1000000c0b    addl    $0x8, %edi00000c0e    jmp 0xc2f00000c10    cmpl    $0x7, %esi00000c13    jne 0xc1a00000c15    addl    $0x9, %edi00000c18    jmp 0xc2f00000c1a    cmpl    $0x8, %esi00000c1d    jne 0xc2400000c1f    addl    $0xa, %edi00000c22    jmp 0xc2f00000c24    xorl    %eax, %eax00000c26    addl    $0xb, %edi00000c29    cmpl    $0x9, %esi00000c2c    cmovnel %eax, %edi00000c2f    movl    %edi, %eax00000c31    leal    -0xc(%ebp), %esp00000c34    leal    -0xc(%ebp), %esp00000c37    popl    %esi00000c38    popl    %edi00000c39    popl    %ebx00000c3a    leave00000c3b    retl00000c3c    nopl    (%eax)

sum6()的汇编代码的逻辑与源码基本相同,既是将x依次与1-9比较,直到找出相等的值(见注释)。
而sum5(),既是switch版本,与sum6()的版本相比主要是优化在两个地方:

–第一是汇编代码会先判断x是否属于default(既不在1-9的范围呢),见注释双斜线处

00000b5c cmpl $0x9, %esi //比较9与x
00000b5f jae 0xb9d //如果x大于等于9(x在上一行有减1),则跳转到b9d行,既是源码的default处。

–第二是根据x的值直接跳转到对应的代码块,见注释三斜线处:

00000b70 jmpl *%eax //之前编译器通过计算将 %eax转为了与x相应的代码块地址,在判断x是在1-9的范围内后,将会直接跳转到此地址。这个跳转的实现由于代码不同编译器有可能会采用不同的计算铺垫。

结论:在选项很多的情况下,switch只用一次跳转即可完成整个判断过程,相对于if…else按固定顺序进行依次判断要快。

乘除与移位

乘法与右移函数:

    public int Sum1(){        int i2=i*2;        return i2;    }    public int Sum2(){        int i22=i<<1;        return i22;    }
_AlgorithmTest_Sum1:00000a80    pushl   %ebp00000a81    movl    %esp, %ebp00000a83    pushl   %ebx00000a84    subl    $0x4, %esp00000a87    calll   0xa8c00000a8c    popl    %ebx00000a8d    addl    $0x574, %ebx            ## imm = 0x57400000a93    movl    0x8(%ebp), %eax00000a96    movl    0x8(%eax), %eax00000a99    shll    %eax                ///00000a9b    leal    -0x4(%ebp), %esp00000a9e    leal    -0x4(%ebp), %esp00000aa1    popl    %ebx00000aa2    leave00000aa3    retl00000aa4    nopw    %cs:(%eax,%eax)_AlgorithmTest_Sum2:00000ab0    pushl   %ebp00000ab1    movl    %esp, %ebp00000ab3    pushl   %ebx00000ab4    subl    $0x4, %esp00000ab7    calll   0xabc00000abc    popl    %ebx00000abd    addl    $0x544, %ebx            ## imm = 0x54400000ac3    movl    0x8(%ebp), %eax00000ac6    movl    0x8(%eax), %eax00000ac9    shll    %eax                ///00000acb    leal    -0x4(%ebp), %esp00000ace    leal    -0x4(%ebp), %esp00000ad1    popl    %ebx00000ad2    leave00000ad3    retl00000ad4    nopw    %cs:(%eax,%eax)

两段汇编代码的内容完全一样,目前的编译器已经可以智能的将乘法转换为左移(三斜线处)。

除法与右移:

    public int Sum3(){        int i3=i/2;        return i3;    }    public int Sum4(){        int i4=i>>1;        return i4;    }
_AlgorithmTest_Sum3:00000ae0    pushl   %ebp00000ae1    movl    %esp, %ebp00000ae3    pushl   %ebx00000ae4    subl    $0x4, %esp00000ae7    calll   0xaec00000aec    popl    %ebx00000aed    addl    $0x514, %ebx            ## imm = 0x51400000af3    movl    0x8(%ebp), %eax00000af6    movl    0x8(%eax), %ecx00000af9    movl    %ecx, %eax00000afb    shrl    $0x1f, %eax        ///00000afe    addl    %ecx, %eax00000b00    sarl    %eax               ///00000b02    leal    -0x4(%ebp), %esp00000b05    leal    -0x4(%ebp), %esp00000b08    popl    %ebx00000b09    leave00000b0a    retl00000b0b    nopl    (%eax,%eax)_AlgorithmTest_Sum4:00000b10    pushl   %ebp00000b11    movl    %esp, %ebp00000b13    pushl   %ebx00000b14    subl    $0x4, %esp00000b17    calll   0xb1c00000b1c    popl    %ebx00000b1d    addl    $0x4e4, %ebx            ## imm = 0x4E400000b23    movl    0x8(%ebp), %eax00000b26    movl    0x8(%eax), %eax00000b29    sarl    %eax                //00000b2b    leal    -0x4(%ebp), %esp00000b2e    leal    -0x4(%ebp), %esp00000b31    popl    %ebx00000b32    leave00000b33    retl00000b34    nopw    %cs:(%eax,%eax)

除法会先进行一次逻辑右移,再进行一次算术右移(三斜线处),并且多了两次寄存器操作。而第二个函数只进行一次算术右移(双斜线处)。

结论:由于当前编译器的智能化,会将乘除自动优化组合成移位运算,所以基本已经没有多少在源码中继续优化的空间了。

0 0
原创粉丝点击