在执行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用的不符合实际情况,代码的执行效率更恶化。
 
  下面我们在看看不同的优化等级下,对最终生成的机器码有什么影响:
  -O2:Optimize even more. GCC performs nearly all supported optimizations that do not involve a space-speed tradeoff. As compared to ‘-O’, this option increases both compilation time and the performance of the generated code.
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 flags
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>
 
  -Os:Optimize for size. ‘-Os’ enables all ‘-O2’ optimizations that do not typically increase code size. It also performs further optimizations designed to reduce code size.
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是如何对代码的优化?的更多相关文章

  1. Java开发代码性能优化总结

    代码优化,可能说起来一些人觉得没用.可是我觉得应该平时开发过程中,就尽量要求自己,养成良好习惯,一个个小的优化点,积攒起来绝对是有大幅度效率提升的.好了,将平时看到用到总结的分享给大家. 代码优化的目 ...

  2. Java开发中程序和代码性能优化

    现在计算机的处理性能越来越好,加上JDK升级对一些代码的优化,在代码层针对一些细节进行调整可能看不到性能的明显提升, 但是我觉得在开发中注意这些,更多的是可以保持一种性能优先的意识,对一些敲代码时间比 ...

  3. Python 代码性能优化技巧(转)

    原文:Python 代码性能优化技巧 Python 代码优化常见技巧 代码优化能够让程序运行更快,它是在不改变程序运行结果的情况下使得程序的运行效率更高,根据 80/20 原则,实现程序的重构.优化. ...

  4. Android代码内存优化建议-OnTrimMemory优化

    原文  http://androidperformance.com/2015/07/20/Android代码内存优化建议-OnTrimMemory优化/ OnTrimMemory 回调是 Androi ...

  5. JAVA_eclipse 保留Java文件时自动格式化代码和优化Import

    Eclipse 保存Java文件时自动格式化代码和优化Import Eclipse中format代码的快捷方式是ctrl+shift+F,如果大家想保存 java文件的时候 自动就格式化代码+消除不必 ...

  6. JavaScript代码性能优化总结

    JavaScript 代码性能优化总结 尽量使用源生方法 javaScript是解释性语言,相比编译性语言执行速度要慢.浏览器已经实现的方法,就不要再去实现一遍了.另外,浏览器已经实现的方法在算法方面 ...

  7. Html代码seo优化最佳布局实例讲解

    搜索引擎对html代码是非常优化的,所以html的优化是做好推广的第一步.一个符合seo规则的代码大体如下界面所示. 1.<!–木庄网络博客–> 这个东西是些页面注释的,可以在这里加我的& ...

  8. 利用封装、继承对Java代码进行优化

    注:本文实例分别可以在oldcastle(未优化的代码)和newcastle(优化后的代码)中查看,网址见文末 城堡游戏: 城堡中有多个房间,用户通过输入north, south, east, wes ...

  9. java代码之美(11)---java代码的优化

    java代码的优化 随着自己做开发时间的增长,越来越理解雷布斯说的: 敲代码要像写诗一样美.也能理解有一次面试官问我你对代码有洁癖吗? 一段好的代码会让人看就像诗一样,也像一个干净房间会让人看去很舒服 ...

  10. 嵌入式程序设计中C/C++代码的优化

    虽然使软件正确是一个工程合乎逻辑的最后一个步骤,但是在嵌入式的系统开发中,情况并不总是这样的.出于对低价产品的需求,硬件的设计者需要提供刚好足够的存储器和完成工作的处理能力.所以在嵌入式软件设计的最后 ...

随机推荐

  1. haproxy 配置文件详解 之 综述

    HAProxy 配置文件根据功能和用途,主要有5 个部分组成,但有些部分并不是必须的,可以根据需要选择相应的部分进行配置. 1.global 部分 用来设定全局配置参数,属于进程级的配置,通常和操作系 ...

  2. Spring Boot进阶系列四

    这边文章主要实战如何使用Mybatis以及整合Redis缓存,数据第一次读取从数据库,后续的访问则从缓存中读取数据. 1.0 Mybatis MyBatis 是支持定制化 SQL.存储过程以及高级映射 ...

  3. 【操作系统之十一】任务队列、CPU Load、指令乱序、指令屏障

    一.CPU Loadcpu load是对使用或者等待cpu进程的统计(数量的累加):每一个使用(running)或者等待(runnable)CPU的进程,都会使load值+1;每一个结束的进程,都会使 ...

  4. spring boot2X整合nacos一使用Feign实现服务调用

    服务调用有两种方式: A.使用RestTemplate 进行服务调用 查看 B.使用Feign 进行声明式服务调用 上一次写了使用RestTemplate的方式,这次使用Feign的方式实现 服务注册 ...

  5. python数据分析5 数据转换

    1数据转换 数据转换时数据准备的重要环节,它通过数据平滑,数据聚集,数据概化,规范化等凡是将数据转换成适用于数据挖掘的形式 1.1 数据平滑 去除数据中的噪声,将连续数据离散化.这里可以采用分箱.聚类 ...

  6. QuantLib 金融计算——基本组件之 Money 类

    目录 QuantLib 金融计算--基本组件之 Money 类 概述 构造函数 成员函数 如果未做特别说明,文中的程序都是 python3 代码. QuantLib 金融计算--基本组件之 Money ...

  7. [EXP]CVE-2019-1821 Cisco Prime Infrastructure思科未授权远程代码执行漏洞

    CVE-2019-1821 Cisco Prime Infrastructure Remote Code Execution https://srcincite.io/blog/2019/05/17/ ...

  8. 在flask中使用sqlalchemy插入数据返回新增的id

    user = User(‘name’=‘张三’)db.session.add(user)db.session.flush()#输出新插入数据的主键print(user.id)#此时数据才插入到数据库中 ...

  9. 彻底解决springboot修改页面和代码会自动重启

    3.application.yml配置 spring.devtools.restart.enabled=falsespring.thymeleaf.cache=false 1.解决thymeleaf修 ...

  10. [WeChat-Bot-Go] 记录帖

    本来是想写一个微信机器人出来,用go语言. Github   目标是想做一个自动发送消息和抢红包的bot. 一开始跟着 这篇 文章写.写着写着发现文章久远,而且用的是第一版网页微信api,所以就自己去 ...