【发布时间】:2017-02-02 13:54:34
【问题描述】:
在 x86_64 中,没有 64 位地址的直接跳转。只有一个 32 位的。 通过间接跳转,我了解管道必须在分支预测发挥作用之前解决一次。 我的问题是:在第一次执行时,有没有办法在 64 位中进行 1-3 个周期的跳转?
【问题讨论】:
标签: x86 x86-64 cpu-architecture micro-optimization branch-prediction
在 x86_64 中,没有 64 位地址的直接跳转。只有一个 32 位的。 通过间接跳转,我了解管道必须在分支预测发挥作用之前解决一次。 我的问题是:在第一次执行时,有没有办法在 64 位中进行 1-3 个周期的跳转?
【问题讨论】:
标签: x86 x86-64 cpu-architecture micro-optimization branch-prediction
直接跳转并不总是“第一次”那么便宜,即使没有 I-cache 未命中。他们仍然需要分支预测。
在长模式下,jcc rel32 和 jmp rel32(以及 rel8 紧凑版本)使用 RIP 的符号扩展相对位移。您可以跳转到任何 64 位地址,只要您来自 2GB 以内的地址。因此,请将您的代码与其他代码保持在 2GB 以内,这样您就可以使用 rel32 置换。
在长模式下没有绝对的直接跳转。 32 位模式的 far JMP ptr16:32 (opcode 0xEA) 和 far CALL ptr16:32 根本没有 64 位版本。 (而且为了性能和方便,无论如何你都不想要一个远 jmp。)像 SYSCALL 和 INT 这样的指令是间接跳转(具有隐式目标),无论如何都没有用。
也没有指令预取/预解码指令来获取 L1 I-cache 或 uop 缓存中的目标热,或者任何暗示管道即将需要从给定地址解码指令的方式。
请参阅PREDECODE wishlist section in Darek Mihocka's article 关于模拟器中的间接跳转,其中让一条客户指令的处理程序直接跳转到下一条客户指令的处理程序是很有用的,而不是使用几乎总是会错误预测的间接调用调度指令. (或者至少当 Mihocka 在 IT-TAGE 分支预测器或多或少地解决该问题之前(在 Intel Haswell 及更高版本的 AMD Zen 或 Zen2 中)时,Mihocka 写道它是有用的:Branch Prediction and the Performance of Interpreters - Don’t Trust Folklore 2015 by Rohou 、Swamy 和 Seznec。)
即使是直接跳转也需要分支目标缓冲区来预测下一个 fetch-block 应该来自其他地方。比解码阶段更早需要此信息,因此必须对其进行预测以避免显着的前端气泡。最近一个有趣的问题提出了这个问题:Slow jmp-instruction。 Realworldtech forum thread 上的回复清楚地表明,分支预测需要在获取块上工作,而不仅仅是指令,即使在易于解码的固定 insn 宽度 ISA(与 x86 不同)上,您也需要早于可以得到解码结果。
对于新出现的直接 (rel32) 跳转,对于代码获取气泡的大小而言,1-3 个周期是不现实的。不过,该气泡的一部分可能会被解码的微指令队列隐藏。
要解码的代码提取可能至少需要 5 或 6 个周期,并且可能更多。假设 L1-I 命中时间为 4 个周期,与 Haswell 的 L1D 负载使用延迟相同。然后 Intel CPU 进行预解码以标记指令边界,然后解码阶段最多解码 4 uop。 David Kanter's Haswell writeup has a diagram of the frontend.
Slow jmp-instruction 问题中的 OP 数据表明,一大块只有 JMP 指令在 Intel Broadwell 上以每 12 个时钟约 1 个 JMP 运行(分支目标=next insn),所以这是你最糟糕的情况,因为你没有做任何其他让前端有时间赶上的事情,所以根本无法隐藏获取/解码气泡。
我假设我们正在讨论从旧版解码器运行。运行from the uop cache 时的 BTB 未命中可能会稍短一些,因为解码后的 uop 可用速度更快。如果分支 target 也命中了 uop 缓存,那么在解码后的 uop 开始进入已解码的 uop 队列(用作循环缓冲区的同一缓冲区)之前的周期也会更少。
如果在代码获取气泡期间解码的微指令队列没有为空,那么在问题阶段可能没有任何气泡(将微指令发送到 CPU 的无序部分)。
或者,如果 OOO 部分有很多未执行的微指令需要处理(即 CPU 正在执行一些代码,其瓶颈限制 IPC 远小于前端带宽),前端气泡可能不会影响太多了。
不过,间接分支更糟糕。最多只有几个周期后才能检测到正确的目标,此时 jmp uop 在后端执行,以检查预测。从错误预测中恢复涉及从执行的错误路径回滚任何独立工作,这与在发出任何错误路径指令/微指令之前重新引导前端不同。
您的基本前提是正确的:间接分支并不便宜,应尽可能避免。 (虽然一个间接分支可能比一小段条件分支便宜,例如this example。)
相关:
【讨论】: