我认为要回答您的问题,您首先需要了解分支预测的工作原理。为了解释这一点,有必要首先知道它为什么做出这些预测。这需要了解现代处理器中的管道是如何工作的。我解释这一点的最好方法是从非流水线 CPU 处理指令的方式开始。
(如果我复习您已经知道的事情,请耐心等待。目前尚不完全清楚您对分支预测的困惑源于何处。)
较旧的 CPU 以及许多简单的现代 CPU 以相当简单明了的方式处理指令。它们将执行指令所需的动作分解为一系列步骤。每条指令都通过这些步骤运行,一旦完成所有这些步骤,它就会进入下一个。例如,一个假设的简单非流水线 CPU 可能遵循如下一系列步骤:
- 将指令提取到内存中。
- 读入指令的操作数。
- 执行该指令的操作。
- 写出结果
以这种方式实现 CPU 相对简单,但它对处理器资源的使用效率非常低。当 CPU 在执行和指令过程中执行一个步骤时,用于实现其他步骤的所有硅片都处于空闲状态。
现代流水线处理器试图通过将执行指令所需的步骤序列转变为类似于装配线的东西来提高效率。指令经过一系列步骤,或者称为阶段,就像在非流水线 CPU 中一样。不同之处在于,一旦一条指令清除了流水线的第一阶段,CPU 就可以向下发送另一条指令。这允许几条指令同时在流水线中,希望能很好地利用芯片的所有部分。虽然一条指令仍然需要经过许多不同的阶段,但理想情况下,指令一个接一个地从流水线中快速出来。流水线不会改善从开始到结束执行指令的时间,而是缩短指令完成之间的时间。
现在,这是对现代流水线的相当简单的描述,它掩盖了使现代流水线 CPU 设计复杂化的许多问题。然而,就分支预测而言,只有一个复杂的问题需要解决:执行分支指令时要做什么。
首先,分支指令本身不需要做任何特别的事情。它可以像其他任何东西一样被扔掉。一旦清除了第一阶段,CPU 就可以发送下一条指令——这就是问题所在。下一条指令是什么?由于它是一个分支,它可以采用两种不同的方式,并且 CPU 在分支指令完成其通过管道的旅程之前不会知道哪种方式。
处理器要做的简单事情就是等待。由于在等待时没有其他指令被送入管道,因此管道清空。只有当分支指令退出(现在为空的)流水线时,CPU 才能恢复将指令放入。下一条指令必须经过现在为空的流水线的所有阶段,导致分支指令完成与下一条指令之间存在延迟指令可以。在这种情况下,指令不会像理想情况下那样快速连续退出管道。
这种延迟在现代处理器上可能相当大。它是流水线中阶段数的函数,基本上每个阶段一个周期。大多数现代 x86 CPU 在其管道中大约有 15 个阶段,因此以这种方式实现分支的成本非常高。一个具有非常短管道的简单 CPU 可能能够摆脱一直等待的情况,但现代处理器必须做其他事情。他们做出预测。
最简单的预测形式是静态分支预测。处理器仅查看分支指令本身来猜测它是否会被采用。最简单的静态预测形式是假设没有采用所有分支,因为这种情况经常发生。更高级的静态预测器假设采用向后的分支,而不采用向前的分支。这假设向后分支是循环,并且循环通常执行不止一次。
静态预测可以很好地工作,但它仍然会做出很多糟糕的预测。您可以通过使用某种动态分支预测来改进这一点。有各种不同的方法可以做到这一点,这里就不多说了,但他们都根据以前的分支走的历史做出了猜测。
然而,CPU 最终做出了预测,它继续进行,就好像它做出了正确的猜测一样。将分支指令放入流水线后,它开始向下发送它假定将执行的指令。这称为推测执行,像这样处理的指令被标记为推测执行。这让管道知道它不应该对这些无法撤消的指令做任何事情。
当分支指令到达流水线末端时,CPU 现在将知道它的猜测是否正确。如果它做出了正确的预测,它不需要做任何事情,它可以让前面的指令完成流水线的旅程。因为它猜对了,所以分支没有额外的成本。
如果它猜错了,它必须撤消推测执行的指令可能已经完成的任何操作,清除管道并开始发送它应该执行的指令。这会导致相同的延迟,就好像它根本没有做出预测一样。
所以回答你的问题“分支预测器如何知道它是否不正确?” 是它不知道也不关心它是否做出正确的预测。它只是做出预测。如果它是一个动态分支预测器,那么它将在其历史记录中记录该分支是否被采用,但它不会记录它是否做出了正确的决定。