likely和unlikely是如何对代码的优化?
在执行if判断时,可以使用GCC提供了__builtin_expect对代码进行优化,可以提高代码的运行速度,参考GCC手册的"3.10 Options That Control Optimization".原理是:CPU在执行指令时采用的是流水线的方式,一条指令的执行大致会经过"取码 --> 译码 -->执行",如果在执行时发现需要进行跳转的话,会flush流水线,然后从新的地址重新开始"取码 --> 译码 --> 执行",这个过程会降低代码的执行效率,所以尽量减少跳转的可能(也就是flush流水线的发生频率),就可以提高代码的执行效率 。下面用一个简单的程序为例分析一下。#include <stdio.h> #define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0) void func1(int a)
{
int b; if (unlikely(a >= )) {
b = a + ;
printf("b = %d\n", b);
} else {
b = a + ;
printf("b = %d\n", b);
}
} void func2(int a)
{
int b; if (likely(a >= )) {
b = a + ;
printf("b = %d\n", b);
} else {
b = a + ;
printf("b = %d\n", b);
} } int main(int argc, const char *argv[])
{
int a = ; scanf("a = %d", &a); func1(a);
func2(a); return ;
}likely(x)用于x为真的可能性更大的场景,unlikey(x)用于x为假的可能性更大的场景,这两个宏的最终目的就是尽量减少跳转,因为只要跳转,pipeline就会flush,就会降低效率。
想让上面的优化生效的话,需要指定一定的优化等级,因为默认是-O0,没有任何优化。下面是-O0的反汇编:
00000000004005bc <func1>:
4005bc: a9bd7bfd stp x29, x30, [sp, #-]!
4005c0: 910003fd mov x29, sp
4005c4: b9001fa0 str w0, [x29, #]
4005c8: b9401fa0 ldr w0, [x29, #]
4005cc: 2a2003e0 mvn w0, w0
4005d0: 531f7c00 lsr w0, w0, #
4005d4: 12001c00 and w0, w0, #0xff
4005d8: 92401c00 and x0, x0, #0xff
4005dc: f100001f cmp x0, #0x0
4005e0: b.eq <func1+0x48> // b.none
4005e4: b9401fa0 ldr w0, [x29, #]
4005e8: add w0, w0, #0x1
4005ec: b9002fa0 str w0, [x29, #]
4005f0: adrp x0, <_init-0x430>
4005f4: 911e4000 add x0, x0, #0x790
4005f8: b9402fa1 ldr w1, [x29, #]
4005fc: 97ffffad bl 4004b0 <printf@plt>
: b <func1+0x64>
: b9401fa0 ldr w0, [x29, #]
: add w0, w0, #0x2
40060c: b9002fa0 str w0, [x29, #]
: adrp x0, <_init-0x430>
: 911e4000 add x0, x0, #0x790
: b9402fa1 ldr w1, [x29, #]
40061c: 97ffffa5 bl 4004b0 <printf@plt>
: d503201f nop
: a8c37bfd ldp x29, x30, [sp], #
: d65f03c0 ret 000000000040062c <func2>:
40062c: a9bd7bfd stp x29, x30, [sp, #-]!
: 910003fd mov x29, sp
: b9001fa0 str w0, [x29, #]
: b9401fa0 ldr w0, [x29, #]
40063c: 2a2003e0 mvn w0, w0
: 531f7c00 lsr w0, w0, #
: 12001c00 and w0, w0, #0xff
: 92401c00 and x0, x0, #0xff
40064c: f100001f cmp x0, #0x0
: b.eq <func2+0x48> // b.none
: b9401fa0 ldr w0, [x29, #]
: add w0, w0, #0x1
40065c: b9002fa0 str w0, [x29, #]
: adrp x0, <_init-0x430>
: 911e4000 add x0, x0, #0x790
: b9402fa1 ldr w1, [x29, #]
40066c: 97ffff91 bl 4004b0 <printf@plt>
: b <func2+0x64>
: b9401fa0 ldr w0, [x29, #]
: add w0, w0, #0x2
40067c: b9002fa0 str w0, [x29, #]
: adrp x0, <_init-0x430>
: 911e4000 add x0, x0, #0x790
: b9402fa1 ldr w1, [x29, #]
40068c: 97ffff89 bl 4004b0 <printf@plt>
: d503201f nop
: a8c37bfd ldp x29, x30, [sp], #
: d65f03c0 ret
可以看到,反汇编完全是按照C语言逻辑走的,一五一十,按部就班,上面的优化宏没有起到任何作用。
下面先用-O1看看效果。GCC对-O和-O1的描述是:the compiler tries to reduce code size and execution time, without performing any optimizations that take a great deal of compilation time.
aarch64-linux-gnu-gcc predict.c -o predict -O1
aarch64-linux-gnu-objdump -D predict > predict.S
下面是func1的反汇编结果:
00000000004005bc <func1>:
4005bc: a9bf7bfd stp x29, x30, [sp, #-]!
4005c0: 910003fd mov x29, sp
4005c4: 36f800e0 tbz w0, #, 4005e0 <func1+0x24>
4005c8: add w1, w0, #0x2
4005cc: adrp x0, <_init-0x430>
4005d0: 911c6000 add x0, x0, #0x718
4005d4: 97ffffb7 bl 4004b0 <printf@plt>
4005d8: a8c17bfd ldp x29, x30, [sp], #
4005dc: d65f03c0 ret
4005e0: add w1, w0, #0x1
4005e4: adrp x0, <_init-0x430>
4005e8: 911c6000 add x0, x0, #0x718
4005ec: 97ffffb1 bl 4004b0 <printf@plt>
4005f0: 17fffffa b 4005d8 <func1+0x1c>
func1的代码里,unlikely(a >= 0)的可能性小,所以为了减少跳转,就应该将else分支里的代码往前放,这样指令就可以一条紧挨着一条执行,不用跳转,即PC每次加4,pipeline不用flush,提高了代码执行速度。与之相反的是func2中,likely(a >= 0)的可能性更大,为了减少分支跳转,所以需要将if分支对应的代码放在前面。下面是func2的反汇编:
00000000004005f4 <func2>:
4005f4: a9bf7bfd stp x29, x30, [sp, #-]!
4005f8: 910003fd mov x29, sp
4005fc: 37f800e0 tbnz w0, #, <func2+0x24>
: add w1, w0, #0x1
: adrp x0, <_init-0x430>
: 911c6000 add x0, x0, #0x718
40060c: 97ffffa9 bl 4004b0 <printf@plt>
: a8c17bfd ldp x29, x30, [sp], #
: d65f03c0 ret
: add w1, w0, #0x2
40061c: adrp x0, <_init-0x430>
: 911c6000 add x0, x0, #0x718
: 97ffffa3 bl 4004b0 <printf@plt>
: 17fffffa b <func2+0x1c>
当然,如果likely和unlikely用的不符合实际情况,代码的执行效率更恶化。
00000000004005f8 <func1>:
4005f8: adrp x2, <_init-0x430>
4005fc: 36f80080 tbz w0, #, 40060c <func1+0x14>
: add w1, w0, #0x2
: 911ba040 add x0, x2, #0x6e8
: 17ffffaa b 4004b0 <printf@plt>
40060c: add w1, w0, #0x1
: 911ba040 add x0, x2, #0x6e8
: 17ffffa7 b 4004b0 <printf@plt> <func2>:
: adrp x2, <_init-0x430>
40061c: 37f80080 tbnz w0, #, 40062c <func2+0x14>
: add w1, w0, #0x1
: 911ba040 add x0, x2, #0x6e8
: 17ffffa2 b 4004b0 <printf@plt>
40062c: add w1, w0, #0x2
: 911ba040 add x0, x2, #0x6e8
: 17ffff9f b 4004b0 <printf@plt>
-O3:Optimize yet more. ‘-O3’ turns on all optimizations specified by ‘-O2’ and also turns on more optimization flags00000000004005f8 <func1>:
4005f8: adrp x2, <_init-0x430>
4005fc: 36f80080 tbz w0, #, 40060c <func1+0x14>
: add w1, w0, #0x2
: 911ba040 add x0, x2, #0x6e8
: 17ffffaa b 4004b0 <printf@plt>
40060c: add w1, w0, #0x1
: 911ba040 add x0, x2, #0x6e8
: 17ffffa7 b 4004b0 <printf@plt> <func2>:
: adrp x2, <_init-0x430>
40061c: 37f80080 tbnz w0, #, 40062c <func2+0x14>
: add w1, w0, #0x1
: 911ba040 add x0, x2, #0x6e8
: 17ffffa2 b 4004b0 <printf@plt>
40062c: add w1, w0, #0x2
: 911ba040 add x0, x2, #0x6e8
: 17ffff9f b 4004b0 <printf@plt>
00000000004005f4 <func1>:
4005f4: adrp x2, <_init-0x430>
4005f8: 37f80080 tbnz w0, #, <func1+0x14>
4005fc: add w1, w0, #0x1
: 911b8040 add x0, x2, #0x6e0
: 17ffffab b 4004b0 <printf@plt>
: add w1, w0, #0x2
40060c: 17fffffd b <func1+0xc> <func2>:
: adrp x2, <_init-0x430>
: 37f80080 tbnz w0, #, <func2+0x14>
: add w1, w0, #0x1
40061c: 911b8040 add x0, x2, #0x6e0
: 17ffffa4 b 4004b0 <printf@plt>
: add w1, w0, #0x2
: 17fffffd b 40061c <func2+0xc>
-Os主要是对代码尺寸的优化(可以看到,此时两个func反汇编出来的汇编指令是最少的),但是从执行效率看,就差点,likely和unlikey此时对代码没有起到任何优化效果。
likely和unlikely是如何对代码的优化?的更多相关文章
- Java开发代码性能优化总结
代码优化,可能说起来一些人觉得没用.可是我觉得应该平时开发过程中,就尽量要求自己,养成良好习惯,一个个小的优化点,积攒起来绝对是有大幅度效率提升的.好了,将平时看到用到总结的分享给大家. 代码优化的目 ...
- Java开发中程序和代码性能优化
现在计算机的处理性能越来越好,加上JDK升级对一些代码的优化,在代码层针对一些细节进行调整可能看不到性能的明显提升, 但是我觉得在开发中注意这些,更多的是可以保持一种性能优先的意识,对一些敲代码时间比 ...
- Python 代码性能优化技巧(转)
原文:Python 代码性能优化技巧 Python 代码优化常见技巧 代码优化能够让程序运行更快,它是在不改变程序运行结果的情况下使得程序的运行效率更高,根据 80/20 原则,实现程序的重构.优化. ...
- Android代码内存优化建议-OnTrimMemory优化
原文 http://androidperformance.com/2015/07/20/Android代码内存优化建议-OnTrimMemory优化/ OnTrimMemory 回调是 Androi ...
- JAVA_eclipse 保留Java文件时自动格式化代码和优化Import
Eclipse 保存Java文件时自动格式化代码和优化Import Eclipse中format代码的快捷方式是ctrl+shift+F,如果大家想保存 java文件的时候 自动就格式化代码+消除不必 ...
- JavaScript代码性能优化总结
JavaScript 代码性能优化总结 尽量使用源生方法 javaScript是解释性语言,相比编译性语言执行速度要慢.浏览器已经实现的方法,就不要再去实现一遍了.另外,浏览器已经实现的方法在算法方面 ...
- Html代码seo优化最佳布局实例讲解
搜索引擎对html代码是非常优化的,所以html的优化是做好推广的第一步.一个符合seo规则的代码大体如下界面所示. 1.<!–木庄网络博客–> 这个东西是些页面注释的,可以在这里加我的& ...
- 利用封装、继承对Java代码进行优化
注:本文实例分别可以在oldcastle(未优化的代码)和newcastle(优化后的代码)中查看,网址见文末 城堡游戏: 城堡中有多个房间,用户通过输入north, south, east, wes ...
- java代码之美(11)---java代码的优化
java代码的优化 随着自己做开发时间的增长,越来越理解雷布斯说的: 敲代码要像写诗一样美.也能理解有一次面试官问我你对代码有洁癖吗? 一段好的代码会让人看就像诗一样,也像一个干净房间会让人看去很舒服 ...
- 嵌入式程序设计中C/C++代码的优化
虽然使软件正确是一个工程合乎逻辑的最后一个步骤,但是在嵌入式的系统开发中,情况并不总是这样的.出于对低价产品的需求,硬件的设计者需要提供刚好足够的存储器和完成工作的处理能力.所以在嵌入式软件设计的最后 ...
随机推荐
- haproxy 配置文件详解 之 综述
HAProxy 配置文件根据功能和用途,主要有5 个部分组成,但有些部分并不是必须的,可以根据需要选择相应的部分进行配置. 1.global 部分 用来设定全局配置参数,属于进程级的配置,通常和操作系 ...
- Spring Boot进阶系列四
这边文章主要实战如何使用Mybatis以及整合Redis缓存,数据第一次读取从数据库,后续的访问则从缓存中读取数据. 1.0 Mybatis MyBatis 是支持定制化 SQL.存储过程以及高级映射 ...
- 【操作系统之十一】任务队列、CPU Load、指令乱序、指令屏障
一.CPU Loadcpu load是对使用或者等待cpu进程的统计(数量的累加):每一个使用(running)或者等待(runnable)CPU的进程,都会使load值+1;每一个结束的进程,都会使 ...
- spring boot2X整合nacos一使用Feign实现服务调用
服务调用有两种方式: A.使用RestTemplate 进行服务调用 查看 B.使用Feign 进行声明式服务调用 上一次写了使用RestTemplate的方式,这次使用Feign的方式实现 服务注册 ...
- python数据分析5 数据转换
1数据转换 数据转换时数据准备的重要环节,它通过数据平滑,数据聚集,数据概化,规范化等凡是将数据转换成适用于数据挖掘的形式 1.1 数据平滑 去除数据中的噪声,将连续数据离散化.这里可以采用分箱.聚类 ...
- QuantLib 金融计算——基本组件之 Money 类
目录 QuantLib 金融计算--基本组件之 Money 类 概述 构造函数 成员函数 如果未做特别说明,文中的程序都是 python3 代码. QuantLib 金融计算--基本组件之 Money ...
- [EXP]CVE-2019-1821 Cisco Prime Infrastructure思科未授权远程代码执行漏洞
CVE-2019-1821 Cisco Prime Infrastructure Remote Code Execution https://srcincite.io/blog/2019/05/17/ ...
- 在flask中使用sqlalchemy插入数据返回新增的id
user = User(‘name’=‘张三’)db.session.add(user)db.session.flush()#输出新插入数据的主键print(user.id)#此时数据才插入到数据库中 ...
- 彻底解决springboot修改页面和代码会自动重启
3.application.yml配置 spring.devtools.restart.enabled=falsespring.thymeleaf.cache=false 1.解决thymeleaf修 ...
- [WeChat-Bot-Go] 记录帖
本来是想写一个微信机器人出来,用go语言. Github 目标是想做一个自动发送消息和抢红包的bot. 一开始跟着 这篇 文章写.写着写着发现文章久远,而且用的是第一版网页微信api,所以就自己去 ...