Hat der Java-Modifikator strictfp irgendwelche Auswirkungen auf moderne CPUs?

9

Ich kenne die Bedeutung des Modifikators strictfp für Methoden (und für Klassen) gemäß der JLS:

JLS 8.4.3.5, strictfp-Methoden:

  

Der Effekt des Modifikators strictfp besteht darin, alles float oder double zu machen   Ausdrücke innerhalb des Methodenkörpers werden explizit FP-strict (§15.4).

JLS 15.4 FP-strikte Ausdrücke:

  

Innerhalb eines FP-strikten Ausdrucks müssen alle Zwischenwerte sein   Elemente der Float-Wert-Menge oder der doppelten Wert-Menge, was bedeutet, dass   Die Ergebnisse aller FP-strikten Ausdrücke müssen von denen vorhergesagt werden   IEEE 754-Arithmetik für Operanden, die mit Einzel- und Doppeloperanden dargestellt werden   Formate.

     

Innerhalb eines Ausdrucks, der nicht FP-strikt ist, wird ein gewisser Spielraum gewährt   eine Implementierung, um einen erweiterten Exponentenbereich zur Repräsentation zu verwenden   Zwischenergebnisse; der Nettoeffekt ist, grob gesagt, dass a   Berechnung könnte "die richtige Antwort" in Situationen erzeugen, in denen   es könnte die ausschließliche Verwendung des Gleitkomma-Wertes oder des doppelten Wertes resultieren   in Überlauf oder Unterlauf.

Ich habe versucht, einen Weg zu finden, um einen tatsächlichen Unterschied zwischen einem Ausdruck in einer strictfp -Methode und einem, der nicht strictfp ist, zu erhalten. Ich habe das auf zwei Laptops versucht, einen mit einer Intel Core i3 CPU und einen mit einer Intel Core i7 CPU. Und ich kann keinen Unterschied machen.

Viele Beiträge weisen darauf hin, dass der native Fließkommawert, der strictfp nicht verwendet, 80-Bit-Fließkommazahlen verwenden kann und dass zusätzliche darstellbare Zahlen unterhalb des kleinsten möglichen Java-Doppels (am nächsten bei Null) oder oberhalb des höchstmöglichen Wertes liegen 64-Bit-Java-Double.

Ich habe diesen Code unten mit und ohne strictfp -Modifikator ausprobiert und es gibt genau die gleichen Ergebnisse.

%Vor%

Tatsächlich nehme ich an, dass jeder Unterschied nur dann auftauchen würde, wenn der Code in Assembly kompiliert wird, sodass ich ihn mit dem -Xcomp JVM-Argument ausführe. Aber kein Unterschied.

Ich habe einen weiteren Beitrag gefunden, der erklärt, wie Sie die Assembly erhalten können von HotSpot generierter Code ( OpenJDK-Dokumentation ). Ich verwende meinen Code mit java -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly . Der erste Ausdruck ( v * 1.0000001 / 1.0000001 ) mit dem strictfp -Modifikator und auch derselbe ohne ihn wird nach:

kompiliert %Vor%

Es gibt nichts in diesem Code, das das Ergebnis jedes Schritts auf 64 Bits abschneidet, wie ich es erwartet hatte. Ansehen Dokumentation von movsd , mulsd und divsd , sie alle erwähnen, dass diese (SSE-) Anweisungen auf 64-Bit-Gleitkommawerten, nicht auf 80-Bit-Werten, wie erwartet, arbeiten. Daher erscheint es logisch, dass der doppelte Wert, auf den diese Anweisungen angewendet werden, bereits der IEEE 754-Wert ist, so dass es keinen Unterschied zwischen strictfp und dem nicht geben würde.

Meine Fragen sind:

  1. Ist diese Analyse korrekt? Ich benutze Intel-Assembly nicht oft, daher bin ich mir meiner Schlussfolgerung nicht sicher.
  2. Gibt es irgendeine (andere) moderne CPU-Architektur (die eine JVM hat), für die es einen Unterschied zwischen der Operation mit und ohne den strictfp Modifikator gibt?
Erwin Bolwidt 21.03.2014, 15:10
quelle

1 Antwort

6

Wenn Sie mit "modern" Prozessoren meinen, die die Art von SSE2-Anweisungen unterstützen, die Sie in Ihrer Frage als von Ihrem Compiler ( mulsd , ...) zitiert haben, dann ist die Antwort nein, strictfp macht keinen Unterschied , weil der Befehlssatz es nicht erlaubt, die Abwesenheit von strictfp zu nutzen. Die verfügbaren Anweisungen sind bereits optimal, um die genauen Spezifikationen von strictfp zu berechnen. Mit anderen Worten, auf dieser Art von moderner CPU erhalten Sie strictfp Semantik die ganze Zeit für den gleichen Preis.

Wenn Sie unter "modern" die historische 387 FPU verstehen, dann ist es möglich, einen Unterschied zu beobachten, wenn eine intermediäre Berechnung in strictfp mode über- oder unterlaufen würde (der Unterschied ist, dass sie nicht überläuft oder bei Unterlauf halte mehr Präzisionsbits als erwartet).

Eine typische strictfp Berechnung, die für den 387 kompiliert wurde, wird wie die Assembly in dieser Antwort aussehen, mit einer guten Platzierung Multiplikationen mit gut gewählten Zweierpotenzen zum Unterlaufen verhalten sich genauso wie in IEEE 754 binary64. Ein Round-Trip des Ergebnisses über einen 64-Bit-Speicherplatz sorgt dann für Überläufe.

Die gleiche Berechnung, die ohne strictfp kompiliert wurde, würde einen 387-Befehl pro Grundoperation erzeugen, beispielsweise nur den Multiplikationsbefehl fmulp für eine Multiplikation auf Quellenebene. (Der 387 wäre so konfiguriert worden, dass er die gleiche Signifikanzbreite wie binary64, 53 Bits, am Anfang des Programms verwendet.)

    
Pascal Cuoq 22.03.2014, 00:32
quelle