Ich versuche, mit einer internen Methode so viel Leistung wie möglich zu erzielen.
Der Java-Code lautet:
List<DirectoryTaxonomyWriter> writers = Lists.newArrayList();
private final int taxos = 4;
[...]
@Override
public int getParent(final int globalOrdinal) throws IOException {
final int bin = globalOrdinal % this.taxos;
final int ordinalInBin = globalOrdinal / this.taxos;
return this.writers.get(bin).getParent(ordinalInBin) * this.taxos + bin; //global parent
}
In meinem Profiler habe ich gesehen, dass 1% der CPU-Ausgaben anfallen java.util.Objects.requireNonNull
, aber ich nenne das nicht einmal. Bei der Überprüfung des Bytecodes habe ich Folgendes gesehen:
public getParent(I)I throws java/io/IOException
L0
LINENUMBER 70 L0
ILOAD 1
ALOAD 0
INVOKESTATIC java/util/Objects.requireNonNull (Ljava/lang/Object;)Ljava/lang/Object;
POP
BIPUSH 8
IREM
ISTORE 2
Der Compiler generiert also diese (nutzlose?) Prüfung. Ich arbeite an null
Grundelementen, die sowieso nicht sein können. Warum generiert der Compiler diese Zeile? Ist es ein Fehler? Oder "normales" Verhalten?
(Ich könnte mit einer Bitmaske herumarbeiten, aber ich bin nur neugierig)
[AKTUALISIEREN]
Der Bediener scheint nichts damit zu tun zu haben (siehe Antwort unten)
Mit dem Eclipse-Compiler (Version 4.10) erhalte ich dieses vernünftigere Ergebnis:
public getParent (I) Ich löse java / io / IOException L0 LEINENUMMER 77 L0 ILOAD 1 ICONST_4 IREM ISTORE 2 L1 LEINENUMBER 78 L.
Das ist also logischer.
INVOKESTATIC
javac
erzeugt dies nicht.openjdk version "11.0.6" 2020-01-14
auf Ubuntu 64 Bit kompiliert .Antworten:
Warum nicht?
Vorausgesetzt
Ein Anruf wie
c.test()
wherec
wird alsC
muss geworfen, wenn erc
istnull
. Ihre Methode entsprichtda Sie nur mit Konstanten arbeiten. Da
test
es nicht statisch ist, muss die Überprüfung durchgeführt werden. Normalerweise wird dies implizit ausgeführt, wenn auf ein Feld zugegriffen wird oder eine nicht statische Methode aufgerufen wird, aber Sie tun dies nicht. Daher ist eine explizite Überprüfung erforderlich. Eine Möglichkeit ist anzurufenObjects.requireNonNull
.Der Bytecode
Vergessen Sie nicht, dass der Bytecode für die Leistung grundsätzlich irrelevant ist. Die Aufgabe von
javac
besteht darin, einen Bytecode zu erzeugen, dessen Ausführung Ihrem Quellcode entspricht. Es ist nicht zu tun , bedeutet keine Optimierungen, wie optimierte Code in der Regel länger und schwieriger zu analysieren, während die Bytecode ist eigentlich der Quellcode für die Optimierung JIT - Compiler. Esjavac
wird also erwartet, dass es einfach bleibt ....Die Performance
Ich würde zuerst den Profiler beschuldigen. Das Profilieren von Java ist ziemlich schwierig und Sie können niemals perfekte Ergebnisse erwarten.
Sie sollten wahrscheinlich versuchen, die Methode statisch zu machen. Sie sollten diesen Artikel über Nullprüfungen lesen .
quelle
this
istnull
. Wie Sie selbst sagten, muss ein Aufruf wiec.test()
fehlschlagen, wenn erc
ist,null
und er muss sofort fehlschlagen, anstatt die Methode einzugeben. Also innerhalbtest()
,this
kann nie seinnull
(sonst gäbe es einen JVM Fehler sein). Also keine Notwendigkeit zu überprüfen. Der eigentliche Fix sollte das Feldtaxos
in ändernstatic
, da es keinen Sinn macht, in jedem Fall Speicher für eine Konstante zur Kompilierungszeit zu reservieren. Danntest()
iststatic
es irrelevant , ob es irrelevant ist.Nun, es scheint, meine Frage war "falsch", da sie nichts mit dem Operator zu tun hat, sondern mit dem Feld selbst. Ich weiß immer noch nicht warum ..
Was wird in:
quelle
this
Referenzennull
? Wäre das möglich?Integer
irgendwie und dies ist das Ergebnis von Autoboxen?ALOAD 0
verweisenthis
? Es wäre also sinnvoll (nicht wirklich), dass der Compiler einen Nullcheck hinzufügtthis
? Großartig: /javac
, um ihn morgen zu überprüfen. und wenn das auch dieses verhalten zeigt, denke ich, dass es ein javac-bug sein könnte?Zunächst ein minimal reproduzierbares Beispiel für dieses Verhalten:
Das Verhalten beruht darauf, wie der Java-Compiler Konstanten zur Kompilierungszeit optimiert .
Beachten Sie, dass im Bytecode
foo()
keine Objektreferenz aufgerufen wird, um den Wert von zu erhaltenbar
.iconst_5
Dies liegt daran, dass es sich um eine Konstante zur Kompilierungszeit handelt und die JVM die Operation einfach ausführen kann , um diesen Wert zurückzugeben.Wenn Sie
bar
in eine nicht kompilierte Zeitkonstante wechseln (entweder indem Sie dasfinal
Schlüsselwort entfernen oder nicht innerhalb der Deklaration, sondern innerhalb des Konstruktors initialisieren), erhalten Sie:Dabei wird
aload_0
die Referenz vonthis
auf den Operandenstapel verschoben, um dann dasbar
Feld dieses Objekts abzurufen.Hier ist der Compiler klug genug zu bemerken, dass
aload_0
(diethis
Referenz bei Mitgliedsfunktionen) logischerweise nicht sein kannnull
.Ist Ihr Fall tatsächlich eine fehlende Compileroptimierung?
Siehe @ maaartinus Antwort.
quelle