循环调度有哪些类型?
循环是 ILP 处理器并行处理的重要来源。因此,控制结构的规律性可以加快计算速度。循环调度是为高度并行 ILP 处理器(包括 VLIW)开发的指令调度器的核心部分。
循环调度的类型
循环调度主要有以下两种类型:
循环展开
循环展开的基本概念是多次重复循环体,并丢弃不必要的迭代间代码,包括递减循环计数、验证循环结束以及迭代间的条件分支。
这将缩短执行时间。当在编译时已经确定多次迭代时,通常在“do”和“for”循环中出现,可以简单地执行循环展开。
循环展开以代码长度为代价节省了性能时间,其方法与代码内联或传统的宏展开非常相似。代码内联是标准的编译器优化方法之一,用于简短且偶尔使用的子程序。
如果需要,代码内联表示每次在“调用点”知道子程序时都添加整个子程序体,而不是独立保存子程序体并在需要时从主代码调用它。
当循环必须执行大量次数,或迭代次数在编译时未固定时,简单的展开不适用。在这种情况下,必须扩展简单的循环展开。通常的方法是展开循环一定次数,例如三次,并为生成的展开循环组设置一个循环。然后,递减、测试循环结束和条件分支回代码仅对每一组展开循环都是必要的。
软件流水线
软件流水线是指连续的循环迭代如同硬件流水线一样执行,如下表所示。让我们看看周期 c+4、c+5 和 c+6。这些周期显示了软件流水线的真正优势。关键点是,对于这些周期,充分利用了后续循环迭代之间的可用并行性。例如,在周期 c+4 中,并行操作如下:
它可以存储迭代 1 的结果(即 a(1)),自动递增索引。
它可以将由 r200 保持的循环计数递减 1。
它可以使用属于周期 4 的操作数执行浮点乘法,即 (2.0 * b(4));
它可以加载迭代 5 的操作数,即 b(5)。
在具有多个流水线执行单元的 ILP 处理器上给定循环的大多数并行执行
周期 | 迭代次数 | ||||||
---|---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 | |
C | 加载 | ||||||
c+1 | fmul(浮点乘法) | 加载 | |||||
c+2 | 递减 | fmul(浮点乘法) | 加载 | ||||
c+3 | nop(空操作) | 递减 | fmul(浮点乘法) | 加载 | |||
c+4 | 存储 | nop(空操作) | 递减 | fmul(浮点乘法) | 加载 | ||
c+5 | 存储 | nop(空操作) | 递减 | fmul(浮点乘法) | 加载 | ||
c+6 | 存储 | nop(空操作) | 递减 | fmul(浮点乘法) | 加载 | ||
c+7 | 存储 | nop(空操作) | 递减 | fmul(浮点乘法) | |||
c+8 | 存储 | nop(空操作) | 递减 | ||||
c+9 | 存储 | nop(空操作) | |||||
c+10 | 存储 |
周期 c+4 到 c+6 具有重复的调度设计。它可以通过等效循环来恢复。这个新循环的每次迭代都包含与初始循环的多次迭代相关的几个操作。
loop:storei;decri+2;fmuli+3;loadi+4;bc loop; //the loop has to be executed for i = 1 to 3