静态调度 OOO 处理器
Statically scheduling OOO processors
LLVM MISched 指令调度程序使用处理器功能单元、管道和延迟的声明性 TableGen 描述。想象一下,试图从这些声明中确定与英特尔优化参考手册中的编码指南等效的内容。
从广义上讲,静态调度 OOO 处理器的 objectives/techniques 是什么?对于 OOO 处理器,它什么时候在 B 之前安排指令 A,什么时候在 B 之后安排 A?
超标量处理器一次可以执行多条指令。有序处理器只会考虑原始顺序中的指令。乱序 (OOO) 处理器可以乱序执行指令,然后按顺序提交结果。推测对于这个问题无关紧要,但我假设这些处理器是流水线的。想想 A53(按顺序)和 Haswell (OOO)。
OOO 处理器接下来要执行的指令是处理器在 运行 时间做出的调度决定。所以这通常被称为动态调度。中序处理器执行哪条指令是由编译器在编译程序时决定的。因此这通常称为静态调度。
但是,编译器也静态 target/schedule OOO 处理器。在有序和 OOO 情况下,编译器可以查看大量 window 指令;编译器必须处理寄存器压力;在这两种情况下,编译器都希望让功能单元保持忙碌。 OOO 处理器通常还可以重命名寄存器,从而减少寄存器压力。
鉴于 OOO 处理器动态调度指令,提前编译器应该做什么来帮助实现这一点?
你大体上是正确的,但编译时调度仍然可以略微提高执行速度。发生这种情况是因为编译器可以以更优化的方式重新排列指令以加快解码速度(x86 的旧变体只有在序列满足某些约束条件时才能并行解码多条指令)或将它们更紧密地打包在处理器的指令缓冲区中。引用 Robert Morgan 的 "Building an optimizing compiler":
The compiler should schedule the insns as if the processor were
not an out-of-order execution processor. The more effective this
schedule is, the larger the size of the effective insns buffer.
在实践中获胜通常很小(百分之几)。
这本身并不是一个调度决策,而是一种优化。基本上,查看 LLVM 的 addILPOpts() 为 OOO 超标量后端添加的通道可以很好地了解可能的情况。早期 if-conversion 正在生成将 运行 并行的代码并避免必须 运行 串行的代码。
LLVM 具有用于 OOO 超标量的 EarlyIfConverter pass。 PowerPC、X86、AMDGPU、SystemZ 和 AArch64 后端使用它。 EarlyIfConverter 并行计算两个表达式并插入一个 select 以选择一个:TII->insertSelect(...)
// Early if-conversion is for out-of-order CPUs that don't have a lot of
// predicable instructions. The goal is to eliminate conditional branches that
// may mispredict.
//
// Instructions from both sides of the branch are executed speculatively, and a
// cmov instruction selects the result.
这个pass是由后端在addILPOpts()中添加的。 使用 ILP 并行评估两个替代方案,而不是有条件地评估一个然后另一个。
LLVM MISched 指令调度程序使用处理器功能单元、管道和延迟的声明性 TableGen 描述。想象一下,试图从这些声明中确定与英特尔优化参考手册中的编码指南等效的内容。
从广义上讲,静态调度 OOO 处理器的 objectives/techniques 是什么?对于 OOO 处理器,它什么时候在 B 之前安排指令 A,什么时候在 B 之后安排 A?
超标量处理器一次可以执行多条指令。有序处理器只会考虑原始顺序中的指令。乱序 (OOO) 处理器可以乱序执行指令,然后按顺序提交结果。推测对于这个问题无关紧要,但我假设这些处理器是流水线的。想想 A53(按顺序)和 Haswell (OOO)。
OOO 处理器接下来要执行的指令是处理器在 运行 时间做出的调度决定。所以这通常被称为动态调度。中序处理器执行哪条指令是由编译器在编译程序时决定的。因此这通常称为静态调度。
但是,编译器也静态 target/schedule OOO 处理器。在有序和 OOO 情况下,编译器可以查看大量 window 指令;编译器必须处理寄存器压力;在这两种情况下,编译器都希望让功能单元保持忙碌。 OOO 处理器通常还可以重命名寄存器,从而减少寄存器压力。
鉴于 OOO 处理器动态调度指令,提前编译器应该做什么来帮助实现这一点?
你大体上是正确的,但编译时调度仍然可以略微提高执行速度。发生这种情况是因为编译器可以以更优化的方式重新排列指令以加快解码速度(x86 的旧变体只有在序列满足某些约束条件时才能并行解码多条指令)或将它们更紧密地打包在处理器的指令缓冲区中。引用 Robert Morgan 的 "Building an optimizing compiler":
The compiler should schedule the insns as if the processor were
not an out-of-order execution processor. The more effective this
schedule is, the larger the size of the effective insns buffer.
在实践中获胜通常很小(百分之几)。
这本身并不是一个调度决策,而是一种优化。基本上,查看 LLVM 的 addILPOpts() 为 OOO 超标量后端添加的通道可以很好地了解可能的情况。早期 if-conversion 正在生成将 运行 并行的代码并避免必须 运行 串行的代码。
LLVM 具有用于 OOO 超标量的 EarlyIfConverter pass。 PowerPC、X86、AMDGPU、SystemZ 和 AArch64 后端使用它。 EarlyIfConverter 并行计算两个表达式并插入一个 select 以选择一个:TII->insertSelect(...)
// Early if-conversion is for out-of-order CPUs that don't have a lot of
// predicable instructions. The goal is to eliminate conditional branches that
// may mispredict.
//
// Instructions from both sides of the branch are executed speculatively, and a
// cmov instruction selects the result.
这个pass是由后端在addILPOpts()中添加的。 使用 ILP 并行评估两个替代方案,而不是有条件地评估一个然后另一个。