处理器
ALU used for
- Load/Store: F = add
- Branch: F = subtract
- R-type: F depends on funct field
流水线
提高吞吐率,并未减少单一指令的执行时间(延迟latency)
吞吐率 TP:单位时间内流水线所完成的任务数量或输出结果的数量
$TP=n/T_k=n/[(n+k-1)*δt]$,n为任务数,$T_k$是处理完n个任务所用的时间
加速比 S:完成同样一批任务,顺序执行所用的时间与使用流水线所用的时间之比
$S=T_0/T_k$
效率 E:流水线上的设备利用率,即时空图上,N个任务占用的时空区与K个流水段占用的总时空区之比。由于流水线有建立时间和排空时间,各段并不总是满负荷工作的。
E=n个任务占用的时空区/k个流水段占用的时空区=n/(n-k+1)
在时空图上体现为执行所有指令需要的时间和实际用的时间,多余的时间由建立时间和排空时间造成
5个处理步骤:IF,ID,EX,MEM,WB
冒险
结构冒险
硬件占用:内存读写—IM,DM分开
数据冒险
拿错数据
- 前推/旁路
- 重新安排代码避免阻塞
C code for A = B + E; C = B + F
1 | lw $t1, 0($t0) |
重新安排代码:
1 | lw $t1, 0($t0) |
控制冒险
取错指令
- 阻塞
- 分支预测
静态预测
动态预测
当预测错误时,流水线控制必须确保被错误预测的分支后面的指令执行不会生效,并且在正确的分支地址处重新开始启动流水线。
流水线数据通路
流水线寄存器
IF/ID,ID/EX,EX/MEM,MEM/WB
最终的数据通路与控制
数据冒险:旁路与阻塞
冒险条件
前提:写操作(EX/MEM.RegWrite, MEM/WB.RegWrite有效),写目的寄存器不能是$zero
1a. EX/MEM.RegisterRd = ID/EX.RegisterRs≠ 0 ForwardA = 10
1b. EX/MEM.RegisterRd = ID/EX.RegisterRt≠ 0 ForwardB = 10
2a. MEM/WB.RegisterRd = ID/EX.RegisterRs≠ 0 ForwardA = 01
2b. MEM/WB.RegisterRd = ID/EX.RegisterRt≠ 0 ForwardB = 01
1—Fwd from EX/MEM pipeline reg
2—Fwd from MEM/WB pipeline reg
ForwardA=0:第一个ALU操作数来自寄存器,ForwardB同理
旁路单元
01选通MEM/WB;10选通EX/MEM
==为什么有两个Rt啊?还有下面加的红色语句我也没有理解目的所在==
更复杂的潜在数据冒险:比如一个寄存器对多个数字进行求和运算,一系列连续的指令将会读写到同一寄存器:
1 | add $1,$1,$2 |
第二条指令的EX/MEM数据才是最新的。因此,改变MEM冒险的控制策略:
MEM hazard
if (MEM/WB.RegWrite and (MEM/WB.RegisterRd ≠ 0)
and not (EX/MEM.RegWrite and (EX/MEM.RegisterRd ≠ 0)
and (EX/MEM.RegisterRd = ID/EX.RegisterRs))
and (MEM/WB.RegisterRd = ID/EX.RegisterRs))
ForwardA = 01
if (MEM/WB.RegWrite and (MEM/WB.RegisterRd ≠ 0)
and not (EX/MEM.RegWrite and (EX/MEM.RegisterRd ≠ 0)
and (EX/MEM.RegisterRd = ID/EX.RegisterRt))
and (MEM/WB.RegisterRd = ID/EX.RegisterRt))
ForwardB = 01
Load-Use冒险检测单元
工作在ID级,从而可以在装载指令与紧随其后需要它的结果的指令间插入阻塞
阻塞条件:ID/EX.MemRead and ((ID/EX.RegisterRt = IF/ID.RegisterRs) or (ID/EX.RegisterRt = IF/ID.RegisterRt))
插入空指令:把ID/EX流水线寄存器的EX、MEM、WB级的控制信号都置为0.仍向前传递,但控制信号为0不会进行Reg和MEM的写操作。(事实上只需将RegWrite和MenWrite置为0)
通路
冒险检测控制单元控制PC和IF/ID流水线寄存器的写入,以及在实际控制信号与全0中进行选择的多选器。若冒险条件为真,阻塞并清除所有控制字段。
控制冒险
增加寄存器比较电路,如果参与比较的两个寄存器,是两步之前的ALU结果,可以通过旁路解决冒险。如果参与比较的两个寄存器其中有一个,是前一步的ALU结果或是两步之前load的结果,需要一个阻塞。如果参与比较的两个寄存器其中有一个,是前一步的load的结果,需要两个阻塞。
动态分支预测
分支预测缓存Branch prediction buffer (aka branch history table):一小块按照分支指令的低位地址索引的存储器区,其中包括一位或多位数据用以说明最近是否发生过分支。
2位预测位
依然需要花费一个时钟周期的开销,去计算分支目标地址。使用延迟分支或分支目标地址缓存
异常
(另一种形式的控制冒险,机制相同,不同的是由异常重置控制信号)
异常程序计数器EPC:保存出错指令的地址+4
协处理器CP0
Jump to handler at 8000 00180
记录引起异常的原因的两种方法
- Cause寄存器:有一个字段记录引起异常的原因
- 向量中断,控制权转移到由异常原因决定的地址处
多重中断,硬件对异常进行排序从而使得最先发生异常的指令被中断。优先级最高的异常处理完后,继续处理后面的异常。
非精确中断/非精确异常:在流水线中,将每一个异常与导致该异常的指令对应起来难度很大,EPC中放入相近的值,由操作系统确定精确位置。精确异常是为了支持虚拟存储器。
指令级并行
增加流水线的指令级并行程度:
- 增加流水线的深度—-更多的重叠
- 复制计算机内部部件的数量—每个流水既可以启动多条指令—-多发射multiple issue
多发射
CPI可能小于1,可使用IPC
静态多发射(在编译时)
动态多发射(在执行时)
发射槽 issue slot
Speculation 推测:编译器或处理器推测指令结果以消除其他指令对该结果的依赖。
推测错误时需要回卷。 指令重排、缓存结果
静态多发射处理器
使用编译器封装指令并处理冒险,若无可同时发射的,用nop代替
发射包 issue packet:在一个时空周期内发射的多条指令的集合。可有编译器静态生成或处理器动态生成。对一个周期内能发射的多条指令有所限制。
超长指令字VLIW
例:Two-issue packets
One ALU/branch instruction
One load/store instruction
数据通路
数据冒险:
EX数据:同一个包中,can’t use ALU result in load/store
load-use:会增加延迟
调度
Loop: lw \$t0,0(\$s1) # \$t0=array element
addu \$t0, \$t0,\$s2 # add scalar in \$s2
sw \$t0,0(\$s1) # store result
addi \$s1,\$s1,–4 # decrement pointer
bne \$s1,\$zero, Loop # branch $s1!=0
ALU/branch | Load/store | cycle | |
---|---|---|---|
Loop: | nop | lw t0, 0($s1) | 1 |
addi s1, $s1,–4 | nop | 2 | |
addu t0, $t0, $s2 | nop | 3 | |
bne s1, $zero, Loop | sw \$t0, 4($s1) | 4 |
循环展开(循环体复制4份):
ALU/branch | Load/store | cycle | |
---|---|---|---|
Loop: | addi s1, $s1,–16 | lw \$t0, 0($s1) | 1 |
nop | lw t1, 12($s1) | 2 | |
addu t0, t0, $s2 | lw \$t2, 8($s1) | 3 | |
addu t1, t1, $s2 | lw \$t3, 4($s1) | 4 | |
addu t2, t2, $s2 | sw \$t0, 16($s1) | 5 | |
addu t3, t4, $s2 | sw \$t1, 12($s1) | 6 | |
nop | sw t2, 8($s1) | 7 | |
bne s1, $zero, Loop | sw \$t3, 4($s1) | 8 |
寄存器重命名——引入临时寄存器,消除虚假的数据依赖(反相关)
标红处是16而不是0,是因为第一行\$t0取的是\$s1未减去16处的值,从第二行开始,通过旁路技术,$s1已减去16动态多发射处理器
也叫超标量处理器
指令顺序发射,乱序执行,顺序提交。处理器决定每个周期几条指令,硬件保证正确性。
流水线被划分为:取指与发射单元、多个功能单元、一个提交单元(含重排序缓冲区)。每个功能单元有自己的缓冲区(保留站)用于保存操作数和操作。
寄存器重命名
保留站和重排序缓冲区提供寄存器重命名机制。发射指令时,它先被复制到合适的功能单元的保留站。如果操作数在寄存器堆中或重排序缓冲区中可用,那么立即数被复制到保留站中。否则该指令一直缓存在保留站中。若指令已发射,那么操作数对应的寄存器的值可以被覆盖。如果操作数不在寄存器堆中或重排序缓冲区中,那么他应该是某个功能单元的结果,硬件帮助定位该功能单元,计算出结果时直接从功能单元复制到保留站,跳过寄存器堆。
推测
Predict branch and continue issuing
Don’t commit until branch outcome determined
Load speculation
Avoid load and cache miss delay
- Predict the effective address
- Predict loaded value
- Load before completing outstanding stores
- Bypass stored values to load unit
Don’t commit load until speculation cleared
为什么需要动态调度?
- 并不是所有的阻塞都可以预知。动态调度在一些指令阻塞时调度其他指令,以掩盖阻塞。
- 对于分支指令,若采用动态推测而不是用动态调度,会极大限制指令级并行度。
- 流水线延迟和发射宽度根据处理器的具体实现的不同有很大差别。
多发射的阻碍:窗口期有限、内存访问延迟、带宽受限
动态调度和推测消耗能源
指令集、数据通路和控制的设计之间相互影响
预测和推测之间有什么区别?
分支预测由处理器完成,以试图确定在条件跳转之后执行将继续的位置,以便它可以从存储器读取下一条指令。
推测执行更进一步,并确定执行下一条指令的结果。如果分支预测是正确的,则使用结果,否则将其丢弃。即使代码中没有实际的条件分支,也可以应用推测执行。处理器可以从通常将连续执行的若干指令确定结果,但是可以例如通过算术溢出中断来停止执行。