目录
3.1 Unified Reservation Station
一、概述
体系结构之二的延续,因为二文章太长了,其他的部分在这里描述
二、ROB
在tomasulo的乱序执行过程中,我们考虑两种情况:
- Exceptions in OutofOrder Execution Tomasulo算法并没有处理异常情况,这样位于异常(如除零)之后的指令如果先执行会导致错误的结果。
- Branch Mispredictions in OOO Execution Tomasulo算法并没有处理Branch Mispredictions情况,这样如果分支预测出错,会导致和上述一样的错误
可以通过ReOrder Buffer解决这个问题:
- Execute OOO
- Broadcast OOO
- Deposit values in registers InOrder. This can be achieved with a ReOrder Buffer
ROB可以记录程序的指令顺序并保存执行结果直到可以安全写入。
2.1 ROB结构
ROB按照program order记录指令,ROB entry由三个部分组成:
- reg 指令对应的register
- value 指令的结果
- done 指令的执行标记
ROB还有两个指针commit和issue,如果你了解过intel网络控制器,可以知道这两个指针和队列头尾指针类似(RDH,RDT),就是个环形队列,issue是头,指示下一条issue指令在ROB存放的位置,commit是尾,指示下一条需要提交的指令,这样commit->issue就表示issue但尚未提交,issue->commit表示ROB空闲的entry
2.2 Tomasulo with ROB
引入ROB后,tomasulo执行的流程有一些变化,为了对比,下面以列表的形式展现,同时给出示意图,以一条指令执行过程说明,执行前,如下图这样:
2.2.1 issue
Tomasulo | Tomasulo with ROB |
1. 从IQ中取指 | 从IQ中取指 |
2. Lookup RAT 如果RAT中对应的寄存器有值存在,使用RAT记录的值 如果RAT中对应的寄存器没有值,使用register file中的寄存器值 | Lookup RAT |
3. 获取空闲的RS entry 如果RS中空闲的entry,将指令放入RS中 如果RS中没有空闲entry,要等待RS空闲 | 获取空闲的RS entry 获取commit指向的ROB entry 如果资源busy等待 |
4. 标记(tag) 目的寄存器 在RAT中将destination register指向RS entry | 标记(tag) 目的寄存器
在RAT中将destination register指向ROB entry |
和之前相比,用rob entry来代替rs entry 存储结果:
2.2.2 dispatch
dispatch执行步骤与之前基本类似,区别在于这个阶段会free RS entry,这是因为RS记录register name的用途被ROB代替了,RS只负责在操作数准备好了dispatch到执行单元去执行,在这之后这个RS entry就没有用了,这样可以更快的为issue提供资源。
2.2.3 broadcast
Tomasulo | Tomasulo with ROB |
1)将 rs tag和result放在总线上 | 将 rob tag和result放在总线上 |
2)结果写入RF | 无,结果不写入RF,而写入rob entry |
3) 更新RAT,如果RAT没有对应的entry说明结果已经写入RF了 | 无,不更新RAT,因为RAT指向rob entry |
4)释放对应的RS entry——修改invalid bit | 无 |
简单来说,最后的写入工作由ROB负责了,所以broadcast功能减少了,马上就可以看到,增加了commit步骤写入。broadcast执行结果示意如下:
2.2.4 commit
commit的步骤是:
1)等待commit指向的指令完成,done置位
2)结果由ROB entry写入RF
3) 更新RAT
4) 释放对应的ROB entry
很明显,将原来broadcast的工作转移到commit来做
2.2.5 引入rob后对比
->
2.26
三、其他
3.1 Unified Reservation Station
- Unified reservation stations: combine the reservation stations for the Adder and the Multiplier. All of the reservation stations are in one large array, so the reservation stations can be used when necessary, they do not have to wait for a specific set of RS.
- The down side of the unified RS is the increased complexity in hardware. The hardware must be able to determine the correct arithmetic unit for each entry.
3.2 Terminology Confusion
在paper中经常使用术语: Issue, Dispatch, Commit,处理器设计人员经常有不同的称呼:
- issue:allocate(因为分配rs entry,rob entry),dispatch
- dispatch:execution,issue
- commit: complete,retire, graduate
四、load & store
目前为止,对不同的相关,我们的解决方法如下:
- Control dependencies Branch Prediction
- False dependencies Register Renaming
- Register dependencies Tomasulolike scheduling
tomasulo算法的load/store操作,都是in-order的,我们知道load/store也有类似RAW,WAW,WAR的数据相关,那么load/store必须按照program order执行吗,是否可以用类似我们解决Register dependencies 的方法让load/store也进行乱序执行从而提高流水线执行效率呢。
2.1 tomasulo中load/store的顺序执行
2.2 现代计算机上如何实现load/store的乱序执行
memory write在commit阶段发生,因为一旦memory写回就无法挽回了,假如因为预测错误导致store指令先执行,这是无法恢复的。所以将store操作delay到commit阶段。而load指令本身需要尽快读取,因为由于cache miss,load操作经常会耗费大量的时间,需要尽快获取。而load指令没有store指令那么多限制,即使读错了,再读一遍就好了。
另一方面,很多load结果操作依赖store操作,既然store操作在commit阶段才写入,那么又如何让load尽快读取到数据呢?cpu设计者引入了一个称为LSQ(load store queue)的结构,使load能尽快获取store的结果。
2.2.1 LSQ结构
- L/S load or store
- addr 地址
- value 地址对应的值
- C complete
LSQ和ROB类似之处在于,二者指令都是按照program order存储,在commit完成
2.2.2 LSQ如何工作
load和store指令按照顺序进入LSQ,当load指令进入LSQ时,使用load的地址和该指令之前的store指令的地址比较,如果匹配上了,就无需再从memory中取值了,称为StoretoLoad Forwarding。当然,一种可能的情况是之前相关的store指令还没有addr,那么这个时候load指令该怎么做呢?
- 等待store完成再读入——本质上还是顺序执行
- 等待前面所有的store完成后,匹配就使用forward,没有匹配就从memory中获取
- load speculation,不管前面有没有相关的store,直接去内存取值,如果前面store addr能确定后证明是错误的,recover。现代cpu通常使用这个方法,其性能最高
OOO Load/Store Execution
OOO Load/Store execution occurs when loads go to memory as soon as the address is available. Sometimes this results in stale data being used and then it needs to be recovered.
2.2.3 LSQ, ROB, RS比较
issue |
| 非load/store指令:
|
executing |
| load操作还要将结果进行广播 |
Commit |
| store操作还需要将结果写入内存 |
2.2.4一个问题
困惑已久,既然指令时顺序发射,乱序执行,顺序提交,对memory操作来说为什么还会有所谓memory barrier保证memory order呢?在wangqi的大作Cache Memory中:
现代处理器在 Commit 最后的执行结果时大多都采用 In-order 方式,这也保证了指令在
经过 Out-of-Oder 的流水线后,程序员看到的最终结果与程序应有的顺序一致。多数程序员
被这一假象迷惑,认为 CPU 的乱序执行仅与硬件流水线相关,并不会影响软件程序。
事实并非如此。 微架构为了实现乱序执行,有些指令,比如存储器读指令, 可能会提前
执行,而后因为种种原因,如分支预测失败, 可能会被迫重新执行。虽然乱序流水线可以保
证最后的结果与程序期待的结果一致,但是无法完全抹去这条本不该执行的指令在流水线中,
在存储器子系统中留下的执行痕迹
他在文中举的例子我也一并列举:
产生的根源是由于load speculation,
Time | C1 | C2 |
t0 | read A1 (得到旧值) | |
t1 | write A1 | |
t2 | increase A2 | |
t3 | check A2 |
假设t0时刻,C2 check A2指令 cache miss, read A1 进行load speculation, 此时得到的是A1的旧值
在t1时刻C1更新A1,并在t2更新A2
在t2时刻C2读取A2,发现其更新了,该条件下应该去执行read A1,误认为此次load speculation是成功的,但是A1的值是旧值!
所以在这种情况下要使用memory barrier