如何理解宏"可能"影响分支预测



我注意到,如果我们知道控制流很有可能是真是假,我们可以告诉编译器,例如,在Linux内核中,有很多likelyunlikely,实际上是由gcc提供的__builtin_expect实现的,所以我想知道它是如何工作的,然后检查那里的程序集:

20:branch_prediction_victim.cpp ****             if (array_aka[j] >= 128)
184                            .loc 3 20 0 is_stmt 1
185 00f1 488B85D0              movq    -131120(%rbp), %rax
185      FFFDFF
186 00f8 8B8485F0              movl    -131088(%rbp,%rax,4), %eax
186      FFFDFF
187 00ff 83F87F                cmpl    $127, %eax
188 0102 7E17                  jle     .L13

然后对于__builtin_expect

20:branch_prediction_victim.cpp ****             if (__builtin_expect((array_aka[j] >= 128), 1))
184                            .loc 3 20 0 is_stmt 1
185 00f1 488B85D0              movq    -131120(%rbp), %rax
185      FFFDFF
186 00f8 8B8485F0              movl    -131088(%rbp,%rax,4), %eax
186      FFFDFF
187 00ff 83F87F                cmpl    $127, %eax
188 0102 0F9FC0                setg    %al
189 0105 0FB6C0                movzbl  %al, %eax
190 0108 4885C0                testq   %rax, %rax
191 010b 7417                  je      .L13
  • 188-setg设置如果大于,这里设置如果大于什么
  • 189-movzbl移动零扩展字节到长,我知道这一次移动%al%eax
  • 190-testq位OR然后设置ZF CF标志,对吗

我想知道它们是如何影响分支预测和提高性能的,还有三条额外的指令,需要更多的周期,对吗?

setcc读取FLAGS,在本例中由cmp之前设置。阅读手册。

这看起来像是忘记了启用优化,所以__builtin_expect只是在寄存器中创建一个0/1布尔值,并对其进行非零分支,而不是对原始FLAGS条件进行分支不要看未优化的代码,它总是很糟糕的

线索是:作为likely的一部分,脑死的booleaning,以及使用RBP作为movq -131120(%rbp), %rax的帧指针从堆栈加载j


likely通常不会改进运行时分支预测,它改进了代码布局,以在按照源代码所说的方式进行时(即快速情况(最大限度地减少分支数量。因此,对于常见情况,它改进了I-cache的局部性。例如,编译器会把事情安排好,所以常见的情况是一个未执行的条件分支,只是失败了。这使得超标量流水线CPU中的前端更容易同时获取/解码多条指令。继续在直线上提取是最容易的。

对于您知道是可预测的情况,likely实际上可以让编译器使用分支而不是cmov,即使编译器启发式(没有概要文件引导的优化(会出错。相关:gcc优化标志-O3使代码比-O2 慢

相关内容

  • 没有找到相关文章

最新更新