第4章 指令系统结构
第三题 条件转移指令
3.假定在指令系统设计中需要考虑两种条件转移指令的设计方法,这两种方法如下。
(1)CPU A:先通过一条比较指令设置条件码A,再用一条分支指令检测条件码。
(2)CPU B:比较操作包含在分支指令中。
在两种CPU中,条件转移指令都需要两个时钟周期,所有其他指令都需要一个时钟周期。在CPU A中,全部指令的25%是条件转移指令,因为每次条件转移都需要一次比较,所以比较指令约占所有指令的25%。因为CPU A不需要在转移中包含分支,所以它的时钟频率是CPU B的1.2倍。请问哪一种CPU性能更高?如果CPU A的时钟频率只是CPU B的1.1倍,结果又是多少?
提示
题目问的是性能,可以默认问的是运行该程序(满足25%是条件转移)的性能。最终以实际运行的时间为标准;想计算时间,需要先计算总指令数量,才能算出所需时间。
题目分析:
两种 CPU 比较性能:
- CPU A:条件转移需要两条指令(比较 + 分支)。
- CPU B:条件转移通过一条指令完成。
条件:
- 条件转移指令占 25%,CPU A 的比较指令也占 25%。
- CPU A 的时钟频率是 CPU B 的 1.2 倍(第一问)或 1.1 倍(第二问)。
解答
-
计算总周期数
假设总指令数为 \(x\):
- CPU A :总周期数: \(2 \times 0.25x + 1 \times 0.25x + 1 \times 0.5x = 1.25x\)
- CPU B :总周期数: \(2 \times 0.25x + 1 \times 0.75x = 1x\)
-
计算性能比
性能比公式:
\[\text{性能比} = \frac{\text{CPU A 时钟频率}}{\text{CPU B 时钟频率}} \times \frac{\text{CPU B 周期数}}{\text{CPU A 周期数}} \]
-
第一问:\(f_A / f_B = 1.2\)
\[\text{性能比} = 1.2 \times \frac{1}{1.25} = 0.96 \]
第二问:\(f_A / f_B = 1.1\)
\[\text{性能比} = 1.1 \times \frac{1}{1.25} = 0.88 \]
-
答案
- 当 CPU A 时钟频率为 CPU B 的 1.2 倍 时,性能比为 96%。
- 当 CPU A 时钟频率为 CPU B 的 1.1 倍 时,性能比为 88%。
结论: CPU A 性能在两种情况下都低于 CPU B。
第5章 静态流水线
第二题 时空图 MIPS程序
2.对于下面的计算:
A=A+B
C=A-B
写出MIPS程序代码,并且画出5级静态流水线(无旁路)的流水线时空图。
题目分析
MIPS 代码实现计算:
A = A + B
C = A - B
假设:
- 流水线无旁路(No forwarding)。
- 数据依赖会导致流水线阻塞。
- 需要绘制 5 级静态流水线的时空图(IF, ID, EX, MEM, WB)。
MIPS 程序代码
按照操作顺序,MIPS 汇编代码如下:
asm
LW R1, 0(R0) # Load A into R1 (A = Memory[0(R0)])
LW R2, 4(R0) # Load B into R2 (B = Memory[4(R0)])
ADD R3, R1, R2 # R3 = A + B
SUB R4, R3, R2 # R4 = (A + B) - B = A
SW R3, 0(R0) # Store R3 (A + B) back to Memory[0(R0)]
SW R4, 8(R0) # Store R4 (A) to Memory[8(R0)]
静态流水线的特点
- 无旁路时的流水线阻塞:
- 在无旁路的情况下,如果后续指令依赖于前一指令的执行结果,必须等待前一指令完成写回阶段(WB)后才能继续执行。
- 流水线执行顺序:
- 每条指令需要经过 IF → ID → EX → MEM → WB\text{IF → ID → EX → MEM → WB} 五个阶段。
时空图分析
- LW R1, 0(R0):
- 完整流水线阶段:IF → ID → EX → MEM → WB。
- LW R2, 4(R0):
- 不存在数据依赖,可以连续进入流水线。
- ADD R3, R1, R2:
- 存在数据依赖,需要等待
LW R1
和LW R2
完成写回(WB),才能进入流水线。
- 存在数据依赖,需要等待
- SUB R4, R3, R2:
- 同样存在数据依赖,需要等待
ADD
完成写回(WB)。
- 同样存在数据依赖,需要等待
- SW R3, 0(R0):
- 无数据依赖,可以直接进入流水线。
- SW R4, 8(R0):
- 依赖于
SUB
的结果,需要等待写回完成。
- 依赖于
时空图:
以下是流水线各指令的执行阶段:
答案总结
-
MIPS 汇编代码:
asmLW R1, 0(R0) LW R2, 4(R0) ADD R3, R1, R2 SUB R4, R3, R2 SW R3, 0(R0) SW R4, 8(R0)
-
时空图:
- LW 指令 无数据依赖,直接进入流水线。
- ADD/SUB 指令 等待前置指令写回后才能执行,产生流水线阻塞。
- SW 指令 需要等待操作数写回完成。
-
流水线阻塞原因:
- 无旁路机制导致后续指令必须等待前一指令的结果写回(WB)后才能继续。
通过时空图可见,执行该段代码共需 18 个时钟周期。
第6章 动态流水线
第五题 浮点流水线
5.以下这个循环是高斯消元法中的核心操作,称为DAXPY循环(双精度的a乘以X 再加上Y),以下代码实现了对长度为100的向量进行DAXPY操作:Y=a*X+Y。
verilog
bar:
LDC1 F2, 0(R1) ; 取数 X(i)
MUL.D F4, F2, F0 ; 乘法操作 a * X(i)
LDC1 F6, 0(R2) ; 取数 Y(i)
ADD.D F6, F4, F6 ; 加法操作 a * X(i) + Y(i)
SDC1 F6, 0(R2) ; 存数 Y(i)
DADDIU R1, R1, #8 ; X 的下标加 1
DADDIU R2, R2, #8 ; Y 的下标加 1
DSGTUI R3, R1, #800 ; 测试循环是否结束
BEQZ R3, bar ; 如果循环没有结束,转移到 bar
NOP ; 空指令
在单发射静态流水线上,假定浮点流水线的延迟如附表6.1所示(延迟为N表示第T 拍操作数准备好开始运算,第 \(T+N-1\) 拍可以写回结果),分支指令在译码阶段(ID)计算结果,采用了分支延迟槽技术,整数操作在一拍之内发射和完成,并且结果是完全旁路的(fully bypassed)。
附表6.1 浮点流水线的延迟 | ||
---|---|---|
产生结果的指令 | 使用结果的指令 | 延迟(时钟周期) |
FP ALU op | Another FP ALU op | 4 |
FP ALU op | Store double | 3 |
Load double | FP ALU op | 2 |
Load double | Store double | 1 |
(1)把这个循环展开足够的次数,要求消除所有停顿周期和循环开销指令。循环将会被展开多少次?写出调度后的代码,每产生一个结果需要多少执行时间?
(2)写出DAXPY循环在软件流水后的代码,可以省略软件流水的装入代码和排空代码,每产生一个结果需要多少执行时间?
题目分析
该问题围绕 DAXPY 循环 \(Y = a \cdot X + Y\) 的优化,要求通过 循环展开 和 软件流水 方法优化指令执行,减少停顿周期和循环开销,提升流水线效率。
通过 循环展开两次,可以减少数据依赖导致的流水线阻塞问题。每次循环处理 2 个元素,并通过指令调度优化流水线的利用率。
解答:
(1)循环展开两次,每产生一个结果需要7拍执行时间
代码:
asm
bar:
L.D F2, 0(R1) ; Load X(i)
L.D F3, 8(R1) ; Load X(i+1)
L.D F6, 0(R2) ; Load Y(i)
MUL.D F4, F2, F0 ; a * X(i)
MUL.D F5, F3, F0 ; a * X(i+1)
L.D F7, 8(R2) ; Load Y(i+1)
DADDIU R1, R1, #16 ; X 下标加 2
ADD.D F6, F4, F6 ; a * X(i) + Y(i)
ADD.D F7, F5, F7 ; a * X(i+1) + Y(i+1)
DADDIU R2, R2, #16 ; Y 下标加 2
S.D F6, -16(R2) ; Store Y(i)
DSGTUI R3, R1, #800 ; 检测是否结束
BEQZ R3, bar ; 循环跳转
S.D F7, -8(R2) ; Store Y(i+1)
执行时间分析:
- 每次循环执行时间:14 个周期,处理 2 个元素。
- 每个元素的平均执行时间: \(\frac{14}{2} = 7 \, \text{拍/元素}\)
(2)循环展开两次,每产生一个结果需要9拍执行时间,9拍处理1个元素。
以下是按照软件流水优化后的代码与分析:
软件流水后的代码
asm
bar:
S.D -24(R2), F6 ; 第 i-3 次循环的存储操作
ADD.D F6, F4, F8 ; 第 i-2 次循环的加法操作
MUL.D F4, F2, F0 ; 第 i-1 次循环的乘法操作
L.D F2, 0(R1) ; 第 i 次循环的装入 X(i)
L.D F8, -8(R2) ; 第 i-1 次循环的装入 Y(i-1)
DADDIU R1, R1, #8 ; X 下标加 1
DSGTUI R3, R1, #800 ; 测试循环是否结束
BEQZ R3, bar ; 跳回 bar
DADDIU R2, R2, #8 ; Y 下标加 1
优化分析
- 软件流水特点:
- 将循环的计算部分分解为 存储 、加法 、乘法 和 装入,使不同循环的操作交错执行。
- 每个操作在不同周期进行,形成"流水线式"并行处理。
- 执行时间:
- 每次循环需 9 个周期完成。
- 每个结果的平均执行时间为 9 个周期/结果。
- 装入代码和排空代码:
- 省略装入和排空阶段的额外指令,直接开始处理循环数据。
结果总结
- 软件流水的代码:见上方优化后的代码。
- 每个结果的执行时间:9 个周期。
- 优化效果:充分利用流水线隐藏数据依赖,每次循环计算与数据装入和存储操作重叠,提高了效率。
第六题 精确例外处理动态流水线
6.假设有一个如附图6.1所示的支持精确例外处理的浮点流水线。流水线分成发射、执行并写回以及提交3个阶段,其中浮点加法部件延迟为2拍(即假设第T拍操作数准备好开始运算,第T十1拍可以写回结果),浮点乘法部件延迟为3拍,浮点操作队列中已有图中所示的指令,且寄存器的初值如图6.1所示,请给出6拍内每一拍的寄存器以及结果总线值的变化。
参考答案:
解析:
本题要求我们根据题目中的浮点流水线结构和指令状态,分析 6 拍内寄存器和结果总线的变化情况。以下是对每一拍的详细解析。
基础信息
- 浮点部件延迟:
- 加法操作部件延迟为 2 拍。
- 乘法操作部件延迟为 3 拍。
- 寄存器初值:
- 如图中所示,寄存器 F0, F1, F2, F3 分别为 1.0, 2.0, 3.0, 4.0。
- 指令队列:
- DIV.D F0, F1, F2:除法。
- MUL.D F3, F0, F2:乘法。
- ADD.D F0, F1, F2:加法。
- MUL.D F3, F0, F2:乘法。
- 操作阶段:
- 发射 (Issue)、执行 (Execute)、写回 (Writeback)、提交 (Commit)。
逐拍解析
Cycle 1:
- 寄存器状态:
- F0 = 1.0, F1 = 2.0, F2 = 3.0, F3 = 4.0。
- 结果总线 (Resbus):无写回。
- 操作:
- DIV.D F0, F1, F2:进入执行阶段,使用除法部件。
- 结果尚未写回。
Cycle 2:
- 寄存器状态:
- F0 = 1.0, F1 = 2.0, F2 = 3.0, F3 = 4.0。
- 结果总线:
- \(\text{DIV.D F0 = 1.0 / 3.0 = 0.67}\),准备写回。
- 操作:
- MUL.D F3, F0, F2:进入执行阶段,使用乘法部件。
- DIV.D 结果准备写回。
Cycle 3:
- 寄存器状态:
- F0 = 0.67, F1 = 2.0, F2 = 3.0, F3 = 4.0。
- 结果总线:
- \(\text{MUL.D F3 = 0.67 × 3.0 = 2.0}\),尚未写回。
- 操作:
- ADD.D F0, F1, F2:进入执行阶段,使用加法部件。
- MUL.D 结果等待写回。
Cycle 4:
- 寄存器状态:
- F0 = 0.67, F1 = 2.0, F2 = 3.0, F3 = 2.0。
- 结果总线:
- \(\text{ADD.D F0 = 2.0 + 3.0 = 5.0}\),尚未写回。
- 操作:
- MUL.D F3, F0, F2:进入执行阶段,使用乘法部件。
- ADD.D 结果等待写回。
Cycle 5:
- 寄存器状态:
- F0 = 5.0, F1 = 2.0, F2 = 3.0, F3 = 2.0。
- 结果总线:
- \(\text{MUL.D F3 = 5.0 × 3.0 = 15.0}\),准备写回。
- 操作:
- 继续完成 MUL.D 的写回。
Cycle 6:
- 寄存器状态:
- F0 = 5.0, F1 = 2.0, F2 = 3.0, F3 = 15.0。
- 结果总线:
- 无剩余操作,所有指令已完成写回。
总结
- 关键时间点:
- \(\text{DIV.D F0}\) 在 Cycle 2 写回。
- \(\text{MUL.D F3}\) 在 Cycle 5 写回。
- \(\text{ADD.D F0}\) 在 Cycle 4 写回。
- 最后一条 \(\text{MUL.D F3}\) 在 Cycle 6 写回。
通过软件流水线设计与寄存器结果表的跟踪,实现了支持精确例外的浮点操作。
第7章 多发射数据通路
第四题 流水线延迟元素执行时间
4.假设流水线延迟如附表7.1所示,分支延迟为1个周期,没有延迟槽。
附表7.1 流水线延迟 | ||
---|---|---|
产生结果的指令 | 使用结果的指令 | 延迟(时钟周期) |
FP ALU op | Another FP ALU op | 4 |
FP ALU op | Store double | 3 |
Load double | FP ALU op | 2 |
Load double | Store double | 1 |
下面循环计算\(Y[i]=a*X[i]+Y[i]\)高斯消元法中的关键一步。
assembly
L: LDC1 F4, 0(R2) ; 读 Y[i]
LDC1 F0, 0(R1) ; 读 X[i]
MUL.D F0, F0, F2 ; 求 a * X[i]
ADD.D F0, F0, F4 ; 求 a * X[i] + Y[i]
SDC1 F0, 0(R2) ; 保存 Y[i]
DADDIU R2, R2, -8 ; 更新 R2
DADDIU R1, R1, -8 ; 更新 R1
BNEZ R1, L ; 如果 R1 不为零,跳转到 L
NOP ; 空指令
(1)假设目标机器的流水线是单发射的,将次循环展开足够的次数,使得代码执行没有不必要的延迟,写出调度后的代码并计算一个元素的执行时间。
(2)假设目标机器的流水线是双发射的,将次循环展开足够的次数,使得代码执行没有不必要的延迟,写出调度后的代码并计算一个元素的执行时间。
(3)自己写一段与题中类似的C代码,用gcc的不同优化编译选项编译后,查看汇编代码,对不同优化选项进行比较,描述gcc做的优化。
参考答案:
4.解:
注意没有延迟槽,注意指令的延迟。(延迟的定义参见6章5题)
(1) 循环展开2次
assembly
L: L.D F0, 0(R1) ; load X[i]
L.D F6, -8(R1) ; load X[i-1]
MUL.D F0, F0, F2 ; a * X[i]
MUL.D F6, F6, F2 ; a * X[i-1]
L.D F4, 0(R2) ; load Y[i]
L.D F8, -8(R2) ; load Y[i-1]
ADD.D F0, F0, F4 ; a * X[i] + Y[i]
ADD.D F6, F6, F8 ; a * X[i-1] + Y[i-1]
DSUBUI R2, R2, 16 ; update R2
DSUBUI R1, R1, 16 ; update R1
S.D F0, 16(R2) ; store Y[i]
S.D F6, 8(R2) ; store Y[i-1]
BNEZ R1, L ; branch if R1 != 0
计算一个元素需14/2=7拍
正常展开即可.
(2)假设双发射流水线中有彼此独立的一条定点流水线和一个浮点流水线。
assembly
定点指令线:
L: L.D F0, 0(R1)
L.D F6, -8(R1)
L.D F10, -16(R1)
L.D F14, -24(R1)
L.D F4, 0(R2)
L.D F8, -8(R2)
L.D F12, -16(R2)
L.D F16, -24(R2)
DSUBUI R2, R2, 32
DSUBUI R1, R1, 32
S.D F0, 32(R2)
S.D F6, 24(R2)
S.D F10, 16(R2)
BNEZ R1, L
浮点指令线:
MUL.D F0, F0, F2
MUL.D F6, F6, F2
MUL.D F10, F10, F2
MUL.D F14, F14, F2
ADD.D F0, F0, F4
ADD.D F6, F6, F8
ADD.D F10, F10, F12
ADD.D F14, F14, F16
计算每个元素需要16/4=4拍
参考答案假设是直接按定点一条浮点一条来做。
(3) 实验题。
c
int main()
{
// initialize the X[i] and Y[i];
int i;
for (i = 100; i >= 0; i--)
Y[i] = a * X[i] + Y[i];
return 0;
}
gcc -O
不进行优化,-O1, -O2
进行部分优化,但是不进行循环展开优化;-O3
进行所有的优化,包括循环展开,对该程序非常有效。
解析:
(1) 单发射流水线
-
目标:消除流水线延迟,保证指令间不会因数据相关而停顿。
-
方法:循环展开 2 次,使加载、计算和存储的操作彼此交错,尽量减少流水线延迟。
-
代码解释
:
- 读取
X[i]
和X[i-1]
,并计算a*X[i]
和a*X[i-1]
。 - 读取
Y[i]
和Y[i-1]
,再进行加法。 - 更新指针
R1
和R2
,分别指向下一批元素。 - 存储计算结果。
- 循环检查
R1
是否为 0。
- 读取
-
执行时间 :循环展开 2 次后,每 2 个元素需要 14 个时钟周期,因此每个元素需要 7 个时钟周期。
(2) 双发射流水线
-
目标:利用双发射能力,同时执行一条定点指令和一条浮点指令,提高并行度。
-
方法:循环展开 4 次,并将定点操作和浮点操作分开调度。
-
代码解释
:
- 在定点流水线上,读取
X[i]
系列和Y[i]
系列的数据,并更新指针。 - 在浮点流水线上,进行乘法和加法运算。
- 数据存储与定点流水线和浮点流水线相辅相成,无阻塞。
- 在定点流水线上,读取
-
执行时间 :每 4 个元素需要 16 个时钟周期,因此每个元素需要 4 个时钟周期。
(3) 实验题 (C语言代码)
- 代码功能 :计算
Y[i] = a*X[i] + Y[i]
。 - 优化选项:
-O
:不优化,保留原始指令顺序。-O1, -O2
:进行部分优化,例如指令调度、常量折叠等,但不进行循环展开。-O3
:执行所有优化,包括循环展开、指令合并、寄存器分配优化等。
- 效果:
-O3
优化显著减少了循环的指令开销,通过循环展开、流水线优化等大幅提升了执行效率。
总结:
- 单发射流水线 :循环展开 2 次,达到无延迟执行,每个元素需要 7 个时钟周期。
- 双发射流水线 :循环展开 4 次,充分利用并行能力,每个元素需要 4 个时钟周期。
- C代码优化 :
-O3
编译优化尤其适合该程序,可显著提升执行效率,建议实际应用中使用高级优化选项。
第五题 物理寄存器个数需求
5. 一个 \(n\) 发射的处理器,流水线情况如下:取指、译码、重命名到物理寄存器后送入发射队列,发射队列乱序发射,功能部件乱序执行,乱序写回物理寄存器,最后顺序提交并释放物理寄存器。已知该处理器有 \(m\) 个逻辑寄存器,i 个功能部件\((i > n)\),每条指令从重命名到写回需要 \(t_1\) 拍,从重命名到提交交需要 \(t_2\) 拍。为了能让流水线满负荷工作,最少需要多少个物理寄存器?(提示:并不是每个参数都有用)
参考答案:
物理寄存器个数应至少为 \(n \times t_2 + m\) 才能让流水线满负荷工作。
每条指令在重命名的时候需要占用一个物理寄存器,每拍 \(n\) 条指令发射,则需要占用 \(n\) 个物理寄存器。被重命名的物理寄存器只有在提交的时候,并且不是当前逻辑寄存器的时候,才被释放。从重命名到提交共计有 \(t_2\) 拍,当流水线满负荷工作,流水线占用了 \(n \times t_2\) 个物理寄存器,另外逻辑寄存器数目为 \(m\) 个,因此共需要物理寄存器的个数为 \(n \times t_2 + m\)。
简要解析:
为了让流水线满负荷工作,物理寄存器数量需要满足两个条件:
- 发射到提交的寄存器占用 :
每拍发射 \(n\) 条指令,从重命名到提交共需 \(t_2\) 拍,因此流水线同时需要保留 \(n \times t_2\) 个物理寄存器用于指令占用。 - 逻辑寄存器需求 :
系统中存在 \(m\) 个逻辑寄存器,每个逻辑寄存器都需要一个物理寄存器映射。
因此,总的物理寄存器需求为:\(n \times t_2 + m\)
关键点:
- 重命名阶段:每条指令分配一个物理寄存器。
- 释放条件:物理寄存器只有在提交后,且不再被当前逻辑寄存器使用时,才会释放。
- 满负荷流水线 :确保所有 \(n\) 条指令从发射到提交的整个过程中,始终有足够的物理寄存器支持指令执行。