码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 如何在代码层面提高CPU分支预测效率


    朋友们可以关注下我的公众号,获得最及时的更新:

     

    或者关注我的知乎账号 : https://www.zhihu.com/people/zhangyachen

     

    关于分支预测的基本概念和详细算法可以参考我之前写的知乎回答,基本概念不再阐述了~~

     

    https://www.zhihu.com/question/486239354/answer/2410692045

     

    说几个常见的能够提升CPU分支预测效率的方法。

    将最常见的条件比较单独从switch中移出

    分支预测除了需要预测方向,还需要预测分支的目标地址。目标地址BTA(Branch Target Address)分为两种:

    • 直接跳转(PC-relative, direct) : offset以立即数形式固定在指令中,所以目标地址也是固定的。
    • 间接跳转(absolute, indirect):目标地址来自通用寄存器,而寄存器的值不固定。

    对于直接跳转,使用BTB可以很好的进行预测。但是对于间接跳转,目标地址不固定,更难预测。switch-case的指令实现(类似jmpq *$rax,$rax是case对应label地址)、C++虚函数调用就属于间接跳转。间接跳转如果还用直接跳转的BTB预测,准确率只有50%左右。

    很多CPU针对间接跳转都有单独的预测器,比如的Intel的论文The Intel Pentium M Processor: Microarchitecture and Performance中介绍额Indirect Branch Predictor:通过额外引入context-information——Global Branch History来提高间接跳转的目标地址预测准确率。

    switch-case的优点是将诸多if/else(conditional branch)转换为统一的unconditioal branch,但缺点就是目标地址难以预测。如果某个case的命中率特别高,就可以将其从switch中单独提出来,这样该分支的预测方向 && 目标地址都很好预测。

    比如java dubbo代码里的一个例子::

    超过99.9%情况state取值都是ChannelState.RECEIVED ,将其单独提出来。官网博客有一个benchmark,性能有很大的改观。

    将使用【控制】的条件转移转换为使用【数据】的条件转移

    CMOV指令就是典型的例子。CPU无需进行分支预测,但是会计算一个条件的两种结果,然后通过检查条件码,要么更新目的寄存器,要么保持不变。

    比如

    v = test-expr ? then-expr : else-expr

    会转换为下列伪代码:

    v = then-expr;
    ve = else-expr;
    t = test-expr;
    if(!t) v=ve;

    编译器会倾向于将使用三元运算符且两种结果的计算量不大的表达式转换为CMOV条件数据转移。例如facebook folly中的例子,注意看注释:

    当分支的结果完全由外部输入决定,local branch history和global branch history都毫无规律时,效果会更好。下面这个是《Computer Systems A Programmer's Perspective 》5.11.2小节的例子,第二个版本性能是第一个三倍:

    /* Rearrange two vectors so that for each i, b[i] >= a[i] */
    void minmax1(long a[], long b[], long n) {
      long i;
      for (i = 0; i < n; i++) {
        if (a[i] > b[i]) {
          long t = a[i];
          a[i] = b[i];
          b[i] = t;
        }
      }
    }
    
    /* Rearrange two vectors so that for each i, b[i] >= a[i] */
    void minmax2(long a[], long b[], long n) {
      long i;
      for (i = 0; i < n; i++) {
        long min = a[i] < b[i] ? a[i] : b[i];
        long max = a[i] < b[i] ? b[i] : a[i];
        a[i] = min;
        b[i] = max;
      }
    }

    使用算数逻辑代替分支

    比如ARM优化手册里提到,可以将范围比较转换为无条件计算,编译器有时候也会自动做这个转换:

    // origin version
    int insideRange1(int v, int min, int max) {
      return v >= min && v < max;
    }
    
    // optimized version
    int insideRange2(int v, int min, int max) {
      return (unsigned) (v - min) < (max - min);
    }

    韦易笑大佬针对这个做过更详细的优化和测试,反正我是看晕了:

    https://zhuanlan.zhihu.com/p/144963553

    https://zhuanlan.zhihu.com/p/147039093

    引用文章内的测试数据:

    Avoiding Branches里有更多的例子,不过用之前还是做测试更靠谱。

    使用template移除分支

    2018年Stephen Yang的博士论文NanoLog: A Nanosecond Scale Logging System介绍了一款C++日志库Nanolog,将日志调用开销的中位数降为了个位数纳秒级别。作者在文章NANOLOG: A NANOSECOND SCALE LOGGING SYSTEM中提到了Nanolog的关键技术和优化,第三条就是将printf在运行时的大量分支逻辑利用C++ template优化成编译期的运算。

    likely/unlikely

    这个很多人已经介绍过了,C++20已经将其标准化,支持将更可能执行的代码放在hot path上,对icache更友好。例如facebook folly中的例子:

    FOLLY_LIKELY是一个包装:

    更进一步,有些ISA的分支指令有一个bit,支持programmer去指定分支是否taken。现代CPU使用的TAGE分支预测器,部分实现会使用该bit去初始化predictor(是初始化,不是一直使用programmer指定的跳转结果)。TAGE预测器可以参考下我开头放的回答:https://www.zhihu.com/question/486239354/answer/2410692045

     

     

    (完)

     

    朋友们可以关注下我的公众号,获得最及时的更新:

  • 相关阅读:
    【香橙派 Orange pi AIpro】| 开发板深入使用体验
    新知实验室TRTC初体验
    DollarTree(美元树)验厂需要注意哪些方面?
    Ubuntu打造家用NAS三——网盘与影视中心
    Docker(二)、linux环境Docker的部署以及构建镜像
    ERR_PNPM_LINKING_FAILED Error: EPERM: operation not permitted, rename
    SyntaxError: invalid character ‘:‘ (U+FF1A)问题解决
    2022 阿里全球数学竞赛获奖名单公布,其中 00 后选手占了一半多,如何评价这一现象?
    使用rpm包制作本地镜像源(本地yum源)
    JavaWeb开发-06-SpringBootWeb-MySQL
  • 原文地址:https://www.cnblogs.com/zhangyachen/p/16173925.html
  • 最新文章
  • 【JVM】编译执行与解释执行的区别是什么?JVM 使用哪种方式?
    用 Hashids 优雅解决 C 端自增 ID 暴露问题
    V8引擎 精品漫游指南--Ignition篇(上) 指令 栈帧 槽位 调用约定 内存布局 基础内容
    LLVM Pass快速入门(四):代码插桩
    milkup:桌面端 markdown AI续写和即时渲染
    基于项目工程构建SBOM(软件物料清单)的研究
    鸿蒙应用开发UI基础第二节:鸿蒙应用程序框架核心解析与实操
    .NET 中如何快速实现 List 集合去重?
    扣子Coze实战:从0到1打造抖音+小红书热点监控智能体
    浅谈数据访问层
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
小工具 小游戏
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1

京公网安备 11010502049817号