从汇编代码的角度观察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)
除法会先进行一次逻辑右移,再进行一次算术右移(三斜线处),并且多了两次寄存器操作。而第二个函数只进行一次算术右移(双斜线处)。
结论:由于当前编译器的智能化,会将乘除自动优化组合成移位运算,所以基本已经没有多少在源码中继续优化的空间了。
- 从汇编代码的角度观察switch与if...else,以及乘除与移位的性能差别。
- if else 与 switch case的区别:
- switch 与 if else 性能区别
- if else 与switch的区别 -switch用法
- 选择语句switch和if else的区别以及代码
- switch 与 if/else
- switch 与 if else
- if.....else if .....else 与switch....case.....
- 不用乘除,for,while,if,else,switch,case ?:等求1+2+..+n的值
- 关于if else 与switch的效率问题
- if...else与switch...case的执行效率问题
- If..else If…else 语句 与 switch 语句的不同之处.
- if与switch的性能比较
- switch case 与 if else
- switch与if..else区别
- if else 与 switch case
- if else与switch区别
- 移位与乘除关系
- curl判断远程文件是否存在
- 使用StackedBarChart进行柱状图开发实例
- Lowest Common Ancestor of a Binary Search Tree 二分搜索树的公共节点
- 第三框架
- UIViewController生命周期
- 从汇编代码的角度观察switch与if...else,以及乘除与移位的性能差别。
- mysql 引擎 学习1
- Poj 3592 Instantaneous Transference【强连通Tarjan+染色缩点+SPFA】
- eclipse中的项目导入到源生项目中的各种问题
- 多态
- qt5做的简单计算器(校企作业)
- poj 1860 spfa算法判断图中是否有正环
- YouCompleteMe插件安装全攻略和问题总结
- hdu 2089 不要62 基础数位DP