每个浮点运算花费的时间相同吗?

3

我认为无论操作数多大,整数加减法所需的时间总是相同的。ALU输出稳定所需的时间可能因输入操作数而异,但利用ALU输出的CPU组件将等待足够长的时间,以便任何整数操作都可以在相同的周期内处理。(加、减、乘和除所需的周期是不同的,但我认为无论输入操作数是什么,加法所需的周期都是相同的。)

对于浮点运算也是这样吗?

我正在尝试实现一个包含大量浮点运算的程序。我想知道是否通过缩放处理数字可以提高程序的运行速度。


获取你所针对的CPU的手册并从中阅读时序。简短回答:不一定。 - Sami Kuhmonen
1
除了非规格化操作数,典型现代浮点单元的操作延迟/吞吐量不依赖于数据。可变延迟对于乱序调度是不方便的,它们通常是完全流水线化的。请参阅Agner Fog的insn表获取x86 CPU详细信息,以及x86标签wikiTL:DR:避免非规格化数字,你就没问题了。如果您不需要逐渐下溢,请在x86 MXCSR中设置Denormals Are Zero和Flush To Zero位,或者在其他体系结构中使用等效方法。 - Peter Cordes
1个回答

6
TL:DR: 避免非规格化数,问题就解决了。如果不需要逐渐下溢,请在x86 MXCSR中设置Denormals Are Zero和Flush To Zero位,或者在其他架构中设置等效的位。在大多数CPU中,产生非规格化结果会陷入微码,因此需要几百个周期而不是5个周期。

有关x86 CPU详细信息,请参见Agner Fog's insn tables,以及标签wiki。


这取决于你的CPU,但是典型的现代浮点数处理器在这方面都很相似。除了非规格化操作数之外,典型的现代浮点数处理器(包括x86、ARM和其他处理器)的加/减/乘操作的延迟/吞吐量不会依赖数据。它们通常是完全流水线化的,但具有多周期延迟(即如果输入就绪,每个周期可以开始执行新的MUL),这使得变化的延迟对于乱序调度来说不方便。可变延迟意味着两个输出将在同一周期内准备好,这将破坏完全流水线化的目的,并使调度程序无法可靠地避免冲突,就像处理已知但混合延迟的指令/uop时通常所做的那样。例如,高性能端的Intel Haswell。
  • mulpd(标量、128b或256b双精度向量):5个时钟周期的延迟,每1个时钟周期2次(两个独立的ALU)。
  • FMA:5个时钟周期的延迟,每1个时钟周期2次。
  • addpd/subpd:3个时钟周期的延迟,每1个时钟周期1次。(但是加法单元与其中一个乘/FMA单元在同一端口上)
  • divpd(标量或128b向量):10-20个时钟周期的延迟,每8-14个时钟周期1次。(也在其中一个乘/FMA单元的端口上)。对于256b向量速度较慢(除法ALU不是全宽度)。与加/减/乘不同,对于float而言速度略快。
  • sqrtpd:16个时钟周期的延迟,每8-14个时钟周期1次。同样不是全宽度,对于float而言速度更快。
  • rsqrtps(快速近似,仅适用于float):5个时钟周期的延迟,每1个时钟周期1次。

div/sqrt是例外:它们的吞吐量和延迟取决于数据。
即使在硬件上,也没有快速的并行算法可以用于div或sqrt。需要某种迭代计算,因此完全流水线化需要为每个流水线阶段复制大量非常相似的硬件。尽管如此,现代Intel x86 CPU具有部分流水线化的div和sqrt,其倒数吞吐量小于延迟。
与mul相比,div/sqrt的吞吐量要低得多(约为1/10或更差),延迟显着较高(约为2倍至4倍)。现代FPUs中div/sqrt单元不完全流水线化的特性意味着它可能具有可变延迟,而不会在ALU输出端口处引起太多冲突。
SSE/AVX没有将sin/cos/exp/log实现为单个指令;数学库应该编写自己的代码。
许多优秀的数学库在SSE出现之前也没有使用x87 fsin,它在所有现有实现中都是微码化的,因此内部实现使用相同的80位加/减/乘/除/平方根硬件,您可以使用简单的指令进行编程;没有专用的fsin硬件(或者至少不多;可能是一个查找表)。大多数其他三角/超越x87函数也是如此,比如fyl2x

如果有专门的fsin硬件,那将是很好的,因为对于非常接近Pi/2的输入,范围缩减到+/- Pi/2可能会从更高的精度中受益。 fsin使用与fldpi相同的80位Pi常量(具有64位尾数)。这是最接近确切值Pi的可表示long double,而且巧合的是下两个二进制数字为零,因此实际上精度高达66位。但它仍然会导致最坏情况下最大误差为1.37万亿个单位,留下不到4个正确的位。(Bruce Dawson关于浮点数的一系列文章是优秀的,如果你即将编写一些浮点数代码,你绝对应该阅读他们。此处索引。

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

我不清楚x87的指数或对数指令,比如fyl2x。它们是微代码,所以在速度方面并没有什么特别之处,但可能对精度还可以。然而,现代数学库不会仅为了执行该指令而将值从xmm寄存器复制到x87中。x87指令的速度可能比您可以使用普通SSE数学指令做的要慢。(几乎肯定不会更快。)

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

rsqrtps使用牛顿-拉弗森迭代法略微不如普通sqrtps精确。在Intel Haswell/Skylake上,我记得延迟大约相同,但可能具有更好的吞吐量。如果没有NR迭代,它对大多数用途来说太不准确了。

总之,这已经变得非常特定于x86了。乘法与开方的相对性能严重依赖于CPU微架构,但即使在x86 vs ARM vs 大多数其他具有硬件FPU的现代CPU之间,您也应该发现muladd的性能不会受到数据的影响。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接