现代CPU中的小分支

问题描述 投票:7回答:1

像Kaby Lake这样的现代CPU如何处理小分支? (在下面的代码中是跳转到标签LBB1_67)。据我所知,分支不会有害,因为跳转不如16字节块大小,这是解码窗口的大小。

或者是否有可能由于某些宏观操作融合,分支将被完全省略?

        sbb     rdx, qword ptr [rbx - 8]
        setb    r8b
        setl    r9b
        mov     rdi, qword ptr [rbx]
        mov     rsi, qword ptr [rbx + 8]
        vmovdqu xmm0, xmmword ptr [rbx + 16]
        cmp     cl, 18
        je      .LBB1_67
        mov     r9d, r8d
.LBB1_67:                               #   in Loop: Header=BB1_63 Depth=1
        vpcmpeqb        xmm0, xmm0, xmmword ptr [rbx - 16]
        vpmovmskb       ecx, xmm0
        cmp     ecx, 65535
        sete    cl
        cmp     rdi, qword ptr [rbx - 32]
        sbb     rsi, qword ptr [rbx - 24]
        setb    dl
        and     dl, cl
        or      dl, r9b
performance x86-64 cpu-architecture avx branch-prediction
1个回答
6
投票

在任何x86 CPU中,短分支距离都没有特殊情况。甚至无条件的jmp到下一条指令(架构上都是nop)需要有效处理正确的分支预测;如果你连续放入足够的BTB条目并且性能下降了悬崖。 Slow jmp-instruction

获取/解码只是一个小问题;是的,在同一个缓存行中的一个非常短的分支仍将在L1i和可能的uop缓存中命中。但是,解码器不太可能特殊情况下预测的前向跳转并利用包含分支和目标的一个块的预解码指令边界发现。

当指令被解码为uop并被送入前端时,寄存器值不可用;这些仅适用于乱序执行后端。

主要问题是,当执行.LBB1_67:之后的指令时,架构状态根据是否采用分支而不同。微架构状态(RAT = Register Allocation Table)也是如此。

或者:

  • r9取决于sbb / setl结果(mov r9d, r8d没有运行)
  • r9取决于sbb / setb结果(mov r9d, r8d确实运行)

条件分支在计算机体系结构术语中称为“控制依赖性”。分支预测+推测执行避免将控制依赖性转换为数据依赖性。如果预测没有采取je,则setl结果(r9的旧值)被mov覆盖,并且在任何地方都不再可用。

在检测到je中的错误预测(实际应该已经采取)之后,没有办法从中恢复,特别是在一般情况下。当前的x86 CPU不会尝试查找重新加入路径的直通路径,也不会查找有关它的功能的任何内容。

如果cl很长时间没有准备就绪,那么很长一段时间都没有发现错误预测,or dl, r9b之后的许多指令都可以使用错误的输入执行。在一般情况下,可靠+有效恢复的唯一方法是放弃从“错误”路径完成的所有指令工作。例如,检测vpcmpeqb xmm0, [rbx - 16]仍然以任何一种方式运行都很难,而且没有找到。 (现代英特尔,自Sandybridge以来,有一个分支顺序缓冲区(BOB),它在分支上快照RAT,允许在执行检测到它时立即高效回滚到分支未命中,同时仍然允许在先前指令的无序执行期间继续执行在那之前,一个分支小姐不得不回到退休状态。)


一些非x86 ISA的CPU(例如我认为的PowerPC)已经尝试过将正好跳过1条指令的分支转换为预测(数据依赖性)而不是推测它们。例如Dynamic Hammock Predication for Non-predicated Instruction Set Architectures讨论了这个想法,甚至决定是否在每个分支的基础上进行谓词。如果你的分支预测历史表明这个分支预测不好,那么预测它可能是好的。 (一个Hammock分支是向前跳过一个或几个指令的分支。在具有固定宽度指令字的ISA上检测正好1个指令的情况是微不足道的,如RISC,但在x86上很难。)

在这种情况下,x86有一个cmovcc指令,一个ALU选择操作,根据标志条件产生两个输入之一。 cmove r9d, r8d而不是cmp / je会使分支错误预测免疫,但代价是引入数据依赖clr8d以获取使用r9d的指令。英特尔CPU不会尝试为您执行此操作。

(在Broadwell和后来的英特尔,cmov只有1 uop,低于2. cmp / jcc是1 uop,而mov本身也是1 uop,所以在未采取的情况下cmov前端的uops也较少在所采用的情况下,即使预测正确,也可以在管道中引入气泡,具体取决于代码的吞吐量有多高:阶段之间的队列是否可以吸收它。)

有关CMOV比分支慢的情况,请参阅gcc optimization flag -O3 makes code slower than -O2,因为引入数据依赖性很糟糕。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.