使用__builtin_expec优化条件检测

__builtin_expect 主要用于减少条件语句中的汇编级别的跳转,增加代码的执行效率,典型的空间换时间。下面使用一个简单的测试代码作为演示:

#if defined __GNUC__ || defined __llvm__
#define sf_likely(x) __builtin_expect ((x), 1)
#define sf_unlikely(x) __builtin_expect ((x), 0)
#else
#define sf_likely(x) (x)
#define sf_unlikely(x) (x)
#endif


int test_likely(int x){
    int return_code;
	if(sf_likely(x)){
		return_code = 11;
	}else{
		return_code = 22;
	}
	return return_code;
}

int test_unlikely(int x){
    int return_code;
	if(sf_unlikely(x)){
		return_code = 11;
	}else{
		return_code = 22;
	}
	return return_code;
}

int test(int x){
    int return_code;
	if(x){
		return_code=11;
	}else{
		return_code=22;
	}
	return return_code;
}

由于平时开发有使用o2优化级别,因此我们也使用这个级别应的汇编代码来了解它的作用。

编译命令

gcc -fprofile-arcs -O2 -c test.c

查看反汇编代码

objdump -d test.o

Disassembly of section .text:

0000000000000000 <test_likely>:
   0:	48 83 05 00 00 00 00 	addq   $0x1,0x0(%rip)        # 8 <test_likely+0x8>
   7:	01 
   8:	85 ff                	test   %edi,%edi
   a:	74 06                	je     12 <test_likely+0x12>
   c:	b8 0b 00 00 00       	mov    $0xb,%eax
  11:	c3                   	retq   
  12:	48 83 05 00 00 00 00 	addq   $0x1,0x0(%rip)        # 1a <test_likely+0x1a>
  19:	01 
  1a:	b8 16 00 00 00       	mov    $0x16,%eax
  1f:	c3                   	retq   

0000000000000020 <test_unlikely>:
  20:	48 83 05 00 00 00 00 	addq   $0x1,0x0(%rip)        # 28 <test_unlikely+0x8>
  27:	01 
  28:	85 ff                	test   %edi,%edi
  2a:	75 0e                	jne    3a <test_unlikely+0x1a>
  2c:	48 83 05 00 00 00 00 	addq   $0x1,0x0(%rip)        # 34 <test_unlikely+0x14>
  33:	01 
  34:	b8 16 00 00 00       	mov    $0x16,%eax
  39:	c3                   	retq   
  3a:	b8 0b 00 00 00       	mov    $0xb,%eax
  3f:	c3                   	retq   

0000000000000040 <test>:
  40:	48 83 05 00 00 00 00 	addq   $0x1,0x0(%rip)        # 48 <test+0x8>
  47:	01 
  48:	85 ff                	test   %edi,%edi
  4a:	b8 0b 00 00 00       	mov    $0xb,%eax
  4f:	75 0a                	jne    5b <test+0x1b>
  51:	48 83 05 00 00 00 00 	addq   $0x1,0x0(%rip)        # 59 <test+0x19>
  58:	01 
  59:	b0 16                	mov    $0x16,%al
  5b:	f3 c3                	repz retq 

 

对比三者之间的差异就可以发现执行效率的不同,显然如果if分支的概率比较高的话,使用likely会带来性能的提升,反之如果else分支的概率比较高的话,使用unlikely就会减少很多跳转指令,提升了性能,这里面使用了一种思想,就是“概率”,把主要的处理放在大概率事件上,减少了跳转 ,优化了代码,减少了执行指令的时间。乍看之下,似乎作用不明显,可是在高并发环境下却会有意想不到的效果,数量产生质量!

点赞