编程语言
首页 > 编程语言> > Java strictfp修饰符对现代CPU有影响吗?

Java strictfp修饰符对现代CPU有影响吗?

作者:互联网

根据JLS,我知道strictfp修饰符对方法(和类)的含义:

JLS 8.4.3.5, strictfp methods:

The effect of the strictfp modifier is to make all float or double
expressions within the method body be explicitly FP-strict (§15.4).

JLS 15.4 FP-strict expressions:

Within an FP-strict expression, all intermediate values must be
elements of the float value set or the double value set, implying that
the results of all FP-strict expressions must be those predicted by
IEEE 754 arithmetic on operands represented using single and double
formats.

Within an expression that is not FP-strict, some leeway is granted for
an implementation to use an extended exponent range to represent
intermediate results; the net effect, roughly speaking, is that a
calculation might produce “the correct answer” in situations where
exclusive use of the float value set or double value set might result
in overflow or underflow.

我一直试图找到一种方法来获得strictfp方法中的表达式和非strictfp中的表达式之间的实际差异.我在两台笔记本电脑上试过这个,一台配备英特尔酷睿i3 CPU,另一台配备英特尔酷睿i7 CPU.我不能有任何区别.

很多帖子都建议原生浮点(不使用strictfp)可以使用80位浮点数,并且在最小可能的java双精度(最接近零)或高于最高可能的64位java双精度之下具有额外的可表示数字.

我使用和不使用strictfp修饰符尝试了下面的代码,它给出了完全相同的结果.

public static strictfp void withStrictFp() {
    double v = Double.MAX_VALUE;
    System.out.println(v * 1.0000001 / 1.0000001);
    v = Double.MIN_VALUE;
    System.out.println(v / 2 * 2);
}

实际上,我假设只有在代码编译成程序集时才会出现任何差异,因此我使用-Xcomp JVM参数运行它.但没有区别.

我发现another post解释了如何获取HotSpot(OpenJDK documentation)生成的汇编代码.我正在使用java -Xcomp -XX运行我的代码:UnlockDiagnosticVMOptions -XX:PrintAssembly.
带有strictfp修饰符的第一个表达式(v * 1.0000001 / 1.0000001),如果没有它,则编译为:

  0x000000010f10a0a9: movsd  -0xb1(%rip),%xmm0        # 0x000000010f10a000
                                                ;   {section_word}
  0x000000010f10a0b1: mulsd  -0xb1(%rip),%xmm0        # 0x000000010f10a008
                                                ;   {section_word}
  0x000000010f10a0b9: divsd  -0xb1(%rip),%xmm0        # 0x000000010f10a010
                                                ;   {section_word}

该代码中没有任何内容将每个步骤的结果截断为64位,就像我预期的那样. Looking up the documentation的movsd,mulsd和divsd,他们都提到这些(SSE)指令操作64位浮点值,而不是我预期的80位值.因此,这些指令操作的双值集已经是IEEE 754值集合似乎是合乎逻辑的,因此在使用strictfp和没有它之间没有区别.

我的问题是:

>这个分析是否正确?我不经常使用英特尔组装,所以我对我的结论没有信心.
>是否存在任何(其他)现代CPU架构(具有JVM),使用和不使用strictfp修饰符的操作之间存在差异?

解决方法:

如果用“modern”表示处理器支持你在编译器中产生的SSE2指令(mulsd,…),那么答案是否定的,strictfp没有区别,因为指令集没有允许利用缺乏strictfp的优势.可用的指令已经最佳,可以根据strictfp的精确规格进行计算.换句话说,在那种现代CPU上,你会以相同的价格获得strictfp语义.

如果“现代”是指历史387 FPU,那么如果中间计算在strictfp模式下溢出或下溢,则可能会观察到差异(不同之处在于它可能不会溢出或者在下溢时保持比精度更高的位)预期).

为387编译的典型strictfp计算看起来像this answer中的汇编,通过精心选择的2的幂来进行良好的乘法,使得下溢的行为与IEEE 754 binary64中的相同.通过64位存储器位置往返的结果然后处理溢出.

在没有strictfp的情况下编译的相同计算将在每个基本操作中产生一个387指令,例如,仅用于源级乘法的乘法指令fmulp. (387将被配置为在程序开头使用与binary64,53位相同的有效位宽度.)

标签:java,assembly,sse,expression-evaluation,strictfp
来源: https://codeday.me/bug/20190928/1829789.html