Linux 中的 likely 和 unlikely
1. 源码
# define likely(x) __builtin_expect(!!(x), 1)
# define unlikely(x) __builtin_expect(!!(x), 0)
实际上就是通过GCC 的内建函数 __builtin_expect() 进行编译优化:
long __builtin_expect (long exp, long c)
该函数是告诉编译器:参数exp 为c 的可能更大,编译器可能就会根据这个提示信息,做一些分支预测上的代码优化。
参数 c,与函数的返回值无关,无论 c 为何值,函数的返回值都是 exp
例如:
if (__builtin_expect(x, 0))foo();
这里更希望不执行 foo函数,因为我们期盼x 表达式的值为 0。
1.2 __builtin_expect注意
- 参数c 与函数的返回值无关,无论 c 为何值,函数的返回值都为 exp;
- exp 是一个完整的表达式,返回值是该表达式的值;
- 编译时需要使用编译选项 -fprofile-arcs;
1.3 likely 和unlikely 中的!!(x)
从源码中我们看到 likyly 和unlikely 就是使用了GCC 的内建函数 __builtin_expect,但exp 是!!(x),通过两次取反实现将表达式 x 变成bool,然后与 1 和 0 作比较,告诉编译器 x 为真或为假的可能性很高。
例如:
!!(100)
第一次取反值为0,第二次取反后为 1。实际就是为了返回100 的bool 值为 true
同样:
!!(-100)
第一次取反值为0,第二次取反后为 1。实际就是为了返回-100 的bool 值为 true
2. 验证 likeyly()
#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 = 0x123;} else {a = 0x456;}printf("a= 0x%x\n", a);return 0;
}
该 demo 告诉编译器 a > 0 的可能性更大。
下面通过汇编的代码来看下编译器的分析预测:
$ gcc -fprofile-arcs -O2 -c test.c
$ objdump -S test.o0000000000000000 <main>:0: f3 0f 1e fa endbr644: 48 83 ec 08 sub $0x8,%rsp8: 48 8b 7e 08 mov 0x8(%rsi),%rdic: 31 f6 xor %esi,%esie: ba 0a 00 00 00 mov $0xa,%edx13: 48 83 05 00 00 00 00 addq $0x1,0x0(%rip) # 1b <main+0x1b>1a: 011b: e8 00 00 00 00 call 20 <main+0x20>20: 48 83 05 00 00 00 00 addq $0x1,0x0(%rip) # 28 <main+0x28>27: 0128: ba 23 01 00 00 mov $0x123,%edx //编译器直接下给a赋值0x1232d: 85 c0 test %eax,%eax //先赋值,再来判断 a>02f: 7e 22 jle 53 <main+0x53> //使用jle指令判断当a<=0,跳转31: 48 8d 35 00 00 00 00 lea 0x0(%rip),%rsi # 38 <main+0x38>38: bf 02 00 00 00 mov $0x2,%edi3d: 31 c0 xor %eax,%eax3f: e8 00 00 00 00 call 44 <main+0x44>44: 48 83 05 00 00 00 00 addq $0x1,0x0(%rip) # 4c <main+0x4c>4b: 014c: 31 c0 xor %eax,%eax4e: 48 83 c4 08 add $0x8,%rsp52: c3 ret53: 48 83 05 00 00 00 00 addq $0x1,0x0(%rip) # 5b <main+0x5b>5a: 015b: ba 56 04 00 00 mov $0x456,%edx //赋值0x456的分支在最后60: eb cf jmp 31 <main+0x31>62: 66 66 2e 0f 1f 84 00 data16 cs nopw 0x0(%rax,%rax,1)69: 00 00 00 006d: 0f 1f 00 nopl (%rax)
编译器通过likely() 得知 a>0 的概率很大,直接先给 a 赋值0x123 了,如果不满足条件会进行跳转,将 a = 0x456 的代码分支放到了最后。
3. 验证 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 (unlikely(a > 0)) {a = 0x123;} else {a = 0x456;}printf("a= 0x%x\n", a);return 0;
}
该 demo 告诉编译器 a > 0 的可能性很小,或不希望 a>0.
下面通过汇编的代码来看下编译器的分析预测:
$ gcc -fprofile-arcs -O2 -c test.c
$ objdump -S test.o0000000000000000 <main>:0: f3 0f 1e fa endbr644: 48 83 ec 08 sub $0x8,%rsp8: 48 8b 7e 08 mov 0x8(%rsi),%rdic: 31 f6 xor %esi,%esie: ba 0a 00 00 00 mov $0xa,%edx13: 48 83 05 00 00 00 00 addq $0x1,0x0(%rip) # 1b <main+0x1b>1a: 011b: e8 00 00 00 00 call 20 <main+0x20>20: 85 c0 test %eax,%eax //面对概率小,编译器选择先判断a>022: 7f 2f jg 53 <main+0x53> //使用jg指令,如果a>0,跳转24: 48 83 05 00 00 00 00 addq $0x1,0x0(%rip) # 2c <main+0x2c>2b: 012c: ba 56 04 00 00 mov $0x456,%edx31: 48 8d 35 00 00 00 00 lea 0x0(%rip),%rsi # 38 <main+0x38>38: bf 02 00 00 00 mov $0x2,%edi3d: 31 c0 xor %eax,%eax3f: e8 00 00 00 00 call 44 <main+0x44>44: 48 83 05 00 00 00 00 addq $0x1,0x0(%rip) # 4c <main+0x4c>4b: 014c: 31 c0 xor %eax,%eax4e: 48 83 c4 08 add $0x8,%rsp52: c3 ret53: 48 83 05 00 00 00 00 addq $0x1,0x0(%rip) # 5b <main+0x5b>5a: 015b: ba 23 01 00 00 mov $0x123,%edx60: eb cf jmp 31 <main+0x31>62: 66 66 2e 0f 1f 84 00 data16 cs nopw 0x0(%rax,%rax,1)69: 00 00 00 006d: 0f 1f 00 nopl (%rax)
4. 总结
- likely()、unlikely() 原理是利用了GCC 的内建函数 __builtin_expect(),通过编译器预测代码分支;
- GCC 编译器会将不希望的代码分支放最后;
- GCC 可能优先执行期盼的执行,再进行判断,不同的编译器版本实现方式不同;
- likely() 表示该表达式为 “True” 的概率大一些,unlikely() 表示改表达式为 “True” 的概率小一些;
- likely()、unlikely() 通过分支预测指令的预取能提高代码的执行效率。 但是前提在使用的过程当中程序的开发者必须对自己的代码逻辑有清晰的认识,知道什么样的逻辑会大概率执行,什么样的逻辑大概率不会执行,只有这样才能通likely,unlikely 的判断做精准的分支预测,提高程序的运行性能。
更多的GCC 内建函数可以查看:GCC 内建函数
相关文章:
Linux 中的 likely 和 unlikely
1. 源码 # define likely(x) __builtin_expect(!!(x), 1) # define unlikely(x) __builtin_expect(!!(x), 0) 实际上就是通过GCC 的内建函数 __builtin_expect() 进行编译优化: long __builtin_expect (long exp, long c) 该函数是告诉编译器:参…...
CSS -属性值的计算过程
目录 一、抛出两个问题1.如果我们学过优先级关系,那么请思考如下样式为何会生效2.如果我们学习过继承,那么可以知道color是可以被子元素继承使用的,那么请思考下述情景为何不生效 二、属性值计算过程1.确定声明值2.层叠冲突3.使用继承4.使用默…...
百度贴吧IP和ID是什么意思?怎么查看
在百度贴吧这一充满活力的网络社区中,IP和ID是两个频繁出现的概念。它们各自承载着不同的意义和作用,对于贴吧用户而言,了解这两个概念有助于更好地参与社区互动、保护个人隐私以及维护社区秩序。本文将详细解析百度贴吧中IP和ID的含义&#…...
SpiderX:专为前端JS加密绕过设计的自动化工具
SpiderX 一、工具概述 SpiderX是一款专为解决前端JS加密问题而设计的自动化绕过工具。在网络安全领域,随着前端加密技术的普及,传统的爬虫和自动化测试工具在面对复杂的JS加密时显得力不从心。SpiderX应运而生,旨在通过自动化手段高效绕过前…...
基于银河麒麟系统ARM架构安装达梦数据库并配置主从模式
达梦数据库简要概述 达梦数据库(DM Database)是一款由武汉达梦公司开发的关系型数据库管理系统,支持多种高可用性和数据同步方案。在主从模式(也称为 Master-Slave 或 Primary-Secondary 模式)中,主要通过…...
【AWS入门】AWS云计算简介
【AWS入门】AWS云计算简介 A Brief Introduction to AWS Cloud Computing By JacksonML 什么是云计算?云计算能干什么?我们如何利用云计算?云计算如何实现? 带着一系列问题,我将做一个普通布道者,引领广…...
适合企业内训的AI工具实操培训教程(37页PPT)(文末有下载方式)
详细资料请看本解读文章的最后内容。 资料解读:适合企业内训的 AI 工具实操培训教程 在当今数字化时代,人工智能(AI)技术迅速发展,深度融入到各个领域,AIGC(人工智能生成内容)更是成…...
【数据结构与算法】Java描述:第四节:二叉树
一、树的相关概念 编程中的树是模仿大自然中的树设计的,呈现倒立的结构,我们着重掌握 二叉树 。 1.1 基本概念: 结点的度:一个结点有几个子结点,度就是几; 如上图:A的度为3 树的度࿱…...
【一起来学kubernetes】14、StatefulSet使用详解
一、核心特性二、架构与组件三、生命周期管理四、典型应用场景**五、注意事项与最佳实践六、对比Deployment一、应用场景二、Pod管理三、部署与更新策略四、其他特性 七、常见问题八、拓展 前文中我们介绍了k8s中常用的一种控制器 Deployment,与之向对应的ÿ…...
Day5 结构体、文字显示与GDT/IDT初始化
文章目录 1. harib02b用例(使用结构体)2. harib02c用例3. harib02d用例(显示字符图案)3. harib02e用例(增加字符图案)4. harib02g用例4.1 显示字符串4.2 显示变量值 5. harib02h用例(显示鼠标&a…...
AI第一天 自我理解笔记--微调大模型
目录 1. 确定目标:明确任务和数据 2. 选择预训练模型 3. 数据预处理 (1) 数据清洗与格式化 (2) 划分数据集 (3) 数据加载与批处理 4. 构建微调模型架构 (1) 加载预训练模型 (2) 修改模型尾部(适配任务) (3) 冻结部分层(可…...
ClientAbortException问题分析
最近遇到一个问题,在设备采数据数据上报后频繁发生ClientAbortException异常,一种处理方案是ClientAbortException 问题分析-CSDN博客 一、ClientAbortException 的触发与影响 1. 定义与场景 ClientAbortException 是后端服务器(如 Tomc…...
系统思考全球化落地
感谢加密货币公司Bybit的再次邀请,为全球团队分享系统思考课程!虽然大家来自不同国家,线上学习的形式依然让大家充满热情与互动,思维的碰撞不断激发新的灵感。 尽管时间存在挑战,但我看到大家的讨论异常积极ÿ…...
【开原宝藏】30天学会CSS - DAY1 第一课
下面提供一个由浅入深、按步骤拆解的示例教程,让你能从零开始,逐步理解并实现带有旋转及悬停动画的社交图标效果。为了更简单明了,以下示例仅创建四个图标(Facebook、Twitter、Google、LinkedIn),并在每一步…...
钉钉项目报销与金蝶系统高效集成技术解析
钉钉报销【项目报销类】集成到金蝶付款单【画纤骨】的技术实现 在企业日常运营中,数据的高效流转和准确对接是提升业务效率的关键。本文将分享一个具体的系统对接集成案例:如何将钉钉平台上的项目报销数据无缝集成到金蝶云星空的付款单系统中。本次方案…...
Python——代码格式
代码格式 良好的代码格式可以提升代码的可读性。和其他语言不同,Python 代码的格式是 Python 语法的组成之一,不符合 Python 代码无法正常运行。 注释 注释是代码中穿插的辅佐性质的文字,用于标识代码的含义和功能,可以提高程序…...
Datawhale coze-ai-assistant:Task 1 了解 AI 工作流 + Coze的介绍
学习网址:Datawhale-学用 AI,从此开始 工作流(Workflow)是指完成一项任务或目标时,按照特定顺序进行的一系列活动或步骤。它强调在计算机应用环境下的自动化,通过将复杂的任务拆分成多个简单的步骤,每一步都…...
深度学习 Deep Learning 第3章 概率论与信息论
第三章 概率与信息论 概述 本章介绍了概率论和信息论的基本概念及其在人工智能和机器学习中的应用。概率论为处理不确定性提供了数学框架,使我们能够量化不确定性和推导新的不确定陈述。信息论则进一步帮助我们量化概率分布中的不确定性。在人工智能中,…...
GStreamer —— 2.15、Windows下Qt加载GStreamer库后运行 - “播放教程 1:Playbin 使用“(附:完整源码)
运行效果 介绍 我们已经使用了这个元素,它能够构建一个完整的播放管道,而无需做太多工作。 本教程介绍如何进一步自定义,以防其默认值不适合我们的特定需求。将学习: • 如何确定文件包含多少个流,以及如何切换 其中。…...
MYsql—1
1.mysql的安装 在windows下安装mysql,直接官网搜索即可:http://www.mysql.com/,自己找想要的版本进行download,官网长这样 安装路径需要是英文路径,设置默认即可,若安装执行内容时报错,则AltCt…...
位运算(基础算法)
按位与AND( & ) 只有当两个位都为1时,结果才为1,否则为0。结果不会变大 按位或 OR( | ) 只有当两个位中有一个为1时,结果才为1,否则为0。结果不会变小 按位异或 XOR ( ^ ) 只…...
硬件地址反序?用位操作为LED灯序“纠偏”。反转二进制数即可解决
特别有意思,LED的灯序与其硬件地址刚好相反,没办法直接通过加1实现二进制进位的亮灯操作,查了一些资料说用数组和switch实现,觉得太麻烦了,思索良久,就想到了反转二进制数解决这个问题。 reverse_bits( )是…...
如何让ai问答机器人通人性?
领域专用的问答机器人,数据是灵魂。通用模型的问题在于,它们虽然知识广博,但对特定领域的深度理解不足。解决这个问题的第一步,就是构建一个高质量的领域知识库。 数据要精准且全面 想让机器人真正“懂”一个领域,数…...
AI绘画笔记--基础知识
一.什么是AI绘画 AI绘画或者说AI生图,本质上来说还是图像生成技术,是一种基于深度学习的人工智能技术,通过提前大量学习学习图像特征,生成符合提示词的新图像。 整个流程可以简化理解为:人们首先让深度学习模型读取大量…...
图解AUTOSAR_CP_BSWMulticoreLibrary
AUTOSAR BSW 多核库详解 AUTOSAR基础软件多核操作库详细解析 目录 架构概述 1.1. 组件架构 1.2. API结构 1.3. 错误处理流程详细设计 2.1. 基础数据类型 2.2. 接口说明 2.3. 错误处理机制使用指南 3.1. 配置说明 3.2. 典型应用场景 3.3. 注意事项 1. 架构概述 1.1. 组件架构 …...
热key探测技术架构设计与实践
参考: 得物热点探测技术架构设计与实践 Redis数据倾斜与JD开源hotkey源码分析揭秘 京东热点检测 HotKey 学习笔记 hotkey: 京东App后台中间件,毫秒级探测热点数据,毫秒级推送至服务器集群内存,大幅降低热key对数据层查询压力 …...
【微服务】java中http调用组件深入实战详解
目录 一、前言 二、http调用概述 2.1 什么是http调用 2.1.1 http调用步骤 2.2 HTTP调用特点 2.3 HTTP调用应用场景 三、微服务场景下http调用概述 3.1 微服务开发中http调用场景 3.2 微服务组件中http的应用 四、常用的http调用组件 4.1 java中常用的http组件介绍 4…...
Python数据结构 ——字典
1.以下关于Python字典变量的定义中,正确的是()。 A. d={[1,2]:1, [3,4]:3} B. d={1:as, 2:sf} C. d = {(1,2):1, (3,4):3} D. d={‘python’:1, 2:[tea, cat]} 答案:C。在Python中,字典是存储可变数量键值对的数据结构,通过字典类型实现映射,键必须是唯一的,必须是不可变数据…...
32、构造函数
1、用构造函数反复创建多个相同结果的对象 问题 如果想反复创建多个相同结构,但是内容不同的对象时,用{}创建会代码重复,及其不便于维护! 解决 今后只要想反复创建同一类型的多个相同结构不同内容的对象时,都用构造函…...
编程环境搭建专栏目录汇总
1.WindowsvscodeclineMCP配置 2. Cline使用openrouter报错:Error Unexpected API Response: The language model did not provide any assista...
