是 mov rax,0x12345678; jmp rax 仍然会杀死分支预测?
Posted
技术标签:
【中文标题】是 mov rax,0x12345678; jmp rax 仍然会杀死分支预测?【英文标题】:is mov rax,0x12345678; jmp rax still kills branch prediction? 【发布时间】:2012-10-19 08:56:51 【问题描述】:我在查找特定于上述两种情况的信息时遇到了问题, 尽管听取了您的专家意见。
第一件事是:我知道间接 jmps 会伤害分支预测,并且即使间接的结果是恒定的,它仍然需要预测维护缓冲区和其他东西,所有这些都与绝对 jmp 相比。
我的问题是,如果有人知道:
mov rax, 1234567812345678h;
jmp rax;
处理器的分支预测器仍然认为是间接的,或者在这种情况下它是否进行数学运算..
我这样做是因为 x64 没有直接的“jmp absolute 64”指令,只有间接的。 :/ (How to execute a call instruction with a 64-bit absolute address? 建议这样做,如果你不能将跳跃距离目标足够近并使用jmp rel32
。)
其次,在这个程度上,jmp 0x1234 和 call 0x1234 之间是否有任何真正的区别(在处理器优化方面(指令缓存、预取器及其提示、分支预测))? (vc2012“速度优化”产生调用,“min_size opt”产生jmp,“混合优化”产生x64的jmp,调用x86)
【问题讨论】:
不要混淆分支预测和分支目标预测。分支预测是是否分支会跳转。分支目标预测是分支将跳转的哪里。在这种情况下,没有分支预测 - 这是一个无条件跳转。 我还要补充一点,在这种情况下,分支目标预测可能非常好(如果 CPU 记得上次的分支目标,那么......)。 因此,如果我理解正确,那么 RAX 被硬编码预初始化为固定地址并没有太大区别(就 cpu 硬件资源而言),并且 RAX 是易失性的,它将使用目标预测两种情况下的cpu资源? (并且后一种情况的唯一额外成本是从另一个 var 读取 RAX 的间接成本(例如)),或者说“它之前硬编码预初始化了一行,所以我不需要占用分支目标历史缓冲区") ? 我担心的是管道中的预取器/早期阶段是否会认为由于 jmp 是 eax 基础它不是“绝对的”,因此它不能预取目标指令,直到最后一分钟当它到达 jmp 时(当它肯定知道 rax 值时),而不是断定 rax 是硬编码的,所以 jmp 是绝对的.. 【参考方案1】:英特尔的分支目标(和分支)预测既非常复杂,又是保密的商业机密。不一定只有一种算法,也就是说,您可以预期预测机制会因 CPU 而异;这取决于英特尔希望针对给定处理器解决问题的晶体管数量。当然,除了英特尔,还有其他 x86 和 x64 处理器制造商。
历史分支目标预测机制——使用相同指令的过去运行来预测后续执行的目标——几乎肯定会预测该分支的正确目标,因为只有一个。因此,如果重新执行此代码序列(例如在循环中)并且它会在指令缓存中停留一段时间,它可能会得到很好的处理。 (但是,在某些处理器上,如果其他地方发生另一个分支导致哈希冲突,则分支目标预测机制可能会被类似于缓存行冲突的效果中和。)
一个更大的问题可能是,如果这样的序列大量出现在新加载到缓存中的代码中,它的处理效果如何,这取决于处理器的非基于历史的目标预测能力。这种(非历史的)分支目标预测可以很容易地确定给定此代码序列的分支位置,尽管这完全取决于制造商是否认为它值得任何给定处理器的裸片上的不动产。做出此类决定的因素包括功耗、权衡其他性能改进(即可能更好地使用相同裸片区域)以及此类和各种其他代码序列的预期频率。
【讨论】:
但是Agner记录了英特尔cpu的一些特性;分支预测在第 11-34 页。 我还没有听说过任何 x86-64 CPU 将 mov r64,imm64 / jmp reg 融合到一个单一的 direct-jmp uop 中,或者甚至基于此进行预测。 ARM CPU 对拇指分支执行类似的操作,这些指令在技术上是 2 条指令,一条用于设置分支目标的一些位,另一条用于其余部分并跳转。但这只是成对使用,没有注册副作用,而且很常见。这些都不适用于 x86 分支:更常见的是内存间接分支(都调用动态库)【参考方案2】:“我知道间接 jmps 会伤害分支预测”
没有。分支预测和间接跳转预测是不同的。此外,间接跳转用于基于表的 switch 语句和解释器中。这些是非常常见的用例,并出现在基准测试中。因此,英特尔和其他公司花费了大量的精力和大量的晶体管来提高它们的性能。一篇论文(在问题之后写得很好!)甚至说从 Sandy Bridge 开始,you shouldn't trust folklore 谈到这种间接跳跃预测。英特尔+AMD 有动力提高这一性能,而且他们确实有。
现在,如果您的 jmp 示例是冷代码,如果这是第一次执行,则无法预测,实际上 Skylake 间接跳转预测器将预测跳转后的下一条指令并从那里推测。您可以使用非法指令 UD2 关闭该推测。无论如何,第二次执行 jmp,(如果它还在 BTB 中)分支目标将是正确的。
关于您的第二个问题,缓存效果无关紧要。我想较小的版本可以英勇地保存缓存行溢出,但仅此而已。硬件预取器用于数据,而不是指令。
【讨论】:
您链接的论文 (hal.inria.fr/hal-01100647/document) 表明 Haswell 而不是 SnB 在预测解释器中的 grand-central-dispatch 分支方面确实做得很好。 (相信是使用 IT-TAGE)。当然,总是去同一个地方的间接分支更容易预测,并且任何形式的间接分支预测都会成功(除非破坏性混叠),因此如果分支频繁运行,即使 Atom 或 Pentium 2 也不会出现问题。 "在下一代处理器 Sandy Bridge 上,误预测率要低得多。"关键是他们已经解决了好几代人。 顺便说一句,分支预测通常包括间接分支的目标预测。您有点暗示它们是具有相似范围的两个不同事物,例如分支方向预测与间接分支目标预测。对于排除间接分支的“分支预测”,AFAIK 没有特定的单一含义。还要注意,在当前块甚至被解码以查看它是否包含任何分支(包括相对直接)之前,前端需要预测下一个要获取的 block。 (Slow jmp-instruction) 好吧,是的,SnB 的分支预测器比 NHM 更好。但是,Haswell 对预测器的内部工作方式做出了最大的改变,这是第一次使用 IT-TAGE,正如那篇论文用它的图表等显示的那样。特别是因为您在那段中谈到了解释器,所以是 Haswell 让简单的调度执行得很好。 Core i7 分支预测器的 CAAQA 部分描述了条件分支的 2 位和锦标赛预测器。但它随后描述了间接预测器,说“一个单独的单元预测间接分支的目标地址”。我认为这意味着他们不会竞争相同的 BTB 插槽。至于分支与跳跃预测,CAAQA 在其 ILP 限制研究部分中对其进行了区分。 (但顺便说一句,那本书的定义很糟糕。) 至于预取器,我不知道用于指令的硬件缓存行预取是一回事。是 L1i 内存预取还是只是缓存提升?以上是关于是 mov rax,0x12345678; jmp rax 仍然会杀死分支预测?的主要内容,如果未能解决你的问题,请参考以下文章
关于间接寻址,如 %segreg:disp(base,index,scale),foo