Ich verwende den folgenden Java-Code auf einem Laptop mit 2,7 GHz Intel Core i7. Ich wollte es messen lassen, wie lange es dauert, eine Schleife mit 2 ^ 32 Iterationen zu beenden, was ungefähr 1,48 Sekunden (4 / 2,7 = 1,48) waren.
Tatsächlich dauert es jedoch nur 2 Millisekunden statt 1,48 s. Ich frage mich, ob dies auf eine darunter liegende JVM-Optimierung zurückzuführen ist.
public static void main(String[] args)
{
long start = System.nanoTime();
for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++){
}
long finish = System.nanoTime();
long d = (finish - start) / 1000000;
System.out.println("Used " + d);
}
javap -v
zu sehen.javac
führt nur sehr wenig tatsächliche Optimierung durch und überlässt das meiste dem JIT-Compiler.Antworten:
Hier gibt es eine von zwei Möglichkeiten:
Der Compiler hat erkannt, dass die Schleife redundant ist und nichts unternimmt, um sie zu optimieren.
Der JIT (Just-in-Time-Compiler) hat erkannt, dass die Schleife redundant ist und nichts tut, und hat sie daher optimiert.
Moderne Compiler sind sehr intelligent; Sie können sehen, wann Code nutzlos ist. Versuchen Sie, eine leere Schleife in GodBolt einzufügen, und sehen Sie sich die Ausgabe an. Aktivieren Sie dann die
-O2
Optimierungen. Sie werden sehen, dass die Ausgabe etwas in der Art von istIch möchte etwas klarstellen, in Java werden die meisten Optimierungen von der JIT vorgenommen. In einigen anderen Sprachen (wie C / C ++) werden die meisten Optimierungen vom ersten Compiler durchgeführt.
quelle
Es sieht so aus, als ob es vom JIT-Compiler optimiert wurde. Wenn ich es ausschalte (
-Djava.compiler=NONE
), läuft der Code viel langsamer:Ich habe den Code von OP in eingefügt
class MyClass
.quelle
Ich werde nur das Offensichtliche sagen - dass dies eine JVM-Optimierung ist, die stattfindet, die Schleife wird einfach überhaupt entfernt. Hier ist ein kleiner Test, der zeigt, welchen großen Unterschied
JIT
es macht, wenn es nur fürC1 Compiler
/ überhaupt deaktiviert / aktiviert ist .Haftungsausschluss: Schreiben Sie keine Tests wie diese - dies soll nur beweisen, dass die eigentliche "Entfernung" der Schleife in folgenden Bereichen erfolgt
C2 Compiler
:Die Ergebnisse zeigen, dass je nachdem, welcher Teil der
JIT
Methode aktiviert ist, die Methode schneller wird (so viel schneller, dass es so aussieht, als würde sie "nichts" tun - Schleifenentfernung, die in derC2 Compiler
- was die maximale Ebene ist , zu geschehen scheint ):quelle
Wie bereits erwähnt, kann der JIT -Compiler (Just-in-Time) eine leere Schleife optimieren, um unnötige Iterationen zu entfernen. Aber wie?
Tatsächlich gibt es zwei JIT-Compiler: C1 und C2 . Zunächst wird der Code mit dem C1 kompiliert. C1 sammelt die Statistiken und hilft der JVM zu erkennen, dass unsere leere Schleife in 100% der Fälle nichts ändert und nutzlos ist. In dieser Situation betritt C2 die Bühne. Wenn der Code sehr oft aufgerufen wird, kann er mithilfe gesammelter Statistiken optimiert und mit dem C2 kompiliert werden.
Als Beispiel werde ich das nächste Code-Snippet testen (mein JDK ist auf slowdebug build 9-internal eingestellt ):
Mit den folgenden Befehlszeilenoptionen:
Und es gibt verschiedene Versionen meiner Ausführungsmethode , die mit C1 und C2 entsprechend kompiliert wurden. Für mich sieht die letzte Variante (C2) ungefähr so aus:
Es ist ein bisschen chaotisch, aber wenn Sie genau hinschauen, werden Sie feststellen, dass es hier keine lange Schleife gibt. Es gibt 3 Blöcke: B1, B2 und B3 und die Ausführungsschritte können
B1 -> B2 -> B3
oder seinB1 -> B3
. WobeiFreq: 1
- normalisierte geschätzte Häufigkeit einer Blockausführung.quelle
Sie messen die Zeit, die benötigt wird, um zu erkennen, dass die Schleife nichts bewirkt, kompilieren den Code in einem Hintergrundthread und entfernen den Code.
Wenn Sie dies mit ausführen, können
-XX:+PrintCompilation
Sie sehen, dass der Code im Hintergrund auf Level 3 oder C1 Compiler und nach einigen Schleifen auf Level 4 von C4 kompiliert wurde.Wenn Sie die Schleife ändern, um eine zu verwenden, wird
long
sie nicht so optimiert.stattdessen bekommst du
quelle
long
Zähler die gleiche Optimierung verhindern?int
note char und short auf Bytecode-Ebene effektiv identisch sind.Sie berücksichtigen die Start- und Endzeit in Nanosekunden und dividieren durch 10 ^ 6, um die Latenz zu berechnen
es sollte sein,
10^9
weil1
Sekunde =10^9
Nanosekunde.quelle