在最新的英特尔软件开发手册中,它描述了两个操作码前缀:
Group 2 > Branch Hints
0x2E: Branch Not Taken
0x3E: Branch Taken
这些允许跳转指令(操作码如Jxx
)的显式分支预测
我记得几年前读到过,在x86上显式分支预测在gccs分支预测的内部环境中基本上是无操作的。
我现在不清楚这些x86分支提示是否是一个新特性,或者它们实际上是否没有操作。
谁能把这个弄清楚?
(即:gccs分支预测函数是否生成这些x86分支提示?现在的英特尔cpu不会忽略它们吗?-这是什么时候发生的?)
更新:
我创建了一个快速测试程序:int main(int argc, char** argv)
{
if (__builtin_expect(argc,0))
return 1;
if (__builtin_expect(argc == 2, 1))
return 2;
return 3;
}
反汇编为:
00000000004004cc <main>:
4004cc: 55 push %rbp
4004cd: 48 89 e5 mov %rsp,%rbp
4004d0: 89 7d fc mov %edi,-0x4(%rbp)
4004d3: 48 89 75 f0 mov %rsi,-0x10(%rbp)
4004d7: 8b 45 fc mov -0x4(%rbp),%eax
4004da: 48 98 cltq
4004dc: 48 85 c0 test %rax,%rax
4004df: 74 07 je 4004e8 <main+0x1c>
4004e1: b8 01 00 00 00 mov $0x1,%eax
4004e6: eb 1b jmp 400503 <main+0x37>
4004e8: 83 7d fc 02 cmpl $0x2,-0x4(%rbp)
4004ec: 0f 94 c0 sete %al
4004ef: 0f b6 c0 movzbl %al,%eax
4004f2: 48 85 c0 test %rax,%rax
4004f5: 74 07 je 4004fe <main+0x32>
4004f7: b8 02 00 00 00 mov $0x2,%eax
4004fc: eb 05 jmp 400503 <main+0x37>
4004fe: b8 03 00 00 00 mov $0x3,%eax
400503: 5d pop %rbp
400504: c3 retq
400505: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
40050c: 00 00 00
40050f: 90 nop
我没有看到2E或3E ?也许gcc出于某种原因忽略了它们?
这些指令前缀对现代处理器(任何比Pentium 4更新的处理器)都没有影响。它们只占用一个字节的代码空间,因此,不生成它们是正确的。
有关详细信息,请参阅Agner Fog的优化手册,特别是3。微体系结构:http://www.agner.org/optimize/
《Intel®64和IA-32架构优化参考手册》不再在关于优化分支的章节(第3.4.1节)中提到它们:http://www.intel.de/content/dam/doc/manual/64-ia-32-architectures-optimization-manual.pdf
这些前缀是Netburst架构的(无害的)残余。在全面优化中,您可以使用它们来对齐代码,但现在它们的作用仅限于此。
gcc不生成前缀是正确的,因为它们对自Pentium 4以来的所有处理器都不起作用。
但是__builtin_expect
有其他效果,比如将不期望的代码路径从代码中的缓存热点位置或内联决策中移动出来,所以它仍然是有用的。
虽然Pentium 4是唯一真正尊重分支提示指令的一代,但大多数cpu确实具有某种形式的静态分支预测,可用于实现相同的效果。这个答案与最初的问题有点离题,但我认为这对任何来到这个页面的人都是有价值的信息。
Intel优化指南和Agner Fog的指南(这里已经提到过)都对这个特性有很好的描述。
英特尔对新一代的看法:
使条件分支后面的失败代码成为具有forward目标
的分支的可能目标
因此,静态预测算法预测代码中向前跳转的条件分支不会被采用。
这与GCC似乎使用__builtin_expect
生成的内容是一致的:'预期的' return 1
/return 2
代码被放置在来自条件分支的未采取路径中,这将被静态预测为未采取。
:
在分支目标缓冲区中没有历史记录的分支使用静态预测算法进行预测:
预测采取无条件分支
预测不采取间接分支
因此,在GCC将无条件jmp
s放置到函数末尾的"预期"未采取的路径中,这些跳转将被静态预测为采取(即不跳过)。
Intel也说:
使条件分支后面的失败代码不太可能成为具有向后目标
的分支的目标
因此,静态预测算法将预测代码中向后跳转的条件分支。
根据Agner Fog,大多数 pentium 也遵循这个算法:
在PPro, P2, P3, P4和P4E上,一个以前没有见过的控制传递指令,或者它不在分支目标缓冲区中,如果它向前走,预计会失败,如果它向后走,则会被采取(例如循环)。在这些处理器上,静态预测比动态预测需要更长的时间。
然而,Core 2系列(和Pentium M)有一个完全不同的策略:
这些处理器不使用静态预测。预测器只是在第一次看到分支时进行随机预测,这取决于分配给新分支的BTB条目中发生了什么。正确预测跳跃或不跳跃的概率只有50%,但预测的目标是正确的。
AMD处理器显然也是如此:
预测第一次看到的分支不被占用。预测一个分支总是在它第一次被取走之后被取走。动态预测仅在获取分支之后使用,然后不使用。分支提示前缀没有作用。
还有一个额外的因素需要考虑:cpu通常喜欢以线性方式执行,因此即使正确预测的取走分支通常也比正确预测的未取走分支更昂贵。
Intel®64和IA-32架构软件开发人员手册->第2卷:指令集参考,A-Z ->第2章:指令格式-> 2.1保护模式、实地址模式和虚拟8086模式的指令格式-> 2.1.1指令前缀
一些早期的微架构使用这些作为分支提示,但最近世代没有,它们被保留以供将来使用。