在执行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. 下载mqtt.fx

    #下载mqtt.fxhttp://www.jensd.de/apps/mqttfx/1.7.1/

  2. Android Studio 之 GridView

    1. 数据源 ArrayList Cursor 2. 适配器 Adapter ArrayAdapter SmipleAdapter SmipleCursorAdapter BaseAdapter

  3. java面试题——高级篇

    一.集合 Hashmap的原理 源码分析参考文章:http://www.cnblogs.com/xwdreamer/archive/2012/06/03/2532832.html 题目参考文章:htt ...

  4. SKF密码设备研究

    SKF介绍 SKF接口是国密标准中智能密码钥匙的C语言应用开发接口标准,目前很多国内密码密码设备厂商都为其产品提供了SKF接口的开发包.开发者可以通过统一的SKF接口开发密码应用,访问来自不同设备供应 ...

  5. 微信第三方平台代公众号发起网页授权 48001 api unauthorized 问题

    https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&gr ...

  6. Spring JDBC最佳实践(1)

    原文地址:https://my.oschina.net/u/218421/blog/38513 Spring提供了两种使用JDBC API的最佳实践,一种是以JdbcTemplate为核心的基于Tem ...

  7. Airtest-UI 自动化集大成者

    前言 Airtest是由网易研发的一款基于Python的.跨平台的UI自动化测试框架,基于图像识别原理,适用于游戏和App.该项目目前已在Github上面开源: https://github.com/ ...

  8. 读《PMI 分析手册》

    目录 读<PMI 分析手册> 官方 PMI 基本概况 官方制造业 PMI 官方非制造业 PMI 综合 PMI 产出指数 PMI 分析框架 PMI 与经济周期 官方 PMI 分析 参考研报 ...

  9. phpcms新建模板页教程

    phpcms新建模板页教程1 直接去template文件夹里的复制的模板页 比方说list1.html2 去后台 界面模板风格 default 默认模板 点击详情列表 找到list1.htm 设置中文 ...

  10. 新博客 https://k8gege.org

    新博客 https://k8gege.org 于2019/12/3启用,忘了发 由于博客园长期被Google误报屏蔽,导致Firefox/Chrome等浏览器无法访问博客 发现将被Google误报的文 ...