define likely(x) __builtin_expect(!!(x),1)
define unlikely(x) __builtin_expect(!!(x),0)
这两个宏的主要作用,就是告诉编译器:某一个分支发生的概率很高,或者说很低,基本不可能发生。编译器就根据这个提示信息,就会去做一些分值预测的编译优化。在这两个宏定义有一个细节,就是对宏的参数 x 做两次取非操作,这是为了将参数 x 转换为布尔类型,然后与 1 和 0 作比较,告诉编译器 x 为真或为假的可能性很高。
__builtin_expect 这个函数的主要用途就是编译器的分支预测优化。现代 CPU 内部,都有 cache 这个缓存器件。CPU 的运行速度很高,而外部 RAM 的速度相对来说就低了不少,所以当 CPU 从内存 RAM 读写数据时就会有一定的性能瓶颈。为了提高程序执行效率,CPU 都会通过 cache 这个 CPU 内部缓冲区来缓存一定的指令或数据。CPU 读写内存 RAM 中的数据时,会先到 cache 里面去看看能不能找到。找到的话就直接进行读写;找不到的话,cache 会重新缓存一部分内存数据进来。CPU 读写 cache 的速度远远大于内存 RAM,所以通过这种方式,可以提高系统的性能。
那 cache 如何缓存内存数据呢?简单来说,就是依据空间相近原则。比如 CPU 正在执行一条指令,那么下一个指令周期,CPU 就会大概率执行当前指令的下一条指令。如果此时 cache 将下面几条指令都缓存到 cache 里面,下一个指令周期 CPU 就可以直接到 cache 里取指、翻译、执行,从而使运算效率大大提高。
但有时候也会出现意外。比如程序在执行过程中遇到函数调用、if 分支、goto 跳转等程序结构,会跳到其它地址执行,那么缓存到 cache 中的指令就不是 CPU 要获取的指令。此时,我们就说 cache 没有命中,cache 会重新缓存正确的指令代码给 CPU 读取,这就是 cache 工作的基本流程。
有了这个理论基础,我们在编写程序时,遇到 if/switch 这种选择分支的程序结构,可以将大概率发生的分支写在前面,这样程序运行时,因为大概率发生,所以大部分时间就不需要跳转,程序就相当于一个顺序结构,从而提高 cache 的命中率。内核中已经实现一些相关的宏,如 likely 和 unlikely,用来提醒程序员优化程序。
————————————————
版权声明:本文为CSDN博主「KK20」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/kz01081/article/details/106165718
网友评论