指令调度概念原理介绍
指令调度是编译优化中用于提高指令级并行,从而提高在计算机上指令流水线的性能。更直接的说,在没有改变原代码语义的情况下,它做了下面两件事:
1. 通过重排指令顺序避免指令流水线停顿;
2. 避免非法或语义模糊的操作(涉及典型的细微的指令流水线时序问题或非互锁的资源);
指令流水线停顿可能是由结构危险(处理器资源限制),数据危险(输出另一条指令所需的一条指令),控制危险(分支);
为了能产生快速的代码,代码生成阶段重排指令,以照顾目标机器在特定方面的性能约束。
不同操作指令的执行时间可能是不同的。内存访问操作可能需要花费几十甚至数百个CPU周期,但某些算术操作(除除法为例),需要几个CPU周期。这种延迟较长的操作对编译后代码性能的影响可能是惊人的。
以下面的例子来简单说明指令调度:假定目前loadAI或storeAI操作需要3个周期,mult操作需要2个周期,而所有其他操作都只需1个周期。下表给出了一个代码片段,并在开始列
给出了每个操作开始执行的周期,在结束列
给出了操作完成的时间。
开始列 | 结束列 | code |
---|---|---|
1 | 3 | loadAI Rarp, @a => R1 // 加载’a’ |
4 | 4 | add R1, R1 => R1 // R1 = a * 2 |
5 | 7 | loadAI Rarp, @b => R2 // 加载’b’ |
8 | 9 | mult R1, R2 => R1 // R1 = (a * 2) * b |
10 | 12 | loadAI Rarp, @c => R2 // 加载’c’ |
13 | 14 | mult R1, R2 => R1 // R1 = (a * 2 * b) * c |
15 | 17 | loadAI Rarp, @d => R2 // 加载’d’ |
18 | 19 | mult R1, R2 => R1 // R1 = (a * 2 * b * c) * d |
20 | 22 | storeAI R1 => Rarp, @a // 保存R1到’a’变量 |
9个操作的指令序列花费了22个周期执行。最小化寄存器的使用并未导致执行变快。
许多处理器都有一种特性,可以在长延迟操作执行期间发起新的操作。只要新操作完成之前不引用长延迟操作的结果,执行都可以正常地进行。
但如果某些插入的操作试图过早地读取长延迟操作的结果,处理器将延缓执行需要该值的操作,直至长延迟操作完成。在操作数就绪之前,操作是不能开始执行的,而操作结束之前,其结果也是无法读取的。
指令调度器重排代码中的各个操作。它试图最小化等待操作数所浪费的周期数。当然,调度器必须确保,新指令序列产生的结果与原来的指令序列是相同的。在很多情况下,调度器可以大幅度改进“朴素”代码的性能。对于我们的例子,好的调度器可能产生下列指令序列,如下表:
开始列 | 结束列 | code |
---|---|---|
1 | 3 | loadAI Rarp, @a => R1 // 加载’a’ |
2 | 4 | loadAI Rarp, @a => R2 // 加载’b’ |
3 | 5 | loadAI Rarp, @a => R3 // 加载’c’ |
4 | 4 | add R1, R1 => R1 // R1 = a * 2 |
5 | 6 | mult R1, R2 => R1 // R1 = (a * 2) * b |
6 | 8 | loadAI Rarp,@d => R2 // 加载’d’ |
7 | 8 | mult R1, R3 => R1 // R1 = (a * 2 * b) * c |
9 | 10 | mult R1, R2 => R1 // R1 = (a * 2 * b * c) * d |
11 | 13 | storeAI R1 => Rarp, @a // 保存R1到’a’变量 |
代码的这个版本仅需要13个周期执行。与最小数目相比,该代码使用的寄存器多一个。在这以指令序列中,除8,10,12周期之外,每个周期都开始一个操作。其他等价的调度也是有可能的,但与之等长的调度一般需要使用更多的寄存器。
结束语
正如上面看到的,变异种大多数正真的困难出现在代码生成阶段。而且这些问题相互影响,使得情况更为复杂。例如,指令调度移动load操作,使之远离依赖的load的算术操作。这样做可以增加需要这些值的时间段,但此期间内所需的寄存器的数目也会相应地增加。类似地,将特定的值赋值给特定的寄存器,可以在两个操作之间建立“伪”相关性,从而限制指令调度。(在第一个操作完成之前第二个操作不能调度执行,即使在公用寄存器中的值并无依赖性。重命名该值可以消除这种伪相关性,代价是使用更多寄存器。)
编译器是一个超级复杂的工程。好的编译器合并了来自形式语言理论,算法研究,人工智能,系统设计,计算机体系结构和程序设计语言理论的思想,并将其应用到程序转换的问题上。
征服编译器很困难,加油吧。。。
参考
《Engineering a Compiler》
https://en.wikipedia.org/wiki/Instruction_scheduling