Linux likely、unlikely

文章介绍了C语言中的likely和unlikely关键字,它们用于提供编译器分支预测信息,通过提前加载可能运行的指令来提高代码执行效率。作者通过实例和反汇编分析了这两个宏在分支决策中的作用和使用场景。
摘要由CSDN通过智能技术生成

在以往我们做分支判断时一般使用if else进行分支的判断常见如下demo

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char ** argv)
{
	int a = atoi(argv[1]);

	if (a > 0){
		a = 0x5a;
	}else {
		a = 0xa5;
	}
	
	printf("a = 0x%x\n", a);

	return 0;
}

但是在Linux内核当中会看到大量的likely、unlikely这一类的判断,这里将介绍这类判断的设计的原因及如何使用。

# define likely(x)	__builtin_expect(!!(x), 1)
# define unlikely(x)	__builtin_expect(!!(x), 0)

一、likely、unlikely设计的原因

在 c语言当中!(x)取反,!!(x)在取反的基础上再次取反,不同的是假设x = 100, !(x) =0, !!(x) = 1,进一步分析假设X= -1; !(x) = 1, !!(x) = 0;这样无论输入的参数是多少结果就变成了bool值true, false。

long __builtin_expect (long exp, long c)

You may use __builtin_expect to provide the compiler with branch prediction information. In general, you should prefer to use actual profile feedback for this (-fprofile-arcs), as programmers are notoriously bad at predicting how their programs actually perform. However, there are applications in which this data is hard to collect.

The return value is the value of exp, which should be an integral expression. The semantics of the built-in are that it is expected that exp == c. For example:

if (__builtin_expect (x, 0))
  foo ();

indicates that we do not expect to call foo, since we expect x to be zero. Since you are limited to integral expressions for exp, you should use constructions such as

if (__builtin_expect (ptr != NULL, 1))
  foo (*ptr);

详细描述参见:https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html

期望exp == c, 举例:

​ 1、假设x == 0, 说明if (__builtin_expect (x, 0)) 判断期望不运行foo();

​ 2、假设ptr != NULL,则说明ptr 倾向于非空 这样判断期望运行foo(*ptr);

所以针对likely, unlikely所代表的含义就如下:

# define likely(x)	__builtin_expect(!!(x), 1) //x 的值为真的可能性更大,对应的运行可能的代码分支
# define unlikely(x)	__builtin_expect(!!(x), 0) //x 的值为假的可能性更大,对应不太可能的代码分支

**You may use __builtin_expect to provide the compiler with branch prediction information **

从这里可以知道likely,unlikely起到了分支预测的功能,通过分支预测提前加载可能运行的指令提高代码的运行效率。

二、反汇编验证

#include <stdio.h>
#include <stdlib.h>

#define likely(x)	__builtin_expect(!!(x), 1)
#define unlikely(x)	__builtin_expect(!!(x), 0)

int main(int argc, char ** argv)
{
	int a = atoi(argv[1]);

	if (likely(a > 0)){
		a = 0x5a;
	}else {
		a = 0xa5;
	}
	
	printf("a = 0x%x\n", a);

	return 0;
}

大概率a > 0为真 ,if likely大概率为真,则分支预取运行X = 0x5a;

gcc -fprofile-arcs -O2 -c likely.c 
objdump -S likely.o

反汇编结果如下:

0000000000000000 <main>:
   0:	48 83 ec 08          	sub    $0x8,%rsp
   4:	48 8b 7e 08          	mov    0x8(%rsi),%rdi
   8:	31 f6                	xor    %esi,%esi
   a:	ba 0a 00 00 00       	mov    $0xa,%edx
   f:	48 83 05 00 00 00 00 	addq   $0x1,0x0(%rip)        # 17 <main+0x17>
  16:	01 
  17:	e8 00 00 00 00       	callq  1c <main+0x1c>
  1c:	48 83 05 00 00 00 00 	addq   $0x1,0x0(%rip)        # 24 <main+0x24>
  23:	01 
  24:	be 5a 00 00 00       	mov    $0x5a,%esi  //率先执行a=0x5a
  29:	85 c0                	test   %eax,%eax
  2b:	7e 1b                	jle    48 <main+0x48>
  2d:	bf 00 00 00 00       	mov    $0x0,%edi
  32:	31 c0                	xor    %eax,%eax
  34:	e8 00 00 00 00       	callq  39 <main+0x39>
  39:	48 83 05 00 00 00 00 	addq   $0x1,0x0(%rip)        # 41 <main+0x41>
  40:	01 
  41:	31 c0                	xor    %eax,%eax
  43:	48 83 c4 08          	add    $0x8,%rsp
  47:	c3                   	retq   
  48:	48 83 05 00 00 00 00 	addq   $0x1,0x0(%rip)        # 50 <main+0x50>
  4f:	01 
  50:	be a5 00 00 00       	mov    $0xa5,%esi   //后执行a = 0xa5
  55:	eb d6                	jmp    2d <main+0x2d>
  57:	66 0f 1f 84 00 00 00 	nopw   0x0(%rax,%rax,1)
  5e:	00 00 

#include <stdio.h>
#include <stdlib.h>

#define likely(x)	__builtin_expect(!!(x), 1)
#define unlikely(x)	__builtin_expect(!!(x), 0)

int main(int argc, char ** argv)
{
	int a = atoi(argv[1]);

	if (unlikely(a > 0)){
		a = 0x5a;
	}else {
		a = 0xa5;
	}
	
	printf("a = 0x%x\n", a);

	return 0;
}

大概率a < 0为假, if unlikely大概率为假,则分支预取运行X = 0xa5;

gcc -fprofile-arcs -O2 -c unlikely.c 
objdump -S unlikely.o

反汇编结果如下:

0000000000000000 <main>:
   0:	48 83 ec 08          	sub    $0x8,%rsp
   4:	48 8b 7e 08          	mov    0x8(%rsi),%rdi
   8:	31 f6                	xor    %esi,%esi
   a:	ba 0a 00 00 00       	mov    $0xa,%edx
   f:	48 83 05 00 00 00 00 	addq   $0x1,0x0(%rip)        # 17 <main+0x17>
  16:	01 
  17:	e8 00 00 00 00       	callq  1c <main+0x1c>
  1c:	85 c0                	test   %eax,%eax
  1e:	7f 28                	jg     48 <main+0x48>
  20:	48 83 05 00 00 00 00 	addq   $0x1,0x0(%rip)        # 28 <main+0x28>
  27:	01 
  28:	be a5 00 00 00       	mov    $0xa5,%esi    //率先执行a = 0xa5
  2d:	bf 00 00 00 00       	mov    $0x0,%edi
  32:	31 c0                	xor    %eax,%eax
  34:	e8 00 00 00 00       	callq  39 <main+0x39>
  39:	48 83 05 00 00 00 00 	addq   $0x1,0x0(%rip)        # 41 <main+0x41>
  40:	01 
  41:	31 c0                	xor    %eax,%eax
  43:	48 83 c4 08          	add    $0x8,%rsp
  47:	c3                   	retq   
  48:	48 83 05 00 00 00 00 	addq   $0x1,0x0(%rip)        # 50 <main+0x50>
  4f:	01 
  50:	be 5a 00 00 00       	mov    $0x5a,%esi      //后执行a = 0x5a
  55:	eb d6                	jmp    2d <main+0x2d>
  57:	66 0f 1f 84 00 00 00 	nopw   0x0(%rax,%rax,1)
  5e:	00 00 

三、总结

​ 1、likely,unlikely同if else一样进行分支判断;likely 期望执行if (true)为真的分支;unlikely 期望执行 if (true) 当中的else分支, 直接运 行else分支代码。

​ 2、likely, unlikely 通过分支预测指令的预取能提高代码的执行效率。 但是前提在使用的过程当中程序的开发者必须对自己的代码逻 辑有清晰的认识,知道什么样的逻辑会大概率执行,什么样的逻辑大概率不会执行,只有这样才能通likely,unlikely 的判断做精准的分 支预测,提高程序的运行性能。

  • 23
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值