GCC的使用方法
一、编译器的可选项介绍
1.启发式猜测 -fprofile-arcs 编译选项

在Linux操作系统中,我们常能见到几个基于预测分支的函数,如下:
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)这两个函数是GCC编译器里面的内建函数,我们可以查到'— Built-in Function: long __builtin_expect (long exp, long c)'
if else 句型编译后, 一个分支的汇编代码紧随前面的代码,而另一个分支的汇编代码需要使用JMP指令才能访问到。显然,通过JMP访问需要更多的时间, 在复杂的程序中,有很多的if else句型,又或者是一个有if else句型的库函数,每秒钟被调用几万次,通常程序员在分支预测方面做得很糟糕, 编译器又不能精准的预测每一个分支,这时JMP产生的时间浪费就会很大,函数__builtin_expert()就是用来解决这个问题的。
让我们来看下面的这一个例子:

上图是正常编译的情况,使用ARM编译器编译出来的,自然均是Thrumb指令集,需要你有一定的ARM的汇编指令理解。
进入main函数,我们先压栈,然后+4设置新的栈基地址,往下移动8字节分配局部变量空间,把立即数10存入寄存器中,然后放到刚才开辟的局部变量空间,然后再加载回寄存器中,然后我们与0进行比较,之后我们使用ble进行跳转。
这时候,反汇编出来的代码就是如图所示:

我们可以看到,似乎连赋值的代码都没有了,只剩下了打印的代码,这就是__builtin_expect函数的激进优化。 接下来我们加一个Volatile看看能不能将这个变量保留下来,因为我们想具体看到他是如何优化这个分支语句的。

我们可以看到,他确实栈上开辟了空间,把10放入了寄存器中,然后存入栈内存中,再放回r3,但是这些只是Volatile的作用,关键点在于cmp指令,我们可以看到cmp在原来的版本中直接就是ble跳转指令,但是在这个版本中 我们看到了movwgt,movwle,movtgt,movtle
gt(Greater Than):如果cmp的结果是大于 0,CPU 就执行带gt后缀的指令(加载“条件成立...”的地址)。le(Less or Equal):如果cmp的结果是小于等于 0,CPU 就执行带le后缀的指令(加载“条件不成立...”的地址)。
这是一种极其高效的优化手段。通常 if-else 需要跳转指令,这会打断 CPU 的流水线。通过这种写法,CPU 不需要跳转,它只是同时看到了两条路,根据刚才的比较结果,自动忽略掉其中一条无效指令,直接拿着正确的字符串地址去打印。
