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

时间:2016-07-20 16:16:01

标签: performance micro-optimization throughput pipelining micro-architecture

给定由真实依赖性链接并且周期性地重复(即循环)的指令链,例如(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没有其他任何事可做,则累加器的最佳数量是依赖链中最关键指令的延迟除以该指令的倒数吞吐量”。 “最关键的指令”是什么意思?还有其他技术文档可以解决这类问题吗?

1 个答案:

答案 0 :(得分:0)

这取决于它们有多长,以及每个循环每个循环可以自行运行多少次。

它还取决于硬件的宽度。 e.g。

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

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

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

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

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

Haswell仍然只有一个时钟吞吐量用于FP添加,但每个时钟有两个用于FP mul和FMA。

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

Skylake将FMA的延迟时间缩短了1个周期,降至4个,并降低了FP添加单位。 (因此FP添加是在FMA单元中完成的,它使movaps的吞吐量翻了一倍,代价是延迟1c。)

因此,在SKL上,您只需要8个向量累加器(8个dep链)来使p01饱和。但是,只要你没有用完寄存器,拥有更多的dep链就不会受到伤害。因此,在Haswell上使用10个累加器的理想代码应该仍然是SKL的理想选择。你可以通过使用[v]addps而不是addps(或其他)使用1.0的常量向量来节省一些功率。

有关吞吐量/延迟/端口号的详细信息,请参阅Agner的说明表;有关详细信息,请参阅其微架PDF。我没有查看端口号码或延迟号码,但我经常输入这个例子,我很确定它是正确的:P。

另请参阅代码wiki中的其他链接。