1

我相信无论操作数有多大,整数加法或减法总是花费相同的时间。稳定 ALU 输出所需的时间可能因输入操作数而异,但利用 ALU 输出的 CPU 组件将等待足够长的时间,以便任何整数运算都将在相同的周期内处理。(ADD、SUB、MUL 和 DIV 所需的周期会有所不同,但我认为无论输入操作数如何,ADD 都会采用相同的周期。)

浮点运算也是这样吗?

我正在尝试实现一个包含大量浮点运算的程序。我想知道缩放我正在处理的数字是否有助于快速运行时间。

4

1 回答 1

4

TL:DR:避免非正规数字,你很好。如果您不需要逐渐下溢,请在 x86 MXCSR 或其他架构的等效项中设置 Denormals Are Zero 和 Flush To Zero 位。在大多数 CPU 中,产生异常结果会导致微码陷入困境,因此需要数百个周期而不是 5 个周期。

有关 x86 CPU 的详细信息,请参阅Agner Fog 的 insn 表,以及标签 wiki。


这取决于您的 CPU,但典型的现代 FPU 在这方面都是相似的。

除了非正规操作数,add/sub/mul 操作的延迟/吞吐量不依赖于典型的现代 FPU(包括 x86、ARM 等)。它们通常是完全流水线的,但具有多周期延迟(即,如果输入准备好,新的 MUL 可以在每个周期开始执行),这使得可变延迟对于乱序调度不方便。

可变延迟意味着两个输出将在同一个周期内准备好,这违背了将其完全流水线化的目的,并且使调度程序无法像在处理已知但混合的延迟指令/微指令时那样可靠地避免冲突。(这些关于有序流水线的讲义显示了这对回写 (WB) 来说是一个结构性风险,但同样的想法也适用于 ALU 本身,它需要一个额外的缓冲区,直到它可以传递它准备好的所有结果。)

作为频谱高性能端的示例:英特尔 Haswell

  • mulpd(标量,128b 或 256b 双精度向量):5c 延迟,每 1c 吞吐量两个(两个单独的 ALU)。
  • FMA:5c 延迟,每 1c 吞吐量两个
  • addpd/ subpd: 3c 延迟,每 1c 吞吐量一个。(但添加单元与 mul/FMA 单元之一位于同一端口)
  • divpd(标量或 128b 向量):10-20c 延迟,每 8-14c 吞吐量一个。(也与 mul/FMA 单元之一在同一端口上)。256b 向量较慢(div ALU 不是全角的)。float与 add/sub/mul 不同,s 的速度要快一些。
  • sqrtpd:16c 延迟,每 8-14c 吞吐量一个。再次不是全宽,对于float.
  • rsqrtps(快速非常近似,仅适用于float):5c 延迟,每 1c 吞吐量一个。

div/sqrt 是个例外:它们的吞吐量和延迟取决于数据

没有针对 div 或 sqrt 的快速并行算法,即使在硬件中也是如此。需要某种迭代计算,因此完全流水线将需要为每个流水线阶段复制大量非常相似的硬件。尽管如此,现代 Intel x86 CPU 具有部分流水线化的 div 和 sqrt,其倒数吞吐量小于延迟。

与 mul 相比,div/sqrt 的吞吐量要低得多(约 1/10 或更差),延迟显着较高(约 2 倍到 4 倍)。现代 FPU 中 div/sqrt 单元的非完全流水线性质意味着它可以是可变延迟,而不会在 ALU 输出端口引起太多冲突。

SSE/AVX 没有将 sin/cos/exp/log 实现为单个指令;数学库应该自己编码。

甚至在 SSE 存在之前,许多优秀的数学库也没有使用x87 。fsin它在所有现有实现上都进行了微编码,因此内部实现使用相同的 80 位 add/sub/mul/div/sqrt 硬件,您可以使用简单的指令进行编程;没有专用fsin硬件(或者至少没有多少;也许是一个查找表)。对于大多数其他三角/超越 x87 函数(如fyl2x.

如果有一些专用fsin硬件会很好,因为范围减小到 +/- Pi/2 可以真正受益于非常接近 Pi/2 倍数的输入的更高精度。 fsin使用您从fldpi. 这是与 Pi 的精确值最接近的可表示形式long double,并且碰巧接下来的两位二进制数字为零,因此它实际上精确到 66 位。但它仍然会导致最坏情况下的最大误差为 1.37 quintillion 个单位,排在最后,只剩下不到 4 位正确。(Bruce Dawson 关于浮点的系列文章非常出色,如果您要编写一些浮点代码,绝对应该阅读它们。 索引在这个。)

英特尔无法在fsin不破坏与现有 CPU 的数值兼容性的情况下提高 x87 的范围缩减精度。当使用相同的输入运行相同的指令时,不同的 x86 CPU 在数值上相同的结果绝对有用。在软件中,您可以使用扩展精度浮点数自己进行范围缩小,例如所谓的双双精度以获得四精度(但仍然只有 的指数范围double)。double double 可以使用 SSE2 packed-double 指令相当有效地实现。SSE2 库的实现fsin可能会追求速度而不是精度,并与 x87 硬件进行相同的权衡;仅使用常规double用于减小范围的 Pi 常数,在最坏的情况下会导致较大的误差。对于某些用例,这将是一个有效的选择,这是软件的一大优势:您可以为您的用例选择正确的软件实现。

关于 x87 exp 或日志指令的 IDK,例如fyl2x. 它们是微编码的,所以它们在速度方面没有什么特别之处,但在准确性方面可能没问题。尽管如此,现代数学库不会仅针对该指令将值从 xmm 寄存器复制到 x87。x87 指令可能比普通 SSE 数学指令要慢。(而且几乎可以肯定不会更快。)


有关快速倒数和快速倒数 sqrt 的更多信息,请参阅为什么 SSE 标量 sqrt(x) 比 rsqrt(x) * x 慢?

具有 Newton-Raphson 迭代的rsqrtps的准确度略低于普通 sqrtps。在 Intel Haswell/Skylake 上,IIRC 的延迟大致相同,但可能具有更好的吞吐量。如果没有 NR 迭代,它对于大多数用途来说太不准确了。

无论如何,这已经变得非常特定于 x86。mul 与 sqrt 的相对性能在很大程度上取决于 CPU 微架构,但即使在 x86 与 ARM 与大多数其他具有硬件 FPU 的现代 CPU 之间,您应该会发现性能muladd数据无关。

于 2016-08-25T02:12:42.827 回答