加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

针对SSE2前处理器的Java运行时如何实现浮点基本操作?

发布时间:2020-12-15 07:37:48 所属栏目:Java 来源:网络整理
导读:当设置了strictfp时,(没有)针对没有SSE2的英特尔处理器的 Java运行时如何处理浮点非正规数? 即使将387 FPU设置为53位精度,它仍保持超大的指数范围: 强制检测每个中间结果的下溢/溢出,和 使得难以避免非正规的双重舍入. 策略包括重新计算导致模拟浮点的非正
当设置了strictfp时,(没有)针对没有SSE2的英特尔处理器的 Java运行时如何处理浮点非正规数?

即使将387 FPU设置为53位精度,它仍保持超大的指数范围:

>强制检测每个中间结果的下溢/溢出,和
>使得难以避免非正规的双重舍入.

策略包括重新计算导致模拟浮点的非正规值的操作,或沿this technique行的永久指数偏移,为OCaml配备63位浮点数,从指数借用一点以避免双重四舍五入.

在任何情况下,除非可以静态地确定操作不下溢/溢出,否则我认为没有办法避免每个浮点计算至少有一个条件分支.如何处理异常(溢出/下溢)情况是我的问题的一部分,但这不能与表示的问题分开(永久指数偏移策略似乎意味着只需要检查溢出).

解决方法

它从我看来,从一个非常简单的测试用例,就像JVM往返每次通过内存的双重计算来获得它想要的舍入.它似乎也用一些魔法常数做了一些奇怪的事情.这就是它为我做一个简单的“计算2天真”程序:

0xb1e444b0: fld1
0xb1e444b2: jmp    0xb1e444dd         ;*iload
                                      ; - fptest::calc@9 (line 6)
0xb1e444b7: nop
0xb1e444b8: fldt   0xb523a2c8         ;   {external_word}
0xb1e444be: fmulp  %st,%st(1)
0xb1e444c0: fmull  0xb1e44490         ;   {section_word}
0xb1e444c6: fldt   0xb523a2bc         ;   {external_word}
0xb1e444cc: fmulp  %st,%st(1)
0xb1e444ce: fstpl  0x10(%esp)
0xb1e444d2: inc    %esi               ; OopMap{off=51}
                                      ;*goto
                                      ; - fptest::calc@22 (line 6)
0xb1e444d3: test   %eax,0xb3f8d100    ;   {poll}
0xb1e444d9: fldl   0x10(%esp)         ;*goto
                                      ; - fptest::calc@22 (line 6)
0xb1e444dd: cmp    %ecx,%esi
0xb1e444df: jl     0xb1e444b8         ;*if_icmpge
                                      ; - fptest::calc@12 (line 6)

我相信来自热点源代码的0xb523a2c8和0xb523a2bc是_fpu_subnormal_bias1和_fpu_subnormal_bias2. _fpu_subnormal_bias1看起来是0x03ff8000000000000000而_fpu_subnormal_bias2看起来是0x7bff8000000000000000. _fpu_subnormal_bias1具有将最小正常double缩放到最小正常long double的效果;如果FPU舍入到53位,那么“正确的事情”就会发生.

我推测看似无意义的测试指令就在那里,以便在需要GC的情况下标记该页面是不可读的,从而可以中断线程.

这是Java代码:

import java.io.*;
public strictfp class fptest {
 public static double calc(int k) {
  double a = 2.0;
  double b = 1.0;
  for (int i = 0; i < k; i++) {
   b *= a;
  }
  return b;
 }
 public static double intest() {
  double d = 0;
  for (int i = 0; i < 4100; i++) d += calc(i);
  return d;
 }
 public static void main(String[] args) throws Exception {
  for (int i = 0; i < 100; i++)
   System.out.println(intest());
 }
}

进一步深入研究,这些操作的代码在hotspot / src / cpu / x86 / vm / x86_63.ad中的OpenJDK代码中很明显.相关摘要:

instruct strictfp_mulD_reg(regDPR1 dst,regnotDPR1 src) %{
  predicate( UseSSE<=1 && Compile::current()->has_method() && Compile::current()
->method()->is_strict() );
  match(Set dst (MulD dst src));
  ins_cost(1);   // Select this instruction for all strict FP double multiplies

  format %{ "FLD    StubRoutines::_fpu_subnormal_bias1nt"
            "DMULp  $dst,STnt"
            "FLD    $srcnt"
            "DMULp  $dst,STnt"
            "FLD    StubRoutines::_fpu_subnormal_bias2nt"
            "DMULp  $dst,STnt" %}
  opcode(0xDE,0x1); /* DE C8+i or DE /1*/
  ins_encode( strictfp_bias1(dst),Push_Reg_D(src),OpcP,RegOpc(dst),strictfp_bias2(dst) );
  ins_pipe( fpu_reg_reg );
%}

instruct strictfp_divD_reg(regDPR1 dst,regnotDPR1 src) %{
  predicate (UseSSE<=1);
  match(Set dst (DivD dst src));
  predicate( UseSSE<=1 && Compile::current()->has_method() && Compile::current()
->method()->is_strict() );
  ins_cost(01);

  format %{ "FLD    StubRoutines::_fpu_subnormal_bias1nt"
            "DMULp  $dst,STnt"
            "FLD    $srcnt"
            "FDIVp  $dst,0x7); /* DE F8+i or DE /7*/
  ins_encode( strictfp_bias1(dst),strictfp_bias2(dst) );
  ins_pipe( fpu_reg_reg );
%}

我没有看到加法和减法,但我敢打赌他们只是在53位模式下对FPU进行加/减,然后通过内存往返往返.我有点好奇是否有一个棘手的溢出案例他们出错了,但我并不好奇地发现.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读