我认为无论操作数多大,整数加减法所需的时间总是相同的。ALU输出稳定所需的时间可能因输入操作数而异,但利用ALU输出的CPU组件将等待足够长的时间,以便任何整数操作都可以在相同的周期内处理。(加、减、乘和除所需的周期是不同的,但我认为无论输入操作数是什么,加法所需的周期都是相同的。)
对于浮点运算也是这样吗?
我正在尝试实现一个包含大量浮点运算的程序。我想知道是否通过缩放处理数字可以提高程序的运行速度。
我认为无论操作数多大,整数加减法所需的时间总是相同的。ALU输出稳定所需的时间可能因输入操作数而异,但利用ALU输出的CPU组件将等待足够长的时间,以便任何整数操作都可以在相同的周期内处理。(加、减、乘和除所需的周期是不同的,但我认为无论输入操作数是什么,加法所需的周期都是相同的。)
对于浮点运算也是这样吗?
我正在尝试实现一个包含大量浮点运算的程序。我想知道是否通过缩放处理数字可以提高程序的运行速度。
有关x86 CPU详细信息,请参见Agner Fog's insn tables,以及x86标签wiki。
mulpd
(标量、128b或256b双精度向量):5个时钟周期的延迟,每1个时钟周期2次(两个独立的ALU)。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次。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常量进行范围缩减,在最坏的情况下会导致较大的误差。这对于某些用例来说是一个有效的选择,这是软件的一个巨大优势:您可以为您的用例选择正确的软件实现。
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之间,您也应该发现mul
和add
的性能不会受到数据的影响。