为什么 x86_64 CPU 上的通用寄存器没有融合乘加法?

Why is there no fused multiply-add for general-purpose registers on x86_64 CPUs?

在 Intel 和 AMD x86_64 处理器上,SIMD 矢量化寄存器具有特定的融合乘加功能,但通用(标量、整数)寄存器 don't - 您基本上需要乘法,然后添加(除非您可以将内容放入 lea)。

这是为什么?我的意思是,它是否无用以至于不值得开销?

整数乘法很常见,但不是 最常见的整数乘法之一。但是对于浮点数,一直使用乘法和加法,FMA 为大量 ALU-bound FP 代码提供 主要 加速。

此外,浮点数实际上避免了 FMA 的精度损失(x*y 内部临时变量在添加之前根本没有四舍五入)。这就是 the ISO C99 / C++ fma() math library function 存在的原因,也是为什么在没有硬件 FMA 支持的情况下实施起来很慢的原因。

整数 FMA(或 multiply-accumulate,又名 MAC)与单独的乘法和加法相比没有任何精度优势。


一些非 x86 ISA 确实提供整数 FMA。它不是没用,但 Intel 和 AMD 都没有费心将它包括在内until AVX512-IFMA(而且这仍然仅适用于 SIMD,基本上暴露了 double-precision FMA/[=12 所需的 52 位尾数乘法器电路=] 供整数指令使用)。

非 x86 示例包括:

  • MIPS32, madd / maddu (unsigned) to multiply-accumulate into the hi / lo registers (常规乘法和除法指令用作目标的特殊寄存器)。

  • ARM smlal 和朋友(32x32=>64 位 MAC,或 16x16=>32 位),也可用于无符号整数。操作数是常规的 R0..R15 通用寄存器。


整数寄存器 FMA 在 x86 上很有用,但具有 3 个整数输入的 uops 很少见。 CMOV 和 ADC 有 3 个输入,但其中一个是标志。即便如此,在为 Haswell 中的 FP FMA 添加了 3 输入微指令支持之后,直到 Broadwell,他们才在英特尔上解码为单个微指令。

Haswell 及更高版本可以使用 3 个整数输入跟踪 fused-domain 微指令,不过 for (some) micro-fused instructions with indexed addressing modes。 Sandybridge/Ivybridge un-laminate 指令如 add eax, [rdx+rcx]。 (但 Nehalem 可以像 Haswell 一样保留它们 micro-fused;SnB 简化了 fused-domain uop 格式)。无论如何,那是融合域,不在调度程序中。只有 Broadwell/Skylake 可以在调度程序中跟踪 3 输入整数 uops,并且仅适用于 2 个整数 + 标志,而不是 3 个整数寄存器。

英特尔确实使用了 "unified" 调度器,其中 FP 和整数运算使用相同的调度器,并且它可以跟踪适当的 3 输入 FP FMA。如果存在技术障碍,那么 IDK。如果不是,IDK 为什么英特尔没有将整数 FMA 作为 BMI2 或其他东西的一部分,它添加了东西 like mulx(2 输入 2 输出 mul,主要是显式操作数,与传统 mul 使用 rdx:rax.)


SSE2/SSSE3 是否有向量寄存器的整数mul-add指令,但只有在加宽16x16后水平相加=> 32 位 (SSE2 pmaddwd) or (unsigned)8x(signed)8=>16-bit (SSSE3 pmaddubsw).

但这些只是 2 个输入指令,所以即使有乘法和加法,它也与 FMA 有很大不同。


脚注:问题标题原来说没有FMA "for scalars"。标量 FP FMA 具有相同的 FMA3 扩展,添加了这些的打包版本:VFMADD231SD 和朋友在标量 double-precision 上运行,并且相同风格的 vfmaddXXXss 可用于 XMM 寄存器中的标量浮点数。