【问题标题】:Understanding stalls and branch delay slots了解停顿和分支延迟槽
【发布时间】:2013-10-09 04:04:19
【问题描述】:

我正在学习计算机体系结构的课程。我从另一所大学找到了这个网站,其中有迄今为止对我有帮助的笔记和视频:CS6810, Univ of Utah。我正在处理该网站上发布的一些旧家庭作业,特别是this one。我正在尝试了解流水线和相关概念,特别是停顿和分支延迟槽。

我现在正在查看旧家庭作业中的第一个问题,但不确定如何解决这些问题。

问题如下:

考虑以下代码段,其中分支占 30% 的时间,而不是 占用了 70% 的时间。

R1 = R2 + R3

R4 = R5 + R6

R7 = R8 + R9

如果 R10 = 0,则跳转到 linex

R11 = R12 + R13

R14 = R11 + R15

R16 = R14 + R17

...

线:R18 = R19 + R20

R21 = R18 + R22

R23 = R18 + R21

...

考虑一个 10 级有序处理器,其中指令在第一个 阶段,分支结果经过三个阶段就知道了。估计 CPI 处理器在以下情况下(假设处理器中的所有停顿都是 分支相关和分支占所有执行指令的 15%):

  1. 在每个分支上,提取都会停止,直到知道分支结果为止。

  2. 每个分支都被预测为不被执行,如果分支被执行,错误获取的指令将被压缩。

  3. 处理器有两个延迟槽,分支后面的两条指令总是被取出并执行,并且

    3.1。您找不到任何说明来填补延迟槽。

    3.2。您可以在分支之前将两条指令移动到延迟槽中。

    3.3。您可以将标签“linex”之后的两条指令移动到延迟槽中。

    3.4。您可以在分支之后(在原始代码中)立即将一条(注意:一条,而不是两条!)指令移动到延迟槽中。

我什至不确定如何开始看待这个问题。我已经阅读了该网站上的所有笔记并观看了视频,并阅读了 H&P 书中的部分内容,但仍然对这个问题感到困惑。如果有人有时间,我会很感激有人帮助我解决这个问题。我只需要知道如何开始概念化答案。

【问题讨论】:

    标签: computer-architecture pipelining branch-prediction


    【解决方案1】:

    在所描述的流水线中,条件分支的方向和目标直到第三个周期结束才可用,因此直到第四个周期开始才能(确定地)获取分支之后的正确下一条指令。

    设计1

    处理分支后指令地址延迟可用性的一个明显方法是简单地等待。这就是设计 1 通过暂停两个周期所做的(这相当于获取两个不属于实际程序的空操作)。这意味着对于采用和未采用的路径,都会浪费两个周期,就像编译器插入了两条无操作指令一样。

    这里是流水线图(ST是停顿,NO是空操作,XX是取消指令,UU是无用指令,I1,I2,I3是分支前的三个指令[在填充任何延迟槽前的原始程序顺序],BI 是分支指令,I5、I6 和 I7 是分支后的直通指令,I21、I22 和 I23 是采用路径开始处的指令;IF是取指令阶段,DE是解码,BR是分支解析,S1是BR之后的阶段):

             Taken                  Not taken
             IF  DE  BR  S1 ...     IF  DE  BR  S1 ...
    cycle 1  BI  I3  I2  I1         BI  I3  I2  I1
    cycle 2  ST  BI  I3  I2         ST  BI  I3  I2
    cycle 3  ST  ST  BI  I3         ST  ST  BI  I3
    cycle 4  I21 ST  ST  BI         I5  ST  ST  BI
    cycle 5  I22 I21 ST  ST         I6  I5  ST  ST
    

    设计 2

    为了避免必须在 IF 阶段结束时检测到分支的存在,并允许有时完成一些有用的工作(在未采用的情况下),而不是让硬件有效地将无操作插入到管道中(即,在分支之后停止取指)硬件可以将分支视为任何其他指令,直到它在第三个流水线阶段解决。这是预测所有分支都没有被采用。如果分支被执行,那么在分支之后获取的两条指令被取消(实际上变成了无操作)。这是设计2:

             Taken                  Not taken
             IF  DE  BR  S1 ...     IF  DE  BR  S1 ...
    cycle 1  BI  I3  I2  I1         BI  I3  I2  I1
    cycle 2  I5  BI  I3  I2         I5  BI  I3  I2
    cycle 3  I6  I5  BI  I3         I6  I5  BI  I3
    cycle 4  I21 XX  XX  BI         I7  I6  I5  BI
    cycle 5  I22 I21 XX  XX         I8  I7  I6  I5
    

    设计 3

    总是预测一个分支不会被采用会在每次采用分支时浪费两个周期,因此开发了第三种机制来避免这种浪费——延迟分支。在延迟分支中,硬件始终在分支之后执行(不取消)延迟槽指令(示例中为两条指令)。通过始终执行延迟槽指令,简化了流水线。编译器的工作是尝试用有用的指令填充这些延迟槽。

    无论采用哪条路径,从分支之前获取的指令(在没有延迟分支的程序中)都是有用的(但是依赖关系会阻止编译器在分支之后调度任何此类指令)。编译器可以使用来自已采用或未采用路径的指令填充延迟槽,但这样的指令不能覆盖另一条路径(或在路径加入后)使用的状态,因为延迟槽指令不会被取消(与预言)。 (如果两条路径都连接——这对于 if-then-else 结构很常见——那么延迟槽可能会从连接点被填充;但这样的指令通常依赖于连接前至少一条路径的指令,哪个依赖项会阻止它们在延迟槽中使用。)如果编译器找不到有用的指令,它必须用空操作填充延迟槽。

    在情况 3.1(延迟分支设计的最坏情况)中,编译器找不到任何有用的指令来填充延迟槽,因此必须使用无操作来填充它们:

             Taken                  Not taken
             IF  DE  BR  S1 ...     IF  DE  BR  S1 ...
    cycle 1  BI  I3  I2  I1         BI  I3  I2  I1
    cycle 2  NO  BI  I3  I2         NO  BI  I3  I2
    cycle 3  NO  NO  BI  I3         NO  NO  BI  I3
    cycle 4  I21 NO  NO  BI         I5  NO  NO  BI
    cycle 5  I22 I21 NO  NO         I6  I5  NO  NO
    

    这在性能上等同于设计 1(停顿两个周期)。

    在案例 3.2(延迟分支设计的最佳案例)中,编译器从分支之前找到两条指令来填充延迟槽:

             Taken                  Not taken
             IF  DE  BR  S1 ...     IF  DE  BR  S1 ...
    cycle 1  BI  I1  ...            BI  I1  ...
    cycle 2  I2  BI  I1  ...        I2  BI  I1 ...
    cycle 3  I3  I2  BI  I1         I3  I2  BI  I1
    cycle 4  I21 I3  I2  BI         I5  I3  I2  BI
    cycle 5  I22 I21 I3  I2         I6  I5  I3  I2
    

    在这种情况下,无论分支是否被采用,所有管道槽都充满了有用的指令。性能 (CPI) 与没有延迟解析分支的理想管道相同。

    在案例 3.3 中,编译器使用来自所采用路径的指令填充延迟槽:

             Taken                  Not taken
             IF  DE  BR  S1 ...     IF  DE  BR  S1 ...
    cycle 1  BI  I3  I2  I1         BI  I3  I2  I1
    cycle 2  I21 BI  I3  I2         I21 BI  I3  I2
    cycle 3  I22 I21 BI  I3         I22 I21 BI  I3
    cycle 4  I23 I22 I21 BI         I5  UU  UU  BI
    cycle 5  I24 I23 I22 I21        I6  I5  UU  UU
    

    在未采用的路径中,I21 和 I22 是无用的。尽管它们实际上已执行(并更新状态),但在未采用的路径中(或在路径的任何连接之后)不使用此状态。对于未采用的路径,就好像延迟槽已被无操作填充。

    在情况 3.4 中,编译器只能从未采用的路径中找到一条安全指令,并且必须用空操作填充另一个延迟槽:

             Taken                  Not taken
             IF  DE  BR  S1 ...     IF  DE  BR  S1 ...
    cycle 1  BI  I3  I2  I1         BI  I3  I2  I1
    cycle 2  I5  BI  I3  I2         I5  BI  I3  I2
    cycle 3  NO  I5  BI  I3         NO  I5  BI  I3
    cycle 4  I21 NO  UU  BI         I6  NO  I5  BI
    cycle 5  I22 I21 NO  UU         I7  I6  NO  I5
    

    对于所采取的路径,执行了一条无用指令和一条无操作,浪费了两个周期。对于未采取的路径,执行一个no-op,浪费一个周期。

    计算 CPI

    本例中CPI的计算公式为:

    %non_branch * CPI_non_branch + %branch * CPI_branch
    

    CPI_branch 的计算方法是考虑分支本身所花费的时间 (baseCPI_branch) 以及分支在被占用时占用了浪费的周期的次数百分比,以及分支未被占用的次数与浪费的周期的百分比当它没有被采取。所以 CPI_branch 是:

    baseCPI_branch + (%taken * wasted_cycles_taken) + 
                     (%not_taken * wasted_cycles_not_taken)
    

    在理想的标量流水线中,每条指令需要一个周期,即每条指令的周期数为 1。在此示例中,非分支指令的行为就像流水线是理想的一样(“处理器中的所有停顿都与分支相关"),因此每个非分支指令的 CPI 为 1。同样,baseCPI_branch(不包括因停顿、无操作等而浪费的周期)为 1。

    根据上面的流水线图,可以确定在采用和未采用路径中浪费的周期数。该示例给出了分支的百分比以及已采用和未采用的分支的百分比。

    对于设计 1,采用和未采用的路径都浪费了 2 个周期,因此 CPI_branch 为:

    1 + (0.3 * 2) + (0.7 *2) = 3
    

    因此总 CPI 为:

    (0.85 * 1) + (0.15 * 3) = 1.3
    

    【讨论】:

    • 再次感谢您抽出时间回复我,但是,我发现这个概念特别令人困惑/令人沮丧。仅从第一个场景开始,通过阅读它们将发生在分支之前的场景,我不理解为什么在分支之后会发生停顿,在我看来。此外,在这种情况下它如何停止两个周期而不是一次?而且我没有按照你的 CPI 计算,这个公式不会适用于所有情况吗?
    • 我对这一切感到非常困惑,我提前道歉。我不遵循您对两个停顿的推理:是因为您在分支的两个结果中都停顿(if-then,if-else)?场景 3 让我感到头疼,因为我什至不明白如何或为什么可以这样移动指令,但也许如果我能理解第一个场景中发生的事情,它会更有意义。我如何找到第一个场景的 CPI “在每个分支上,提取都停止,直到分支结果已知”(因为它看起来在这里最容易理解)?
    • @basil 对于延迟槽,编译器会填充它们(因此给出的指令编号不会来自实际的可执行文件,而是来自编译器尝试填充分支延迟槽之前的版本)。这两个停顿是为了在第 4 周期开始时可以获取适当的指令(在第 3 周期结束时解决分支,使下一条指令地址可用)。设计 2 通过获取未采用的路径对此进行了改进,因此如果未采用分支,则有用的指令将进入管道。 ...
    • ...延迟分支实际上是预测未采取的一种扩展;无论分支的方向如何,编译器都会在分支之后放置不会取消的指令。理想情况下,这些指令对两个方向都有用(如案例 3.2),但由于它们没有被取消,它们不能覆盖以后使用的任何状态。我想我会尝试通过另一个答案的编辑来进一步澄清,但我很累,需要考虑如何表达。
    • @basil 最近的编辑(2013-10-05 14:45:27Z)是否阐明了如何处理分支风险以及如何计算 CPI?
    猜你喜欢
    • 2021-07-10
    • 2018-07-16
    • 2011-06-03
    • 1970-01-01
    • 2011-10-17
    • 2019-07-10
    • 1970-01-01
    • 1970-01-01
    • 2013-11-18
    相关资源
    最近更新 更多