基本思想

  • 核心思想

    • 记录和检测指令相关,操作数一旦就绪就立即执行,把发生RAW冲突的可能性减少到最小;
    • 通过寄存器换名来消除WAR冲突WAW冲突
  • IBM 360/91首先采用了Tomasulo算法在这里插入图片描述
    IBM之所以回会采用Tomasulo算法,是基于以下几个方面的考虑
    (1)IBM360/91的设计目标是基于整个360系列的统一的指令系统和编译器来实现高性能,而不是设计和利用专用的编译器来提高性能。这样,就需要更多地依赖于硬件;
    (2)IBM 360 体系结构只有4个双精度浮点寄存器,限制了编译器调度的有效性;
    (3)360/91的访存时间和浮点计算时间都很长。

    通过寄存器换名可以消除 WAR 冲突和 WAW 冲突
    考虑以下代码在这里插入图片描述
    第2条和第5条指令之间存在输出相关,可能导致WAW冲突;第2条和第5条指令之间存在反相关,可能导致WAR冲突。若是采用记分牌算法,可能会导致长时间的停顿。可以采用消除名相关的办法,引入两个临时寄存器S和T:在这里插入图片描述
    下面在MIPS指令集的情况下来介绍 Tomasulo 算法在这里插入图片描述
    先对上图中的各组成部件做一简要说明:
    (1)保留站(Reservation Station)
    保留站设置在运算部件的入口。浮点加法器的入口处共有三个加法保留站,分别命名为Add1、Add2、Add3;浮点乘法器的入口处有两个保留站,分别命名为Mult1、Mult2.每个保留站都有一个标识,唯一地标识了该保留站。每个保留站中保存一条已经流出并等待到本功能部件执行的指令,其内容包括操作码、操作数以及用于检测和解决冲突的信息。在一条指令流出到保留站的时候,如果该指令的源操作数已经在寄存器中就绪,则将之取到该保留站中。如果操作数还没有就绪,则在该保留站中记录将产生这个操作数的保留站的标识。
    (2)公共数据总线CDB(Common Data Bus)
    CDB是该结构中的一条重要的数据通路,所有功能部件的计算结果都是送到CDB上,由它把这些结果直接播送到各个需要该结果的地方。从存储器读取的数据也是送到CDB上。CDB连接到除了load缓冲器以外的所有部件的入口。在具有多个执行部件且采用多发射(即每个时钟周期发射多条指令)的流水线中,需要采用多条CDB。
    (3)load 缓冲器和store缓冲器
    load 缓冲器和 store缓冲器存放的是读写存储器的数据或地址。它们的行为和保留站类似,所以在后面的讨论中也把它们当作保留站来看待。只有在必须区分它们时,才加以区分。
    load缓冲器的作用有以下三个:
     - 存放用于计算有效地址的分量;
     - 记录正在进行的 load操作,等待存储器的响应;
     - 保存已经完成了的load的结果(即从存储器取来的数据),等待CDB传输。
    类似地,store缓冲器的作用有以下三个
     - 存放用于计算有效地址的分量;
     - 保存正在进行的 store 访存的目标地址,该 store 正在等待存储数据的到达;
     - 保存该store的地址和数据,直到存储部件接收。
    (4)浮点寄存器 FP
    共有 16 个浮点寄存器:F0,F2,F4,…,F30. 它们通过一对总线连接到功能部件,并通过 CDB 连接到 store 缓冲器.
    (5)指令队列
    指令部件送来的指令放入指令队列;
    指令队列中的指令按先进先出的顺序发射.
    (6)运算部件
    浮点加法器完成加法和减法操作
    浮点乘法器完成乘法和除法操作

    在Tomasulo算法中,寄存器换名是通过保留站和发射逻辑来共同完成的。当指令发射时,如果其操作数还没有计算出来,则将该指令中相应的寄存器号换名为将产生这个操作数的保留站的标识。指令发射到保留站后,其操作数寄存器号或者换成了数据本身(如果该数据已经就绪),或者换成了保留站的标识,不再与寄存器有关系。
    举一个简单的例子,将上面的结构图做个简化,并增加一个寄存器状态表 Q j Q_j Qj.在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
    (1)参见图(b),当指令 M U L MUL MUL 流出到保留站 M u l t 1 Mult1 Mult1 时,由于其操作数 a a a b b b 就绪(在 F 2 F2 F2 F 4 F4 F4 中),就将它们从寄存器取到保留站,这样该指令以后就跟 F 2 F2 F2 F 4 F4 F4 没有关系了,执行时直接从保留站中取数据。同时将目的寄存器 F 0 F0 F0 对应的 Q i Q_i Qi 标志置为 M u l t 1 Mult1 Mult1,表示该寄存器的内容将由保留站 M u l t 1 Mult1 Mult1 提供(如图(b)中的虚线所示)。
    (2)参见图(c),当指令 A D D ADD ADD 流出到保留站 A d d 1 Add1 Add1 时,也将操作数 c c c 取到保留站,但发现 F 0 F0 F0 中的操作数还没有就绪,于是就把其提供者 M u l t 1 Mult1 Mult1 的标识取到保留站中。这样就有两个地方在等 M u l t 1 Mult1 Mult1 的结果。同时,它将目的寄存器 F 2 F2 F2 对应的 Q i Q_i Qi 标志置为 A d d 1 Add1 Add1,表示该寄存器的内容将由保留站 A d d 1 Add1 Add1 提供。
    (3)参见图(d),当 M u l t 1 Mult1 Mult1 的运算结果产生后(设为 e e e),就把数据放到总线上(广播),所有等待该数据的地方都会自动把数据取走。 A d d 1 Add1 Add1 中的 A D D ADD ADD 指令得到该数据后,马上就可以开始执行。

    Tomasulo 算法采用分布的保留站,因而具有以下两个特点:
    (1)冲突检测和指令执行控制是分布的。每个功能部件的保留站中的信息决定了什么时候指令可以在该功能部件开始执行。
    (2)计算结果通过 CDB 直接从产生它的保留站传送到所有需要它的功能部件,而不用经过寄存器。

指令执行步骤:
使用 Tomasulo 算法的流水线需3段:

(1)流出

从指令队列的头部取一条指令。如果该指令的操作所要求的保留站有空闲的,就把该指令送到保留站(设为 r r r)。并且,如果其操作数在寄存器中已经就绪,就将这些操作数送入保留站r。如果其操作数还没有就绪,就把将产生该操作数的保留站的标识送入保留站 r r r. 这样,一旦被记录的保留站完成计算,它就直接把数据送给保留站 r r r。这一步实际上是进行了寄存器换名(换成保留站的标识)和对操作数进行了缓冲,消除了WAR冲突。另外,还要完成对目的寄存器的预约工作,将之设置为接收保留站 r r r 的结果。这实际上相当于提前完成了写操作(预约)。由于指令是按程序顺序流出的,当出现多条指令写同一个结果寄存器时,最后留下的预约结果肯定是最后一条指令的,就是说消除了 WAW 冲突。
当然,如果没有空闲的保留站,指令就不能流出,这是发生了结构冲突。

(2)执行。

如果某个操作数还没有计算出来,本保留站将监视 CDB,等待所需的计算结果。一旦那个结果产生,它就被放到 CDB 上,本保留站将立即获得该数据。当两个操作数都就绪后,本保留站就用相应的功能部件开始执行指令规定的操作。这里是等到所有操作数都备齐后才开始执行指令,也就是说是靠推迟执行的方法解决 RAW 冲突的。由于结果数据是从其产生的部件(保留站)直接送到需要它的地方,所以这已经是最大限度地减少了 RAW 冲突的影响。
显然,保留站有可能会出现多条指令在同一时钟周期完成就绪的情况。不同的功能部件可以并行执行,但在一个功能部件内部,就绪的多条指令就得逐条地处理。可以采用随机的方法选择要执行的指令。
load 和 store 指令的执行需要两个步骤:计算有效地址(要等到基地址寄存器就绪)和把有效地址放入 load 和 store 缓冲器。load缓冲器中的load指令的执行条件是存储器部件就绪。而store缓冲器中的store指令在执行前必须等到要存入存储器的数据到达。通过按顺序进行有效地址计算来保证程序顺序,这有助于避免访问存储器的冲突。
(3)写结果
功能部件计算完毕后,就将计算结果放到 CDB 上,所有等待该计算结果的寄存器和保留站(包括 store 缓冲器)都同时从 CDB 上获取所需要的数据。

每个保留站有以下7个字段:
O p O_p Op:要对源操作数进行的操作
Q j , Q k Q_j, Q_k Qj,Qk:将产生源操作数的保留站号,等于0表示操作数已经就绪且在 V j V_j Vj V k V_k Vk中,或者不需要操作数
V j , V k V_j, V_k Vj,Vk:源操作数的值,对于每一个操作数来说, V V V Q Q Q字段只有一个有效。
B u s y Busy Busy:为“ y e s yes yes”表示本保留站或缓冲单元“忙”
A A A:仅 l o a d load load s t o r e store store 缓冲器有该字段。开始是存放指令中的立即数字段,地址计算后存放有效地址。
Q i Q_i Qi:寄存器状态表,每个寄存器在该表中有对应的一项,用于存放将把结果写入该寄存器的保留站的站号。为0表示当前没有正在执行的指令要写入该寄存器,也即该寄存器中的内容就绪。

具体算法

各符号的意义
r r r:分配给当前指令的保留站或者缓冲器单元编号;
r d rd rd:目的寄存器编号;
r s , r t rs,rt rs,rt:操作数寄存器编号;
i m m imm imm:符号扩展后的立即数;
R S RS RS:保留站;
r e s u l t result result:浮点部件或 l o a d load load缓冲器返回的结果;
Q i Q_i Qi:寄存器状态表;
R e g s [ ] Regs[] Regs[]:寄存器组;
O p Op Op:当前指令的操作码.在这里插入图片描述
对于 load 指令来说, r t rt rt 是保存所取数据的寄存器号,对于 store 指令来说, r t rt rt 是保存所要存储的数据的寄存器号。与 r s rs rs 对应的保留站字段是 V j V_j Vj Q j Q_j Qj;与 r t rt rt 对应的保留站字段是 V k V_k Vk Q k Q_k Qk.
请注意: Q i Q_i Qi Q j Q_j Qj Q k Q_k Qk 的内容或者为0,或者是一个大于0的整数。 Q i Q_i Qi为0表示相应寄存器中的数据就绪, Q j Q_j Qj Q k Q_k Qk 为 0 表示保留站或缓冲器单元中的 V j V_j Vj V k V_k Vk 字段中的数据就绪。当它们为正整数时,表示相应的寄存器、保留站或缓冲器单元正在等待结果。这个正整数就是产生该结果的保留站或 load 缓冲器单元的编号。

  1. 指令流出
    (1)浮点运算指令在这里插入图片描述
    (2)load 和 store 指令在这里插入图片描述
  2. 执行
    (1)浮点运算指令在这里插入图片描述
    (2)load 和 store 指令在这里插入图片描述
  3. 写结果
    (1)浮点运算指令和 load 指令在这里插入图片描述
    (2)store 指令在这里插入图片描述

总结:Tomasulo 算法的三个阶段

  • Issue——发射:从浮点操作队列取指令
    保留站空闲(no structural hazard),
    控制发射指令 & 发送操作数(renames registers)-- WAR
  • Execute——执行
    操作数就绪,开始执行
    若没有就绪,检测 CDB,等待结果
  • Write result——执行完成
    写结果到 CDB
    置保留站空闲

正常总线:data + destination (“go to” 总线)
CDB: data + source (“come from” 总线)

示例在这里插入图片描述

假设 load 操作的延迟是 2 个时钟周期;浮点“+,-“的延迟是 2 个时钟周期;浮点”*“的延迟是 10 个时钟周期;浮点”/"的延迟是40个时钟周期。

指令相关图在这里插入图片描述
初始状态:在这里插入图片描述
cycle 1在这里插入图片描述
cycle 2在这里插入图片描述
cycle 3在这里插入图片描述
cycle 4在这里插入图片描述
cycle 5在这里插入图片描述
cycle 6在这里插入图片描述
cycle 7在这里插入图片描述
cycle 8在这里插入图片描述
cycle 10在这里插入图片描述
cycle 11在这里插入图片描述
cycle 15在这里插入图片描述
cycle 16
在这里插入图片描述
cycle 56在这里插入图片描述
cycle 57在这里插入图片描述

Loop 示例在这里插入图片描述

初始状态:在这里插入图片描述
cycle 1在这里插入图片描述
cycle 2在这里插入图片描述
cycle 3在这里插入图片描述
cycle 4在这里插入图片描述
cycle 5在这里插入图片描述
cycle 6在这里插入图片描述
cycle 7在这里插入图片描述
cycle 8在这里插入图片描述
cycle 9在这里插入图片描述
cycle 10在这里插入图片描述
cycle 11在这里插入图片描述
cycle 12在这里插入图片描述
cycle 13在这里插入图片描述
cycle 14在这里插入图片描述
cycle 15在这里插入图片描述
cycle 16在这里插入图片描述
cycle 17在这里插入图片描述
cycle 18在这里插入图片描述
cycle 19在这里插入图片描述
cycle 20在这里插入图片描述
为什么 Tomasulo 可以实现循环间并行?

  • 寄存器重命名
    动态循环展开 loop unrolling

  • 保留站
    循环浮点指令发射先于整数控制流
    避免 WAR 引起的停顿(如 scorebord)

  • Tomasulo 算法在运行中维护数据相关性

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐