0

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

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

  • (a0->b0->c0)->(a0->b0->c0)->...
  • (a1->b1->c1)->(a1->b1->c1)->...

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

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

根据 Agner 的手册Optimizing subroutines in assembly language第 12.15 节:“如果 CPU 无事可做,则累加器的最佳数量是依赖链中最关键指令的延迟除以该指令的倒数吞吐量”。“最关键的指导”是什么意思?是否有任何其他技术文档可以解决此类问题?

4

1 回答 1

0

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

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

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

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


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

在 Sandybridge 上,它具有每时钟一个吞吐量,但延迟为 3c,因此单条相关addps指令链以每 3c 一个 uop 的速度运行,仅维持最大 FP 乘法吞吐量的 1/3。(并让其他两个执行端口完全无人占用。)

addps三个并行的 dep 链可以使 port1 的指令饱和。所以如果你使用三个累加器,你可以保持三个加法。如果你还在飞行中保持 5 个 FP 乘法,你也可以使 port0 饱和。循环开销可以在端口 5 上运行(希望不会从 p01 窃取循环)。负载微控制器可以与添加物进行微融合,因此它们不会占用融合域带宽。但是您可以使用单独的指令来执行一些负载,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 添加是在 FMA 单元中完成的,这使 的吞吐量增加了一倍[v]addps,但延迟增加了 1c)。

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


有关更多详细信息,请参阅 Agner 的吞吐量/延迟/端口号说明表和他的 microarch PDF。我没有检查端口号或延迟号,但我经常输入这个例子,我很确定它是正确的:P。

标签 wiki中的其他链接。

于 2016-07-21T07:48:45.177 回答