指令级并行
指令之间可以重叠执行
基本概念:
提高ILP常见基本方法: 循环级并行
数据相关:
相关会带来冒险,解决方法两种:
1.指令调度,保持相关,但避免发生冒险
2.通过代码变换消除相关
数据流经寄存器的时候,检测容易。
但是若数据流动经过存储器的时候,检测比较复杂。
因为实际逻辑地址与物理地址转换之间的关系,导致相同形式的地址有效地址未必相同,而形式不同的地址有效地址可能相同。
复习:
名称相关
名称为指令所访问的寄存器或者存储器单元的名称
如果两条指令使用相同的名称,但是它们之间并没有数据流动,则称这两条指令存在名称相关。
相关有两种:
1.反相关:
指令j写的名称 = 指令i读的名称
2.输出相关:
指令j写的名称 = 指令i写的名称
如果指令中名称改变了,不影响另一条指令的执行。
换名技术:
1.改变指令中操作数的名称来消除名相关。
2.对于寄存器操作数进行换名:寄存器换名
保证程序的正确执行:数据流与异常行为
BEQZ RNAME,ADDRESS 若R ==0 ,跳转到ADDRESS的偏移地址处
基本流水线调度与循环展开
1 |
|
转换为MIPS
假设R1的初值是数组元素的最高地址,8(R2)指向最后一个元素。F2包含标量值s
1 |
|
指令调度之后的MIPS
1 | Loop: LW F1,0(R1) |
循环展开技术
把循环体代码复制多次并且按顺序排列,然后相应调整循环的结束条件。
给编译器进行指令调度带来更大空间
总结:
指令级并行做法:
1.循环展开
2.寄存器重命名以消除冒险
3.指令调度
使用高级分支预测降低分支成本
1 |
|
MIPS
1 | ADDI R3,R1,#-2 |
相关预测
利用其他分支行为进行预测的分支预测器
竞赛预测器:
动态调度克服数据冒险
静态调度: 仅仅依靠编译器对代码进行静态调度,减少相关和冒险。
只是在编译而不是程序执行的阶段进行代码调度和优化,本质上是把相关指令拉开距离来减少可能产生的停顿。
动态调度:
在程序的执行过程中,依靠专门硬件对代码进行调度,减少数据相关导致的停顿。
动态调度思想:
把Decode阶段拆分为两个阶段
指令调度使得异常处理变得复杂:
不精确异常:
当执行指令i导致发生异常时,处理机的现场(状态)与严格按程序顺序执行指令i时的现场不同。
精确异常:
当执行指令i导致发生异常时,处理机的现场(状态)与严格按程序顺序执行指令i时的现场相同。
Tomasulo 算法
核心思想:记录和检测指令相关,操作数一旦就绪立即执行,把发生RAW冒险的可能性减少到最小。
通过寄存器换名来消除WAR和WAW冒险
反相关会导致WAR冲突
输出相关会导致WAW冲突
其实前两个F6和最后一个F6逻辑上是没有关联的
第一个F8和最后两个F8也是没有关联的
保留站
保留站保存一条已经流出并等待到本功能部件执行的指令。
包括:操作码,操作数和用于检测和解决冲突的信息。
公共数据总线CDB
所有功能部件的计算结果都送到CDB上,由它把这些结果直接送到各个需要该结果的地方。
在具有多个执行部件且采用多流出(即每个时钟周期流出多条指令)的流水线中,需要采用多条CDB
缓冲器
存放读写寄存器的数据与地址
寄存器换名是通过保留站和流出逻辑来共同完成的。
当指令流出的时候,操作数如果还没有计算出来,把指令中相应的寄存器号换名为将产生这个操作数的保留站的标识。
指令流出保留站之后,操作数寄存器号或者换为数据本身,或者换成保留站的标识,不再与寄存器有关系。
Tomasulo算法特点
冒险检测与指令执行控制是分布的。
每个功能部件的保留站中的信息决定了什么时候指令可以在该功能部件开始执行。
计算结果通过CDB直接从产生他的保留站传送到所有需要它的功能部件,不用经过寄存器。
Tomasulo算法
指令执行步骤:
发射: 从指令队列头部取指令
example:
当执行了第一条指令并且已经把结果写入的时候
算法两个优点:
冒险检测逻辑是分布的。
(通过保留站和CDB实现)
如果有多条指令已经获得了一个操作数,并同时在等待同一运算结果,那么这个结果一产生,就可以通过CDB同时播送给所有这些指令,使它们可以同时执行。
消除了WAW冒险和WAR冒险导致的停顿。
使用保留站进行寄存器换名,并且操作数一旦准备就绪就放到保留站。
以下是Tomasulate C++ 模拟
基于硬件推测:
1 | 对分支指令的结果进行猜测,并假设这个猜测 |
//看到PPT 101