使用 Math.fma 的准确性和性能优势是什么?

2022-09-01 23:29:15

我今天才注意到Java 9中存在,它计算
(对于双精度值和浮点值)。Math.fma(a, b, c)a*b + c

返回三个参数的融合乘法加法;也就是说,返回与第三个参数相加的前两个参数的确切乘积,然后舍入一次到最接近的浮点数。舍入是使用舍入到最接近的偶数舍入模式完成的。相反,如果将 a * b + c 计算为正则浮点表达式,则涉及两个舍入错误,第一个用于乘法运算,第二个用于加法运算。

因此,看起来它通过执行1舍入而不是2来提高准确性。这是对的吗?这是以CPU能力为条件的,还是我们可以始终依靠它?

我猜它可能是使用特殊的CPU指令实现的。事实果真如此吗?如果是这样,我们能否期望性能也带来好处?我有兴趣阅读有关当前平台/ CPU的实际好处,以及假设的未来好处。

编辑(试图让它不那么宽泛):我不是在关注非常详细的答案:对几个项目是/否来纠正/确认我的理解,加上一些指针,足以让我将答案标记为已接受。我对准确性和性能方面都非常感兴趣,我认为它们相辅相成......


答案 1

是的,FMA提高了准确性,正是出于您所说的原因。

JVM 使用 FMA CPU 指令(如果可用)。但是,FMA并非在所有地方都可用。例如,在Haswell之前的Intel x86 CPU就没有它。这意味着大多数英特尔 CPU 目前没有 FMA。

如果CPU FMA不可用,Java使用一个非常慢的解决方案:它执行FMA使用(这是当前的解决方案 - 它可能会在未来改变,但我敢打赌,与CPU FMA相比,它总是很慢)。java.math.BigDecimal


答案 2

我在Mac上使用第5代i7。当我这样做时:

sysctl -n machdep.cpu.brand_string

我可以看到我的cpu是,并且cu支持,你可以通过以下方式看到:Intel(R) Core(TM) i7-5557U CPU @ 3.10GHzFMA

sysctl -a | grep machdep.cpu | grep FMA

结果,我得到了一行,其中存在此字符串。现在让我们看看JVM是否真的使用它。

这些方法(一个 for 和一个 for )被注释了,这意味着可以用实际的CPU本机指令替换它们 - 如果这样可用,但这意味着该方法必须足够热 - 调用多次,这是一个依赖于JVM的东西。doublefloat@HotSpotIntrinsicCandidateJIT

我试图用以下方式模拟它:

 public static void main(String[] args) {

    double result = 0;
    for (int i = 0; i < 50_000; ++i) {
        result = result + mine(i);
    }
    System.out.println(result);
}

private static float mine(int x) {
    return Math.fma(x, x, x);
}

我用以下命令运行它:

 java -XX:+UnlockDiagnosticVMOptions  
      -XX:+PrintInlining 
      -XX:+PrintIntrinsics 
      -XX:CICompilerCount=2 
      -XX:+PrintCompilation  
      org.so/FMATest

那里会有一堆行,但其中之一是:

 @ 6   java.lang.Math::fma (12 bytes)   (intrinsic)

这意味着JVM确实为FMA指令使用了一种内在方法。


推荐