目录
一、概述
最近看一个课程High Performance Computer Architecture收获不少,总结如下,算是对前篇文章的补充。本文试图将体系结构的脉络理清,不再像前篇那样,着眼于概念和术语解释层面,然而书写过程中深感功力不够,只能后续慢慢补充。
二、性能指标和评估
- latecy
- throughput
延迟和吞吐反相关
speedup = latency反比 = throughout正比
三、pipeline
计算机体系结构的发展就是一个不断演进的提升性能的过程。流水线的引入提高了指令吞吐量,3.1展示了一个经典的五级流水线,该技术使得CPI达到了1——每个cycle执行一条指令。然而,这仅仅是理想情况,事实上,由于程序指令中普遍存在着相关性,流水线不可避免的会产生气泡,导致pipeline stall,3.2 给出了一个pipeline stall的例子。3.3则给出一个跳转指令由于跳转目标不确定导致的pipeline flush的情况。为了避免pipeline stall,可以使用一种称为forward的技术,这将在3.4给予阐述,然而我们将看到,由于硬件设计上的一些限制,forward也不是万能的。
在3.5 我们分析流水线不能全速执行的原因,通过介绍数据相关,控制相关这两类相关,说明相关会造成流水线冒险,在后面的章节,将看到CPU是通过何种技术,来避免或是减少这些冒险的。
3.1 一个典型的五级流水线
- 使用指标CPI(cycles per instruction)衡量指令吞吐,则在不使用流水线情况下,CPI=5
- 考虑引入流水线的情况,在cycle 1起始,instruction 1流入流水线,在cycle 5结束,instruction 1 流出流水线,当流水线建立起来(对于5-stage 需要5个cycle),每一个cycle都有一条指令流出(完成),则CPI=1, 因此流水线提高了指令吞吐。需要注意的是,流水线并没有提高指令执行的latency——仍然是5cycle
3.2 pipeline stall
关注两条指令:
I1: LD R1, (R2)
I2: ADD R3, R1, 1
cycle 1,I1指令在EXE stage,计算load有效地址,I2指令在ID stage,读取R1的值,注意到该R1的值由于依赖I1指令的结果,因此在这个阶段取到的值并不正确!不能流入下一个stage
所以在cycle 2,I2指令仍在ID阶段,I1指令流进MEM阶段,因此产生了一个流水线stall(称为pipeline bubble),对应图中STALL1, I1 在MEM阶段仅将值加载到流水线寄存器中,因此I2仍不能获取R1的最新值,不能流入下一个stage
所以在cycle3,l2指令依然处于ID阶段,I1指令流入WB stage,因此产生新的stall,对应图中STALL2,I1在cycle的前半个周期将R1值回写,这样在后半个周期I2 读到最新的R1,这时指令可以流入下一个stage了(如果WB是全周期的,那么会产生新的STALL3,因为I2在该cycle中仍然无法得到正确的R1)
这之后,流水线可以正常的运行了
可以看到流水线stall阻止了当前核后续指令的流动,使CPI增加。
3.3 pipeline flush
如果指令是一条跳转指令,由于在EXE阶段才能确定JUMP跳转的地址,因此在EXE stage,IF, ID的指令可能与跳转目标不一致,所以此时流水线flush掉EXE之前的所有指令(如上图两个删除线),这样,随着流水线的进行,产生了两个bubble。
3.4 forward
3.5 Dependencies
3.5.1 Control dependencies
When an instruction is dependent on the outcome of a branch decision, these instructions are said to have a control dependence.
- 20% of instructions are branches and jumps
- 50% of branch and jump instructions are taken
CPI = CPI of program + % of instructions mispredicted * penalty for misprediction
控制相关其实是说,如果在分支指令中,由于跳转的目标不定,可能会导致取值错误,这样会造成流水线flush,影响流水线性能,和跳转目的相关的指令称为控制相关。(这里只是为了简单的说明问题,更精确的定义参见量化研究方法)
3.5.2 Data dependencies
When an instruction needs data from another instruction that is called a data dependence.
Type of data dependencies:
1. RAW read after write. Also called Flow, True Dependence.
The following dependencies are False or Name dependencies.
2. WAW write after write. Also called Output Dependence.
3. WAR write after read. Also called AntiDependence
RAR read after read is not a dependence.
Dependencies and Hazards
Dependencies are caused by the program, not the pipeline.
Some dependences will not cause problems, but some, like RAW, can cause problems in pipelines. Hazards are true dependencies that result in incorrect execution of the program, but not all true dependencies will result in a hazard. Hazards are caused by both the program and the pipeline
数据相关分为真数据相关——RAW,和伪数据相关(又称名称相关)——WAW(又称输出相关),WAR(又称反相关),注意相关是程序的一种属性,和流水线无关。从名字我们可以看出,只有对于一部分真数据相关来说,才会产生流水线冒险(如果流水线不做任何处理,会导致执行结果有误)。
3.6 冒险
只有相关才会产生冒险,不同于相关这种只是程序的属性,冒险和程序,pipeline的实现这两者都有关系,一般的,冒险分为结构冒险,数据冒险,控制冒险,CPU通过不同的流水线设计手段来避免冒险,我将在后文展开。到目前为止,我们避免冒险(先保证正确再考虑性能),有三种手段了:pipeline stall, pipeline flush,forward,在现代CPU上有着更高级的技术。
四、branch prediction
我们已经知道可以通过流水线flush的方法避免控制冒险,但是这样降低了流水线的效率。我们知道分支指令跳转是不确定的,而跳转地址也不可能在IF的时候就能确定,那么如何解决这个问题呢?现代CPU解决的办法是:猜! 只要保证猜测的正确率足够高,猜测失败的惩罚在可接受范围内,那么就可以保证流水线的效率,这种技术称为branch prediction。
五、schedule
上节我们了解了流水线避免控制冒险的方法,这节主要分析避免数据冒险的方法,和上文一样可以通过stall的方法避免数据冒险,但是我们要寻求更高流水线效率,性能更高的方法。在这之前,先说明一下数据相关,我们知道,只有一部分真数据相关RAW可能产生数据冒险,其他的诸如WAR,WAW为何称为伪数据相关呢,这是由于可以通过设计pipeline完全消除这些相关,从而不会产生数据冒险,而RAW相关无法消除,可以看到,CPU设计者是如何处理这些相关性以提高性能的。
5.1 消除false dependencies
现代处理器使用一种称为register renaming的技术消除伪数据相关,我们知道程序员视图中的register称为程序寄存器(architectural register),数量一般比较少,通过引入physical register,在两种register进行映射,就可以避免false dependencies,pipeline中进行register renaming的部件称为RAT(register alias table),可以认为是一张表,保存了architectural register和physical register的对应关系:
如上图,architectural register R0~Rn,其和physical register对应关系在表中给出, R1<->P3, R3<->P8,注意,最左表的R0-Rn标识在硬件上是没有的——反正architectural register的数量是固定的,默认按顺序排列就好了,上图只是一个逻辑示意,试图说明RAT的作用。如果在每条指令执行时,都将一个architectural register映射一个physical register,这里给一个例子:
可以看出,伪数据相关消除了——rename后绿色箭头表示的指令相关没有了,真数据相关仍然存在,对上述指令,rename后IPC提高了,即提高了指令并行度。
5.2 Tomasulo算法
本节将介绍tomasulo算法是如何最大可能减少RAW的,同时也给出该算法是如何通过register renaming消除WAW和WAR的,tomasulo的算法示意如下:
- IQ: instruction queue
- RF: register file
- RS: reservation station
- CBD:
图中没有画出load/store部件,下面的步骤也先不涉及load/store指令
tomasolu算法三个过程,对应图中高亮部分:
- issue
- dispatch
- broadcast/write result (Wr)
5.2.1 ISSUE
指令的issue是顺序的
1) 从IQ中取指
2) Lookup RAT
如果RAT中对应的寄存器有值存在,使用RAT记录的值,
如果RAT中对应的寄存器没有值,使用register file中的寄存器值
3)获取空闲的RS entry
如果RS中空闲的entry,将指令放入RS中
如果RS中没有空闲entry,要等待RS空闲
4)标记(tag) 目的寄存器
将表示本条指令执行结果的寄存器存入RAT
给一个例子,是从视频中截取的,偷懒不画图了,:)
下面给出执行步骤的说明:
第一条指令是F2=F4+F1,此时RAT对应的F1,F4为空,从RF中取值F4=0.71,F1=3.14,且RS空闲,将指令放入RS后,与指令结果相关的寄存器放入RAT中,假如这里将第一条指令放在RS1中,那么最终RAT F2对应的entry填上RS1:
第二条指令F1=F2/F3,这里需要注意的是F2在RAT中对应了RS1,所以不能取RF中的值了,可以看出指令1和2是RAW,指令2需要等待指令有完成,此时RS仍有空闲,将指令放入,执行后的结果:
注意到第二条指令F1=F2/F3,第四条指令F1=F2+F3是WAW,那么有一种情况是,当issue 指令4时,指令2还没有Wr,即RS4仍然存在于RAT F1中,这时候直接将其覆盖。当指令2Wr时的动作在后面阐述。
5.2.2 dispatch
Dispatch needs to latch the results and determine which instructions are ready to execute.
dispatch指令流程是:
1)监控CBD总线,释放broadcast对应的RS
2) RS中的entry 匹配RS TAG
3)匹配成功将RS的结果插入到对应的RS entry中
4)当RS中所有操作数都准备好了,这时候可以dispatch到execution unit(adder/mulltiplier)
5) 执行单元执行完毕,将执行结果放在CBD上进行broadcast
看一个例子:
假设某个状态如上图所示,这时候dispatch锁存CBD的结果,决定去执行哪一条指令。我们看到,上图所示的cycle中锁存了RS1<->0.29这个执行结果,按照上面的步骤,先将RS1释放,这样可以后续的issue可以进来,另外,RS2,RS3,RS4中的操作数RS1都匹配了此时的结果中的RS tag(RS1),则将这三个RS entry分别替换,如下图所示:
这个时候,可以看到RS3,RS4两条指令都准备好了,由于两条指令对应不同的execution unit,那他们可以同时执行,执行完毕后,在总线上广播结果。由于总线是共享的,如果多条指令结果准备广播,该采取什么策略呢:
- Can choose the oldest instruction first
- The instruction with the most dependencies goes first (how many instructions are waiting for this result) this is difficult to implement in hardware
- Random selection
5.2.3 Broadcast
1)将tag和result放在总线上
2)结果写入RF
3) 更新RAT,如果RAT没有对应的entry说明结果已经写入RF了
4)释放对应的RS entry——修改invalid bit
还是看一个例子:
按照上面的步骤我们得到如下结果:
broadcast stale result
我们在issue小节给出的例子有一种情况没有说明,回忆一下,指令2和指令4是WAW的,因此其在RAT entry是同一个,存在这样一种情况,当指令4issue时,指令2还没有dispatch,指令4将指令2的RAT entry覆盖了(可以去翻翻上面的图),当指令2 broadcast时,需要写入RF,但此时由于在RAT中没有entry和其对应了,它不知道写入哪一个RF,这种情况称为“stale”
这种情况该怎么办呢?这种情况下,结果不对RF和RAT进行任何更新操作,而是对所有的RS entry,检测时候匹配对应的RS Tag,如果匹配,就将对应的tag修改成result。
5.2.4 其他
遗留了几科没看: