Im folgenden Codefragment ist Foo1
eine Klasse, die jedes Mal, wenn die Methode bar()
aufgerufen wird, einen Zähler erhöht. Foo2
macht dasselbe, aber mit einer zusätzlichen indirekten Ebene.
Ich würde erwarten, dass Foo1
schneller ist als Foo2
, aber in der Praxis ist Foo2
konsistent um 40% schneller als Foo1
. Wie optimiert die JVM den Code so, dass Foo2
schneller läuft als Foo1
?
java -server CompositionTest
. java -client CompositionTest
führt zu den erwarteten Ergebnissen, dass Foo2
langsamer ist als Foo1
. Der Versuch, die Leistung moderner Sprachen vorherzusagen, ist nicht sehr produktiv.
Die JVM wird ständig modifiziert, um die Leistung gängiger, lesbarer Strukturen zu erhöhen, was im Gegensatz dazu ungewöhnlichen, umständlichen Code langsamer macht.
Schreiben Sie Ihren Code so klar wie möglich - wenn Sie dann wirklich einen Punkt identifizieren, an dem Ihr Code tatsächlich als zu langsam zum Übergeben von schriftlichen Spezifikationen identifiziert wird, müssen Sie möglicherweise einige Bereiche manuell anpassen - aber das wird wahrscheinlich beinhalten große, einfache Ideen wie Objekt-Caches, JVM-Optionen optimieren und wirklich dummen / falschen Code beseitigen (Falsche Datenstrukturen können RIESIG sein, ich habe einmal eine ArrayList in eine LinkedList geändert und eine Operation von 10 Minuten auf 5 Sekunden reduziert, Multi-Threading a Ping-Operation, die ein Klasse-B-Netzwerk entdeckte, nahm eine Operation von 8 Stunden zu Minuten).
Ihre Microbench-Markierung ist bedeutungslos. Auf meinem Computer läuft der Code in ungefähr 8ms für jede Schleife ... Um eine sinnvolle Zahl zu haben, sollte ein Benchmark wahrscheinlich für mindestens eine Sekunde laufen.
Wenn beide für etwa eine Sekunde ausgeführt werden (Hinweis, Sie brauchen mehr als Integer.MAX_VALUE
Wiederholungen) Ich finde, dass die Laufzeiten von beiden identisch sind.
Die wahrscheinlichste Erklärung dafür ist, dass der JIT-Compiler bemerkt hat, dass Ihre Indirektion sinnlos ist und sie (oder zumindest die Methodenaufrufe) so optimiert wurde, dass der in beiden Schleifen ausgeführte Code identisch ist.
Er kann das, weil er weiß, dass bar
in Foo2
effektiv final ist, und dass das Argument für den Foo2
-Konstruktor immer ein Foo1
ist (zumindest in unserem kleinen Test) . Auf diese Weise kennt es den genauen Code-Pfad, wenn Foo2.bar
aufgerufen wird. Es weiß auch, dass diese Schleife viele Male ausgeführt wird (in der Tat weiß sie genau, wie oft die Schleife ausgeführt wird) - so scheint es eine gute Idee zu sein, den Code einzubinden.
Ich habe keine Ahnung, ob das genau das ist, aber das sind alles logische Beobachtungen, die ich mit dem JIT über den Code machen konnte. Vielleicht könnten in Zukunft einige JIT-Compiler sogar die gesamte while-Schleife optimieren und einfach die Anzahl auf Wiederholungen setzen, aber das erscheint ziemlich unwahrscheinlich.
Tags und Links java optimization performance jit jvm