5.2 软件方法的指令级并行——基本块内的指令级并行
基本块是指一段顺序执行的代码,除了入口处没有其他转入分支,除了出口处没有其他转出分支
考虑一下C语言代码:
for (i = 1; i <= 1000; i++) {
x[i] = x[i] + s;
}
其基本块对应的汇编程序为:
Loop: LD F0,0(R1)
ADDD F4,F0,F2
SD 0(R1),F4
DADDI R1,R1,#-8
BNEZ R1,Loop
遵循以下指令延迟规定:
那么可以直接分析基本块汇编程序的指令周期延迟(共9个周期):
1Loop: LD F0,0(R1)
2 <stall>
3 ADDD F4,F0,F2
4 <stall>
5 <stall>
6 SD 0(R1),F4
7 DADDI R1,R1,#-8
8 <stall>
9 BNEZ R1,Loop
5.2.1 静态调度
静态调度是指通过改变指令顺序而不改变指令间数据相关来改善指令延迟,把上述R1的递减改到前面并利用延迟槽技术(设置延迟槽为1)可以让上述基本快代码压缩到6个周期完成:
1Loop: LD F0,0(R1)
2 DADDI R1,R1,#-8
3 ADDD F4,F0,F2
4 <stall>
5 BNEZ R1,Loop
6 SD 8(R1),F4
说明:
- DADDI让R1递减提前,那么SD中存储位置是R1+8而不是R1
- 延迟槽是无论分支是否成功都要执行的指令
5.2.2 循环展开
静态调度能够大幅提升基本快执行效率(50%),但是还有一个周期的停顿不能消除,那么由此引入另一种块内消除延迟方法——循环展开
循环展开是将循环中多个基本块展开成一个基本块从而填充stall间隙的方法
将上段基本块做4段展开,并做调度:
1Loop: LD F0,0(R1)
2 LD F6,-8(R1)
3 LD F10,-16(R1)
4 LD F14,-24(R1)
5 ADDD F4,F0,F2
6 ADDD F8,F6,F2
7 ADDD F12,F10,F2
8 ADDD F16,F14,F2
9 SD 0(R1),F4
10 SD -8(R1),F8
11 DADDI R1,R1,#-32
12 SD 16(R1),F12
13 BNEZ R1,Loop
14 SD 8(R1),F16
平均每个次循环仅需要14/4=3.5个cycle,性能大幅增加!