关于CPU分支指令流水线预测

155 篇文章 1 订阅
CPU在执行代码时会预读指令到缓存中,频繁的跳转指令如JMP会导致流水线中断和效率下降。文章通过C/C++代码示例解释了如何通过分支预测优化,比如使用likely和unlikely宏或C++20的[[likely]],[[unlikely]]关键字来指导编译器优化,以提高性能,尤其是在高并发和计算密集型场景下。
摘要由CSDN通过智能技术生成

CPU在执行程式代码指令时,会预测并读入一部分指令到CPU缓存存储器中(注意不是很慢的内存),因为CPU访问内存的效率跟访问CPU内部L1/L2/L3高速缓存存储器的效率根本不是一回事。

所以,如果CPU指令流水线被频繁打断,导致CPU需要不停的擦除缓存并重新从内存中读入数据会导致机器代码指令的执行效率会变低一些。

注意:二进制程式机器代码在 Windows/Linux 之中是存放在共享缓存中,是的,我们执行的PE程式机器代码是放在虚拟内存之中的,这也意味着操作系统可能会将PE程式执行的代码部分放入硬盘IO,当需要重新读回的时,从硬盘IO之中拉回物理内存,这个过程损耗略大。

所以硬盘读写速度不够快,其实会影响到整个操作系统的工作效率的,OK:走题了。

所以:在追求极致效能的场景上面,我们应当考虑并尝试对CPU指令流水线分支预测进行优化,尽量防止CPU频繁的被中断被预读缓存到CPU高速缓存器之中的指令块。

但在此之前,人们需要知晓CPU在什么情况下会导致CPU指令流水线缓存被中断,我的答案很简单,JMP跳转指令的情况,只要人们执行了任何形式的跳转指令都将导致CPU指令流水线被中断,例如:JNE、JE、JLE、JGE、JO、JA、JB、JBE等跳转指令。

以下为一个用于演示分析的 C/C++ 代码:

int add(int x, int y) 
{ 
    if (x == 100) {
        return x - y;
    }
    else {
        return x * y; 
    }
}

以下为上述用于演示分析 C/C++ 代码的目标平台输出指令:

add(int, int):                               # @add(int, int)
        push    rbp
        mov     rbp, rsp
        mov     dword ptr [rbp - 8], edi
        mov     dword ptr [rbp - 12], esi
        cmp     dword ptr [rbp - 8], 100
        jne     .LBB0_2
        mov     eax, dword ptr [rbp - 8]
        sub     eax, dword ptr [rbp - 12]
        mov     dword ptr [rbp - 4], eax
        jmp     .LBB0_3
.LBB0_2:
        mov     eax, dword ptr [rbp - 8]
        imul    eax, dword ptr [rbp - 12]
        mov     dword ptr [rbp - 4], eax
.LBB0_3:
        mov     eax, dword ptr [rbp - 4]
        pop     rbp
        ret

 从上述汇编代码的输出之中,我们可以很明确的看到:

        cmp     dword ptr [rbp - 8], 100
        jne     .LBB0_2

执行了CMP指令并设置JZ标志位,如果JZ为1则不跳转否则跳转到 .LBB0_2 的RVA相对位置进行执行(JNE 不等于时则跳转)。

所以可以得出CPU分支预测时缓存的指令为预读一部分:if (x == 100)  条件成立(真/TRUE)时所需执行的指令。

在 GUN C/C++ 之中提供了 likely、unlikely 两个编译器宏(仅GUN GCC编译器支持)其作用则为显示的约定:IF / ELSE 语句块中,那部分更倾向被编译器优化令CPU分支指令流水线时缓存预读指令,但注意它的效能提升并不巨大,唯有当某个确定行为需被执行的重复次数超级多时,才会获得一定略微显著的提升。

if (likely(条件)) {

...

}

else {

...

}

例如上述代码的分支预测与:if (条件) { .... } else { ... } 是相同的,但并非绝对,例如:我们编写了略微复杂的 condition 情况时,在不提前声明 likey 的情况,IF 条件成立会使用JMP转移到其它位置进行执行,导致CPU分支预测时倾向于ELSE的部分,unlikey的作用顾名思义是相反的,此处不再过多的啰嗦。

C/C++ 20 语言标准中提供两个关键字,其作用与 likely、unlikey 作用一致,拽写的形式为:

[[likey]]、[[unlikey]]    C++ attribute: likely, unlikely (since C++20) - cppreference.com

在现代 C/C++ 编译器之中,编译器输出的代码都倾向于让CPU预测 IF 条件成立时包含的块所执行的指令序列,而非为ELSE,大多数情况下 C/C++ 编译器都可以很好的让代码的执行效率拉到最高,但并非绝对的,具体的优化场景人们需要根据目的实现及编译器输出的目标平台指令最终结果作为实际参考依据。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值