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

What are the accuracy & performance benefits of using Math.fma?

我今天才注意到 Java 9 中存在 Math.fma(a, b, c),计算 a*b + c
(对于 double and float 个值)。

Returns the fused multiply add of the three arguments; that is, returns the exact product of the first two arguments summed with the third argument and then rounded once to the nearest float. The rounding is done using the round to nearest even rounding mode. In contrast, if a * b + c is evaluated as a regular floating-point expression, two rounding errors are involved, the first for the multiply operation, the second for the addition operation.

看起来 通过进行 1 次舍入而不是 2 次舍入提高了准确性。对吗?这取决于 CPU 能力,还是我们可以指望 总是

我猜它可能是使用特殊的 CPU 指令来实现的。是这样吗?如果是这样,我们是否也可以期待 性能优势 ?我有兴趣了解当前 platforms/CPUs 的实际收益,以及假设的未来收益。

编辑(试图让它不那么宽泛):我没有在意非常详细的答案:yes/no 到 correct/confirm 我理解的几个项目,再加上一些提示,就足以让我将答案标记为已接受。我对准确性和性能方面都很感兴趣, 我认为他们在一起...

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

JVM 使用 FMA CPU 指令(如果可用)。但是,FMA 并非随处可用。比如 Intel x86 CPUs before Haswell 就没有。这意味着大多数英特尔 CPU 目前没有 FMA。

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

我正在使用第 5 代 i7 mac。当我这样做时:

sysctl -n machdep.cpu.brand_string

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

sysctl -a | grep machdep.cpu | grep FMA

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

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

我正在尝试模拟:

 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指令使用了内在方法。