FMA 和朴素 a*b+c 的区别?
Difference between FMA and naive a*b+c?
在 FMA(3) 的 BSD 库函数手册中,它说 "These functions compute x * y + z."
那么 FMA 和执行 x * y + z 的原始代码有什么区别?为什么FMA在大多数情况下有更好的表现?
a*b+c
产生的结果就好像计算是:
- 计算
a
和b
的无限精确乘积。
- 将该产品四舍五入为正在使用的 floating-point 格式。
- 计算该结果和
c
的无限精确总和。
- 将总和四舍五入为正在使用的 floating-point 格式。
fma(a, b, c)
产生的结果就好像计算是:
- 计算
a
和b
的无限精确乘积。
- 计算该乘积与
c
的无限精确总和。
- 将总和四舍五入为正在使用的 floating-point 格式。
因此它跳过了将中间产品四舍五入为floating-pint格式的步骤。
在带有 FMA 指令的处理器上,融合 multiply-add 可能更快,因为它是一条 floating-point 指令而不是两条,而且硬件工程师通常可以设计处理器以高效地执行此操作。在没有 FMA 指令的处理器上,融合 multiply-add 可能会更慢,因为软件必须使用额外的指令来维护获得所需结果所需的信息。
[我没有足够的业力来发表评论;添加另一个答案似乎是唯一的可能性。 ]
Eric 的回答很好地涵盖了所有内容,但需要注意:有时使用 fma(a, b, c)
代替 a*b+c
会导致难以诊断的问题。
考虑
x = sqrt(a*a - b*b);
如果换成
x = sqrt(fma(a, a, -b*b));
对于 a
和 b
的值,sqrt
函数的参数可能为负,即使 |a|>=|b|
也是如此。特别是,如果 |a|=|b|
并且无限精确乘积 a*a
小于 a*a
的舍入值,则会发生这种情况。这是因为计算 a*a
时的舍入误差由 fma(a, a, -a*a)
.
给出
在 FMA(3) 的 BSD 库函数手册中,它说 "These functions compute x * y + z."
那么 FMA 和执行 x * y + z 的原始代码有什么区别?为什么FMA在大多数情况下有更好的表现?
a*b+c
产生的结果就好像计算是:
- 计算
a
和b
的无限精确乘积。 - 将该产品四舍五入为正在使用的 floating-point 格式。
- 计算该结果和
c
的无限精确总和。 - 将总和四舍五入为正在使用的 floating-point 格式。
fma(a, b, c)
产生的结果就好像计算是:
- 计算
a
和b
的无限精确乘积。 - 计算该乘积与
c
的无限精确总和。 - 将总和四舍五入为正在使用的 floating-point 格式。
因此它跳过了将中间产品四舍五入为floating-pint格式的步骤。
在带有 FMA 指令的处理器上,融合 multiply-add 可能更快,因为它是一条 floating-point 指令而不是两条,而且硬件工程师通常可以设计处理器以高效地执行此操作。在没有 FMA 指令的处理器上,融合 multiply-add 可能会更慢,因为软件必须使用额外的指令来维护获得所需结果所需的信息。
[我没有足够的业力来发表评论;添加另一个答案似乎是唯一的可能性。 ]
Eric 的回答很好地涵盖了所有内容,但需要注意:有时使用 fma(a, b, c)
代替 a*b+c
会导致难以诊断的问题。
考虑
x = sqrt(a*a - b*b);
如果换成
x = sqrt(fma(a, a, -b*b));
对于 a
和 b
的值,sqrt
函数的参数可能为负,即使 |a|>=|b|
也是如此。特别是,如果 |a|=|b|
并且无限精确乘积 a*a
小于 a*a
的舍入值,则会发生这种情况。这是因为计算 a*a
时的舍入误差由 fma(a, a, -a*a)
.