我最近在考虑现代CPU中的分支预测。据我所知,分支预测是必要的,因为当在管道中执行指令时,我们在执行分支之前并不知道条件运算的结果。
既然我知道现代无序的CPU可以以任何顺序执行指令,只要它们之间的数据依赖性得到满足,我的问题是,CPU是否可以以这样的方式重新排列指令,即在CPU需要执行分支时已经知道分支目标,从而可以"预测"分支方向,因此根本不需要猜测?
那么CPU可以转动这个吗:
do_some_work();
if(condition()) //evaluating here requires the cpu to guess the direction or stall
do_this();
else
do_that();
对此:
bool result = condition();
do_some_work(); //bunch of instructions that take longer than the pipeline length
if(result) //value of result is known, thus decision is always 100% correct
do_this();
else
do_that();
一个特殊且非常常见的用例是在集合上迭代,其中退出条件通常是循环不变的(因为我们在迭代集合时通常不会修改集合)。
我的问题是,现代CPU能做到这一点吗?如果能,已知哪些特定的CPU内核具有这一功能?
请记住,分支预测是在管道的早期完成的,因此您仍然没有对指令进行解码,并且您无法解决数据依赖性,因为您不知道使用了哪个寄存器。你可能会在某个地方记住这一点,但这并不是100%(因为你的存储容量/时间会受到限制),所以这几乎就是你的普通分支预测器所做的——仅根据指令指针推测目标。
然而,提前进行条件评估是有用的,这在过去已经做过了,而且主要是一种编译器技术,但可能会通过一些硬件支持(例如,提升分支条件)来增强。不过,分支预测失误对性能的主要影响是评估延迟,因为现在分支恢复本身很短。
这意味着,编译器只需提升条件并提前计算,而无需任何硬件修改,就可以减轻大部分惩罚——如果您预测错误了分支,您仍需支付flush的惩罚(而使用当代预测因子的几率通常很低),但您会知道,在对分支本身进行解码后(因为数据将提前准备好),因此损坏将仅限于使其通过该分支的极少数指令。
然而,能够提升评估并不简单。在大多数情况下,编译器可能能够检测是否存在任何直接的数据依赖关系(在您的示例中为do_some_work()
),但在大多数情况中都会存在。循环不变量是编译器今天已经移动的第一件事。此外,一些最难预测的分支依赖于一些内存提取,并且通常不能假设内存会保持不变(可以,之后会进行一些特殊检查,但大多数常见的编译器不会这样做)。不管怎样,它仍然是一种编译器技术,而不是分支预测的根本改变。
执行分支预测是因为CPU的指令获取器需要知道在分支指令之后要获取哪些指令,而这要等到分支执行之后才能知道。
如果一个处理器有一个5级流水线(大多数处理器有更多),如下所示:
- 指令获取
- 指令解码
- 寄存器读取
- ALU执行
- 寄存器回写
提取器将停滞3个周期,因为直到ALU执行周期之后才知道分支结果。
提升分支测试条件并不能解决从获取分支指令到执行分支指令的延迟问题。