最大化执行吞吐量的最小依赖链数是多少?

What is the minimal number of dependency chains to maximize the execution throughput?

给定由真实依赖关系链接并定期重复(即循环)的指令链,例如 (a->b->c)->(a->b->c)->...

假设它可以拆分成几个更短且独立的子依赖链以从乱序执行中获益:

乱序引擎将每条指令调度到相应的CPU单元,该单元具有延迟和倒数吞吐量。

最大化执行吞吐量的最佳子依赖链数是多少?

根据 Agner 手册 Optimizing subroutines in assembly language,第 12.15 节:"The optimal number of accumulators if the CPU has nothing else to do is the latency of the most critical instruction in the dependency chain divided by the reciprocal throughput for that instruction"。 "most critical instruction" 是什么意思?是否有任何其他技术文档可以解决此类问题?

这取决于它们有多长,以及每个周期可以单独 运行 多少微指令。

这也取决于硬件的宽度。例如

  • 具有两个 ALU 执行单元和每个时钟 3 个融合域 uop 吞吐量的 PIII 对比
  • Haswell 有四个 ALU 执行单元(其中只有三个可以处理向量),每个时钟 4 个融合域 uop 吞吐量。

我认为"most critical instruction"的意思是构成关键路径的大部分长度。如果循环携带的依赖链由具有不同延迟的多条指令组成,则它是某种平均值。 (比如几何平均数?)


一个很好的例子是 FP add(例如对数组求和):

在 Sandybridge 上,它具有每时钟一个吞吐量,但有 3c 延迟,因此一个独立的 addps 指令链 运行s 在每 3c 一个 uop,仅维持 1/3最大 FP 乘法吞吐量。 (并让其他两个执行端口完全未被占用。)

三个并行的 dep 链可以使 port1 充满 addps 指令。所以如果你使用三个累加器,你可以保持三个 add 处于飞行状态。如果您还在飞行中保持 5 FP 倍增,您也可以使端口 0 饱和。端口 5 上的循环开销可以 运行(希望不会从 p01 窃取循环)。 load uops 可以与 adds 微融合,因此它们不会占用融合域带宽。但是您可以使用单独的 movaps 指令执行一些负载,并且仍然不会使每时钟 4 个融合域 uop 吞吐量饱和,但前端的瓶颈可能会限制您减少吞吐量。


Haswell 对于 FP add 仍然只有一个每时钟吞吐量,但是对于 FP mul 和 FMA 每个时钟有两个吞吐量。

因此,如果您使用 FMA(乘数为 1.0)对数组求和,则需要 10 个向量累加器(10 个 dep 链)来保持 10 个 FMA 运行,使 p01 饱和。 p5 和 p6 未使用,但您也可以使用微型熔断负载使负载端口饱和。


Skylake 将 FMA 的延迟减少了 1 个周期,降至 4 个,并放弃了 FP 添加单元。 (因此 FP add 是在 FMA 单元中完成的,这使 [v]addps 的吞吐量翻了一番,但延迟增加了 1c)。

所以在 SKL 上你只需要 8 个向量累加器(8 个 dep 链)来使 p01 饱和。但是拥有更多的 dep 链并没有坏处,只要你不 运行 超出寄存器。因此,在 Haswell 上使用 10 个累加器的理想代码在 SKL 上应该仍然是理想的。不过,您可以通过使用 addps 而不是 fma213ps(或其他)和常数向量 1.0 来节省一些电量。


请参阅 Agner 的 throughput/latency/port 数字说明表,以及他的微架构 PDF 以了解更多详细信息。我没有检查端口号或延迟号,但我经常输入这个示例,所以我很确定它是正确的:P.

另请参阅 标签 wiki 中的其他链接。