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++代码的优化
虽然使软件正确是一个工程合乎逻辑的最后一个步骤,但是在嵌入式的系统开发中,情况并不总是这样的.出于对低价产品的需求,硬件的设计者需要提供刚好足够的存储器和完成工作的处理能力.所以在嵌入式软件设计的最后 ...
随机推荐
- 以V8中js源码为例了解GitHub查看代码功能
GitHub作为开源仓库,许多开源项目仓库这里,当然不乏十分优秀的,比如Node.V8,我一直比较好奇js源码,像java的话,因为环境是JDK,我们结合IDE很容易就能跳转到其源码内部去查看实现,但 ...
- Spring Cloud 升级最新 Greenwich 版本,舒服了~
去年将 Spring Cloud 升级到了 Finchley 版本: Spring Cloud 升级最新 Finchley 版本,踩了所有的坑! 这个大版本栈长是踩了非常多的坑啊,帮助了不少小伙伴. ...
- 【技术博客】 Laravel 5.1单元测试(PHPUnit)入门
目录 Laravel 5.1单元测试(PHPUnit)入门 简介 安装与配置 1. 安装 2. 配置 编写测试样例 1. 新建测试样例 2. 编写函数的测试 3. 编写Web功能测试 运行测试与查看结 ...
- activiti学习7:spring和activiti进行整合
目录 activiti学习7:spring和activiti进行整合 一.整合原理 二.整合步骤 2.1 新建一个maven工程并导入相关依赖 2.2 创建spring配置文件 三.测试 activi ...
- 实现一个特殊的栈,要求push,poll , getMin方法时间复杂度都是O(N)
借助两个栈来实现 public class GetMinStack { private Stack<Integer> stackData; private Stack<Integer ...
- [转帖]UML类图关系图解
UML类图关系图解 https://www.cnblogs.com/TvvT-kevin/p/9357339.html 一.类结构 在类的UML图中,使用长方形描述一个类的主要构成,长方形垂直地分为三 ...
- 【Python】处理Excel的库Xlwings
# # 引入库 import xlwings as xw import time # 打开Excel程序,默认设置:程序可见,只打开不新建工作薄 # app = xw.App(visible=True ...
- DDR3(4):读控制
写控制完成后开始设计读控制,写控制和读控制是非常相似的. 一.总线详解 由 User Guide 可知各信号之间的逻辑关系,读数据是在给出命令之后一段时间后开始出现的.图中没有给出app_rd_dat ...
- HashMap源码原理
HashMap源码解析(负载因子,树化策略,内部hash实现,resize策略) 内部属性: 负载因子: final float loadFactor(默认为0.75f) 实际容量: int thre ...
- MVC拦截
1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Web; 5using Syst ...