2013-08-28 5 views
9

Wie funktioniert eine Java-Laufzeitumgebung, die auf einen Intel-Prozessor ohne SSE2 abzielt, mit Gleitkomma-Denormalien, wenn strictfp gesetzt ist?Wie implementieren Java-Laufzeiten für Pre-SSE2-Prozessoren Gleitkomma-Grundoperationen?

Selbst wenn die 387 FPU für 53-Bit-Genauigkeit festgelegt sind, hält es einen überdimensionierten Exponenten Bereich, der:

  1. Kräfte Unterlauf/Überlauf an jedem Zwischenergebnis zu erfassen, und
  2. macht es schwierig, vermeiden Sie Doppelrundung von Denormalen.

Strategien umfassen erneut Berechnen der Operation, die mit emulierten Gleitkommazahlen in einem denormal Wert resultiert, oder ein permanent entlang der Linien von this technique Offset Exponent OCaml mit 63-Bit-floats auszurüsten, sich ein wenig von dem Exponenten borgen um Doppelrunden zu vermeiden.

In jedem Fall sehe ich keine Möglichkeit, mindestens eine bedingte Verzweigung für jede Gleitkommaberechnung zu vermeiden, es sei denn, die Operation kann statisch bestimmt werden, dass sie nicht unterläuft/überläuft. Wie Fälle behandelt werden, ist Teil meiner Frage, aber das kann nicht von der Frage der Darstellung getrennt werden (die permanente Exponenten Offset Strategie scheint zu bedeuten, dass zum Beispiel nur Überläufe überprüft werden müssen)).

+0

@ ChrisJester-Young Danke, dass du geholfen hast, die Frage klarer zu stellen. –

+0

Ich kenne die Antwort auf Ihre Frage nicht. Wenn Sie jedoch über einen solchen Computer verfügen, können Sie die Flags "-XX: + UnlockDiagnosticVMOptions -XX: + PrintAssembly" übergeben, um zu sehen, welcher Code generiert wird. – tmyklebu

+0

@tmyklebu Ich weiß nicht einmal, ob ich eine Java-Runtime installiert habe.Ich habe nur ein perverses Interesse an der Nachahmung der Doppelpräzision mit dem 387. Auch jemand hat mir einmal einen Verweis auf die Memoiren eines Doktoranden zu genau diesem Thema gegeben, die ich vergessen habe zu archivieren und nun nicht mehr finden kann. Du warst es nicht, oder? –

Antwort

9

Es sieht für mich aus einem sehr trivialen Testfall, wie die JVM Round-Trips jede double Berechnung durch Speicher, um die Rundung zu bekommen, die es will. Es scheint auch etwas seltsam mit ein paar magischen Konstanten zu tun. Hier ist, was sie für mich getan hat für eine einfache „Compute 2^n naiv“ Programm:

0xb1e444b0: fld1 
0xb1e444b2: jmp 0xb1e444dd   ;*iload 
             ; - fptest::[email protected] (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::[email protected] (line 6) 
0xb1e444d3: test %eax,0xb3f8d100 ; {poll} 
0xb1e444d9: fldl 0x10(%esp)   ;*goto 
             ; - fptest::[email protected] (line 6) 
0xb1e444dd: cmp %ecx,%esi 
0xb1e444df: jl  0xb1e444b8   ;*if_icmpge 
             ; - fptest::[email protected] (line 6) 

Ich glaube 0xb523a2c8 und 0xb523a2bc sind _fpu_subnormal_bias1 und _fpu_subnormal_bias2 vom Hotspot-Quellcode. _fpu_subnormal_bias1 sieht aus wie 0x03ff8000000000000000 und _fpu_subnormal_bias2 scheint 0x7bff8000000000000000 zu sein. _fpu_subnormal_bias1 hat die Wirkung der Skalierung der kleinsten Normalen double auf die kleinste Normale long double; Wenn die FPU auf 53 Bits aufrundet, wird das "Richtige" passieren.

Ich würde spekulieren, dass die scheinbar sinnlose Anweisung test da ist, so dass der Thread unterbrochen werden kann, indem diese Seite für den Fall, dass ein GC erforderlich ist, nicht lesbar ist.

Hier ist der Java-Code:

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()); 
} 
} 

weiter Graben, der Code für diese Operationen ist in plain sight im Code OpenJDK in hotspot/src/cpu/x86/vm/x86_63.ad. Relevante Schnipsel:

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_bias1\n\t" 
      "DMULp $dst,ST\n\t" 
      "FLD $src\n\t" 
      "DMULp $dst,ST\n\t" 
      "FLD StubRoutines::_fpu_subnormal_bias2\n\t" 
      "DMULp $dst,ST\n\t" %} 
    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_bias1\n\t" 
      "DMULp $dst,ST\n\t" 
      "FLD $src\n\t" 
      "FDIVp $dst,ST\n\t" 
      "FLD StubRoutines::_fpu_subnormal_bias2\n\t" 
      "DMULp $dst,ST\n\t" %} 
    opcode(0xDE, 0x7); /* DE F8+i or DE /7*/ 
    ins_encode(strictfp_bias1(dst), 
       Push_Reg_D(src), 
       OpcP, RegOpc(dst), 
       strictfp_bias2(dst)); 
    ins_pipe(fpu_reg_reg); 
%} 

Ich sehe nichts für Addition und Subtraktion, aber ich wette, sie nur ein Add/subtrahieren mit der FPU in 53-Bit-Modus und dann Round-Trip das Ergebnis durch die Erinnerung. Ich bin ein bisschen neugierig, ob es einen trickreichen Überlauf gibt, bei dem sie falsch liegen, aber ich bin nicht neugierig genug, um das herauszufinden.

+0

Wenn man den Code sieht, ohne ihn noch vollständig zu verstehen, schlägt er eine Art von Variation der "Exponenten-Offset" -Methode vor. Der Code muss etwas wie folgt tun: 1- Multiplizieren Sie eines der Argumente mit 2^-K1, so dass das Ergebnis der Multiplikation mit dem erweiterten Exponenten ein Denormalwert mit der gleichen Anzahl von effektiven Ziffern ist, wie das Ergebnis normalerweise hätte Standardexponent 2- tue das Produkt 3- multipliziere das Ergebnis mit 2^K2, so dass das Ergebnis entweder exakt oder + inf ist (genau wenn das Produkt mit dem Standardexponenten überläuft) 4- multipliziert mit 2^(K1-K2). Nun, wo ist die dritte Konstante? –

+0

Warten Sie, in Ihrer Methode 'calc' ist einer der Operanden der Multiplikation ziemlich offensichtlich konstant. Dieser kann so voreingestellt sein, dass nur zwei Multiplikationen übrig bleiben, um korrekte Ergebnisse für Unterläufe und Überläufe zu gewährleisten. –

+0

Ja, aber die JVM macht das nicht. Ich denke, was passiert ist: (1) die FPU ist im 53-Bit-Modus; (2) eine seltsame Konstante skaliert das kleinste "doppelte" subnormale bis zum kleinsten "langen doppelten" subnormal und das andere skaliert es zurück, wodurch Unterlauf gehandhabt wird; (3) Der Umlauf durch ein "Doppel" im Speicher sorgt für einen Exponentenüberlauf. Ich nehme an, Sie bekommen keine Doppelrundung für die Multiplikation. – tmyklebu

Verwandte Themen