从汇编层面分析if语句和switch的性能差异

1、if 语句源代码

#include<iostream>
int main() {
	int no = 4;
	if (no == 1) {
		printf("no is 1");
	} else if (no == 2){
		printf("no is 2");
	} else if (no == 3){
		printf("no is 3");
	} else if (no == 4) {
		printf("no is 4");
	} else if (no == 5) {
		printf("no is 5");
	} else {
		printf("no is other");
	}

	int age = 4;
}

2、对以上代码进行反汇编,得到汇编代码

// 把4赋值 int no = 4;
00007FF75D0B191A  mov         dword ptr[no], 4

//cmp == compare 比较 (把4和1比较 if (no == 1))
00007FF75D0B1921  cmp         dword ptr[no], 1

// jne == jump not equal 不相等的时候就会跳转,跳转到 07FF75D0B1935h 这个地址 ,就是下面这一句代码: 00007FF75D0B1935  cmp         dword ptr[no], 2
00007FF75D0B1925  jne         main + 45h(07FF75D0B1935h)

// 如果相等,继续往下执行,输入 no is 1
00007FF75D0B1927  lea         rcx, [string "no is 1" (07FF75D0B9C28h)]

//call 调用代码,也就是调用printf函数
00007FF75D0B192E  call        printf(07FF75D0B11D6h)

//jmp 无条件跳转  ,这里是跳转到 07FF75D0B1991h 这个地址 ,也就是执行: mov         dword ptr[age], 4 (源代码是:int age = 4)这时候已经跳出if - else 语句了
00007FF75D0B1933  jmp         main + 0A1h(07FF75D0B1991h)

/************************ 第二个if语句判断,跟上面相同的逻辑  ******************************/
//cmp == compare 比较 (把4和1比较 if (no == 1))
00007FF75D0B1935  cmp         dword ptr[no], 2
00007FF75D0B1939  jne         main + 59h(07FF75D0B1949h)
00007FF75D0B193B  lea         rcx, [string "no is 2" (07FF75D0B9C38h)]
00007FF75D0B1942  call        printf(07FF75D0B11D6h)
00007FF75D0B1947  jmp         main + 0A1h(07FF75D0B1991h)

/************************ 第三个if语句判断,跟上面相同的逻辑  ******************************/
00007FF75D0B1949  cmp         dword ptr[no], 3
00007FF75D0B194D  jne         main + 6Dh(07FF75D0B195Dh)
00007FF75D0B194F  lea         rcx, [string "no is 3" (07FF75D0B9C48h)]
00007FF75D0B1956  call        printf(07FF75D0B11D6h)
00007FF75D0B195B  jmp         main + 0A1h(07FF75D0B1991h)

/************************ 第四个if语句判断,跟上面相同的逻辑  ******************************/
00007FF75D0B195D  cmp         dword ptr[no], 4
00007FF75D0B1961  jne         main + 81h(07FF75D0B1971h)
00007FF75D0B1963  lea         rcx, [string "no is 4" (07FF75D0B9C58h)]
00007FF75D0B196A  call        printf(07FF75D0B11D6h)
00007FF75D0B196F  jmp         main + 0A1h(07FF75D0B1991h)


/************************ 第五个if语句判断,跟上面相同的逻辑  ******************************/
00007FF75D0B1971  cmp         dword ptr[no], 5
00007FF75D0B1975  jne         main + 95h(07FF75D0B1985h)
00007FF75D0B1977  lea         rcx, [string "no is 5" (07FF75D0B9C68h)]
00007FF75D0B197E  call        printf(07FF75D0B11D6h)
00007FF75D0B1983  jmp         main + 0A1h(07FF75D0B1991h)

/************************ 最后一个if语句判断,跟上面相同的逻辑  ******************************/
00007FF75D0B1985  lea         rcx, [string "no is other" (07FF75D0B9C78h)]
00007FF75D0B198C  call        printf(07FF75D0B11D6h)

/************************************* int age = 4, if语句结束的提示代码  **************************************/
00007FF75D0B1991  mov         dword ptr[age], 4

3、if语句的使用总结:

从上面的代码可以看出:如果用if语句,会把每一个条件都走一遍,知道匹配到对应的条件位置为止,所以写if-else语句的时候,尽量把出现的概率比较大的匹配值写在前面,例如 i== 4 或者 i == 3 会经常出现,那么代码应该这么写,效率会更高

	if (no == 3) {
		printf("no is 3");
	} else if (no == 4) {
		printf("no is 4");
	} else if (no == 1) {
		printf("no is 3");
	} else if (no == 2) {
		printf("no is 4");
	} else if (no == 5) {
		printf("no is 5");
	} else {
		printf("no is other");
	}

4、switch语句的底层实现

源码:

#include<iostream>
int main() {
	int no = 4;
	switch (no)
	{
	case 0:
		printf("i is 0");
	case 1:
		printf("i is 1");
	case 2:
		printf("i is 2");
	case 3:
		printf("i is 3");
	case 4:
		printf("i is 4");
	case 5:
		printf("i is 5");
	default:
		printf("i is other");
		break;
	}

	int age = 4;
}

5、switch 反汇编分析:

switch的原理:

通过hash 算法,得到一个hash之后的地址值,跟if不一样的是不需要一个一个条件去比较,而是直接算出对应的值的内存地址,然后跳转到对应的内存地址,直接执行对应的代码,这是一种典型的空间换时间的方式,
 

// 把4赋值 int no = 4;
00007FF6F462191A  mov         dword ptr[no], 4
00007FF6F4621921  mov         eax, dword ptr[no]

// hash 算法,得到一个hash之后的地址值,跟if不一样的是不需要一个一个比较,而是直接算出对应的值的内存地址,然后跳转到对应的内存地址,直接执行对应的代码
// 最终算出来的内存地址放在eax里面: 00007FF6F4621941  mov         eax, dword ptr[rcx + rax * 4 + 119B4h],
// 然后jmp eax,无条件跳转到eax这个地址,执行对应的代码,也就是case语句
00007FF6F4621924  mov         dword ptr[rbp + 0F4h], eax
00007FF6F462192A  cmp         dword ptr[rbp + 0F4h], 5
00007FF6F4621931  ja          $LN9 + 0Ch(07FF6F4621995h)
00007FF6F4621933  movsxd      rax, dword ptr[rbp + 0F4h]
00007FF6F462193A  lea         rcx, [7FF6F4610000h]  // 目标地址传送指令
00007FF6F4621941  mov         eax, dword ptr[rcx + rax * 4 + 119B4h]
00007FF6F4621948  add         rax, rcx
00007FF6F462194B  jmp         rax

// case 0 里面的代码
$LN4 :
00007FF6F462194D  lea         rcx, [string "i is 0" (07FF6F4629C10h)]
00007FF6F4621954  call        printf(07FF6F46211D6h)

// case 1 里面的代码
$LN5 :
00007FF6F4621959  lea         rcx, [string "i is 1" (07FF6F4629C18h)]
00007FF6F4621960  call        printf(07FF6F46211D6h)

// case 2 里面的代码
$LN6 :
00007FF6F4621965  lea         rcx, [string "i is 2" (07FF6F4629C20h)]
00007FF6F462196C  call        printf(07FF6F46211D6h)

// case 3 里面的代码
$LN7 :
00007FF6F4621971  lea         rcx, [string "i is 3" (07FF6F4629C30h)]
00007FF6F4621978  call        printf(07FF6F46211D6h)

// case 4 里面的代码
$LN8 :
00007FF6F462197D  lea         rcx, [string "i is 4" (07FF6F4629C40h)]
00007FF6F4621984  call        printf(07FF6F46211D6h)

// case 5 里面的代码
$LN9 :
00007FF6F4621989  lea         rcx, [string "i is 5" (07FF6F4629C50h)]
00007FF6F4621990  call        printf(07FF6F46211D6h)

// default 里面的代码
00007FF6F4621995  lea         rcx, [string "i is other" (07FF6F4629D48h)]
00007FF6F462199C  call        printf(07FF6F46211D6h)

// int age = 4 
00007FF6F46219A1  mov         dword ptr[age], 4

6、最后的总结:

(1)用能用switch的,尽量不要用if ,如果用了if,就要结合else if ,并且尽量把条件出现的概率比较高的,放在最前面

(2)不要小看这点点的性能优化,代码的性能优化其实更多的是一个习惯问题,一旦养成了这个习惯,代码多了,积累起来,这一点点的性能影响就很大了

(3)switch不用全部判断是因为把switch里面的判断一个做了hash计算,可以直接得到地址值,然后跳到执行对应的代码,但其实也不是所有的switch都会通过特定算法生成值,如果条件值比较少,就不会生成hash计算。而是直接比较

(4)苹果官方为什么说swift性能比oc要好,swift对于switch使用就是其中一个很好地体现,swift的switch支持范围值,填补了oc里面的这个功能的空虚,例如以下swift代码:

let no = 9
switch  no{
 case 0..9:
   print("0-9")
 case 10..20:
   print("10-20")
 default:
    print("other")
}

猜你喜欢

转载自blog.csdn.net/s12117719679/article/details/84950578