指令调度简介
指令调度是指对程序块或过程中的操作进行排序以有效利用处理器资源的任务^[1]^。指令调度的目的就是通过重排指令,提高指令级并行性,使得程序在拥有指令流水线的CPU上更高效的运行。指令调度优化的一个必要前提就是CPU硬件支持指令并行,否则,指令调度是毫无意义的。
根据指令调度发生的阶段,可以把其分为静态调度和动态调度^[2]^。
- 静态调度:发生在程序编译时期。静态调度由编译器完成,在生成可执行文件之前通过指令调度相关优化,完成指令重排。
- 动态调度:发生在程序运行时期。需要提供相应的硬件支持,比如乱序执行(OoOE: out-of-order execution),此时指令的发射顺序和执行顺序可能是不一致,但CPU会保证程序执行的正确性。
无论是静态调度还是动态调度,都是通过指令重排以提高指令流水,进而提高程序执行性能。静态调度和动态调度二者相辅相成,可以弥补对方的一些天然不足,协同完成指令流水优化,提高程序性能。本文主要介绍静态调度,如无特殊说明,后续指令调度均指静态指令调度。
现代计算机的指令并行方案
现代计算机的三种并行模式:流水线、超标量、多核。其中流水线和超标量与指令调度相关性更强,下面简单介绍一下这两种模式。
- 流水线
将指令执行过程分成多个阶段,每个阶段使用不同的硬件资源,从而使得多条指令的执行时间可以重叠。
经典五段式流水线:IF(取指)、ID(译码)、EX(执行)、MEM(访存)、WB(回写)。在五段式流水线中将一条指令的执行过程分成了5个阶段。-
使能流水线之前
-
使能流水线之后
在最优情况下,一个cycle中,由于指令执行的每个阶段使用不同的硬件资源,不存在竞争关系,从而可以使每个指令执行在不同的阶段。而由于数据依赖等原因的存在,流水线的并行程度一般很难达到最优,具体的并行程度需要依赖于指令调度的效果。
-
对于如下原始指令序列
ldr x1, [x2, x3]
add x1, x1, #1
ldr x5, [x2, x4]
sub x5, x5, #1
mul x6, x1, x5
在指令调度之前,耗时17个cycle:
在指令调度之后,耗时13个cycle:
ldr x1, [x2, x3]
ldr x5, [x2, x4]
add x1, x1, #1
sub x5, x5, #1
mul x6, x1, x5
-
超标量
具备超标量结构的CPU在一个内核上集成了多个译码器、ALU等单元。相比于具备普通流水线技术的CPU,具备超标量技术的CPU可以在同一个阶段执行多条处在相同阶段的指令。
超标量流水线:指令调度与寄存器分配的关系
讲到指令调度,不可避免的会想到寄存器分配,而指令调度和寄存器分配之间可以说具有相互约束、相互作用的关系。
指令调度通过重排指令顺序,降低指令间依赖,提高程序的并行度,相应的,改变指令的执行时机也会改变指令所使用的寄存器的生命周期;而寄存器分配又是挖掘程序的局部性,尽量缩短寄存器的生命周期,以能够让更多的数据直接存储在寄存器中。
寄存器分配同样也会影响指令调度,例如当对寄存器的需求超过寄存器数量时,会选择增加一些访存指令,这些指令也需要纳入到指令调度的考虑范畴之内。
所以说两者相互约束。可以知道,将指令调度问题和寄存器分配问题作为两个约束条件进行联合求解得到的解决方案是相对更优的,但由于无论是指令调度还是寄存器分配,都是很复杂的NP完全问题,综合考虑下,编译器一般会分别处理二者^[1]^。
在LLVM编译器的设计中,寄存器分配之前和寄存器分配之后都会执行指令调度。
- 寄存器分配之前执行指令调度:当前LLVM IR中分配的寄存器为虚拟寄存器,寄存器数量不受限制,此时指令调度受到的约束最小,可以更大程度上提高指令并行度。但是在寄存器分配阶段,使用物理寄存器替换虚拟寄存器,由于物理寄存器数量有限,寄存器压力增大,可能产生寄存器spill场景影响程序性能。
- 寄存器分配之后执行指令调度:寄存器分配阶段由于寄存器复用等情况会增加指令间依赖,破坏在寄存器分配之前做好的指令调度优化,所以在寄存器分配之后还要再次执行指令调度。
-
处理器
+关注
关注
68文章
19076浏览量
228689 -
cpu
+关注
关注
68文章
10794浏览量
210666 -
指令调度器
+关注
关注
0文章
4浏览量
1488
发布评论请先 登录
相关推荐
评论