C++关键字之likely和unlikely详解

 更新时间:2022年10月15日 11:50:13   作者:misaka-mikoto  
这篇文章主要介绍了C++关键字之likely和unlikely,C++20之前的,likely和unlikely只不过是一对自定义的宏,而C++20中正式将likely和unlikely确定为属性关键字,本文给大家详细讲解,需要的朋友可以参考下

什么是likely和unlikely

既然程序是我们程序员所写,在一些明确的场景下,我们应该比CPU和编译器更了解哪个分支条件更有可能被满足。我们是否可将这一先验知识告知编译器和CPU, 提高分支预测的准确率,从而减少CPU流水线分支预测错误带来的性能损失呢?答案是可以!它便是likely和unlikely。在Linux内核代码中,这两个宏的应用比比皆是。下面是他们的定义:

#define likely(x) __builtin_expect(!!(x), 1) 
#define unlikely(x) __builtin_expect(!!(x), 0)

likely,用于修饰if/else if分支,表示该分支的条件更有可能被满足。而unlikely与之相反
以下为示例。unlikely修饰argc > 0分支,表示该分支不太可能被满足。

#include <cstdio>

#define likely(x)       __builtin_expect(!!(x), 1)
#define unlikely(x)     __builtin_expect(!!(x), 0)

int main(int argc, char *argv[])
{
    if (unlikely(argc > 0)) {
        puts ("Positive\n");
    } else
    {
        puts ("Zero or Negative\n");
    }
    return 0;
}

likely/unlikely的原理

接下来,我们从汇编指令分析likely/unlikely到底是如何起作用的?
首先我们将上述代码中的unlikely去掉,然后反汇编,作为对照组
汇编如下,我们看到,if分支中的指令被编译器放置于分支跳转指令jle相邻的位置,即CPU流水线在遇到jle指令所代表的的'岔路口'时,更倾向于走if分支

.LC0:
        .string "Positive\n"
.LC1:
        .string "Zero or Negative\n"
main:
        sub     rsp, 8
        test    edi, edi                
        jle     .L2                     ; 如果argc <= 0, 跳转到L2
        mov     edi, OFFSET FLAT:.LC0   ; 如果argc > 0, 从这里执行
        call    puts
.L3:
        xor     eax, eax
        add     rsp, 8
        ret
.L2:
        mov     edi, OFFSET FLAT:.LC1
        call    puts
        jmp     .L3

接着我们在if分支中加上unlikely, 反汇编如下。这里的情况正好与对照组相反,if分支下的指令被编译器放置于远离跳转指令jg的位置。这意味着CPU此时更倾向于走else分支。

.LC0:
        .string "Positive\n"
.LC1:
        .string "Zero or Negative\n"
main:
        sub     rsp, 8
        test    edi, edi
        jg      .L6
        mov     edi, OFFSET FLAT:.LC1
        call    puts
.L3:
        xor     eax, eax
        add     rsp, 8
        ret
.L6:
        mov     edi, OFFSET FLAT:.LC0
        call    puts
        jmp     .L3

因此,通过对分支条件使用likely和unlikely,我们可给编译器一种暗示,即该分支条件被满足的概率比较大或比较小。而编译器利用这一信息优化其机器指令,从而最大限度减少CPU分支预测失败带来的惩罚。

likely/unlikely的适用条件

CPU有自带的分支预测器,在大多数场景下效果不错。因此在分支发生概率严重倾斜、追求极致性能的场景下,使用likely/unlikely才具有较大意义。

C++20中的likely/unlikely

C++20之前的,likely和unlikely只不过是一对自定义的宏。而C++20中正式将likely和unlikely确定为属性关键字。

int foo(int i) {
    switch(i) {
               case 1: handle1();
                       break;
    [[likely]] case 2: handle2();
                       break;
    }
}

到此这篇关于C++关键字之likely和unlikely详解的文章就介绍到这了,更多相关C++ likely和unlikely内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • c++统计文件中字符个数代码汇总

    c++统计文件中字符个数代码汇总

    本文给大家汇总介绍了3种使用C++实现统计文件中的字符个数的方法,非常的简单实用,有需要的小伙伴可以参考下。
    2015-09-09
  • 基于C++中常见编译错误的总结详解

    基于C++中常见编译错误的总结详解

    本篇文章是对C++中的常见编译错误进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • 程序员都不知道C语言中的这些小细节

    程序员都不知道C语言中的这些小细节

    本文通过7到实例题目给大家展示C语言中的一些小细节,很少有朋友真正的掌握,感兴趣的朋友跟随小编一起看看吧
    2021-05-05
  • C++实现LeetCode(133.克隆无向图)

    C++实现LeetCode(133.克隆无向图)

    这篇文章主要介绍了C++实现LeetCode(133.克隆无向图),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • C++ NFS挂载及挂载命令

    C++ NFS挂载及挂载命令

    这篇文章主要介绍了C++ NFS挂载,文中给大家提到了挂载NFS时常用的命令,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-12-12
  • C++ 多态虚函数的底层原理深入理解

    C++ 多态虚函数的底层原理深入理解

    这篇文章主要介绍了C++ 多态虚函数的底层原理深入理解,多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为,通常是父类调用子类的重写函数,在C++中就是 父类指针指向子类对象,此时父类指针的向下引用就可以实现多态
    2022-08-08
  • C++中strstr函数的实现方法总结

    C++中strstr函数的实现方法总结

    这篇文章主要介绍了C++中strstr函数的实现方法总结的相关资料,希望通过本文能帮助到大家,让大家掌握这部分内容,需要的朋友可以参考下
    2017-10-10
  • C语言学习之标识符的使用详解

    C语言学习之标识符的使用详解

    C语言标识符是用于表示变量、函数、常量、类型等程序元素的名称,这篇文章将通过一些简单的示例为大家介绍一下C语言标识符的使用,需要的可以参考一下
    2023-05-05
  • C++之Qt5双缓冲机制案例教程

    C++之Qt5双缓冲机制案例教程

    这篇文章主要介绍了C++之Qt5双缓冲机制案例教程,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • C++实现LeetCode(35.搜索插入位置)

    C++实现LeetCode(35.搜索插入位置)

    这篇文章主要介绍了C++实现LeetCode(35.搜索插入位置),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07

最新评论