J'essaie d'obtenir autant de performances que possible à partir d'une méthode interne.
Le code Java est:
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
}
Dans mon profileur, j'ai vu qu'il y avait 1% de dépenses CPU java.util.Objects.requireNonNull
, mais je n'appelle même pas cela. Lors de l'inspection du bytecode, j'ai vu ceci:
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
Le compilateur génère donc cette vérification (inutile?). Je travaille sur des primitives, ce qui ne peut pas être de null
toute façon, alors pourquoi le compilateur génère-t-il cette ligne? Est-ce un bug? Ou un comportement «normal»?
(Je pourrais travailler avec un bitmask, mais je suis juste curieux)
[MISE À JOUR]
L'opérateur semble n'avoir rien à voir avec cela (voir la réponse ci-dessous)
En utilisant le compilateur eclipse (version 4.10) j'obtiens ce résultat plus raisonnable:
public getParent (I) I lève java / io / IOException L0 LINENUMBER 77 L0 ILOAD 1 ICONST_4 IREM ISTORE 2 L1 LINENUMBER 78 L
C'est donc plus logique.
INVOKESTATIC
javac
ne génère pas cela.openjdk version "11.0.6" 2020-01-14
sur ubuntu 64 bits.Réponses:
Pourquoi pas?
En supposant
un appel comme
c.test()
oùc
est déclaré commeC
doit lancer quandc
estnull
. Votre méthode équivaut àcar vous ne travaillez qu'avec des constantes. Étant
test
non statique, la vérification doit être effectuée. Normalement, cela se ferait implicitement lorsqu'un champ est accédé ou qu'une méthode non statique est appelée, mais vous ne le faites pas. Une vérification explicite est donc nécessaire. Une possibilité est d'appelerObjects.requireNonNull
.Le bytecode
N'oubliez pas que le bytecode n'est fondamentalement pas pertinent pour les performances. La tâche de
javac
est de produire un bytecode dont l'exécution correspond à votre code source. Il n'est pas destiné à effectuer des optimisations, car le code optimisé est généralement plus long et plus difficile à analyser, tandis que le bytecode est en fait le code source du compilateur JIT d'optimisation. Doncjavac
s'attend donc à ce que cela reste simple ....La performance
Je blâmerais d'abord le profileur. Le profilage de Java est assez difficile et vous ne pouvez jamais vous attendre à des résultats parfaits.
Vous devriez probablement essayer de rendre la méthode statique. Vous devriez sûrement lire cet article sur les contrôles nuls .
la source
this
est pasnull
. Comme vous l'avez dit vous-même, un appel comme celui-cic.test()
doit échouer quand il l'c
estnull
et il doit échouer immédiatement, au lieu d'entrer dans la méthode. Donc à l'intérieurtest()
, çathis
ne peut jamais êtrenull
(sinon il y aurait un bug JVM). Donc pas besoin de vérifier. Le correctif réel devrait changer le champtaxos
enstatic
, car il est inutile de réserver de la mémoire dans chaque instance pour une constante de temps de compilation. Alors, que ce soittest()
eststatic
est hors de propos.Eh bien, il semble que ma question était «fausse» car elle n'a rien à voir avec l'opérateur, mais plutôt avec le champ lui-même. Je ne sais toujours pas pourquoi ..
Qui se transforme en:
la source
this
référencesnull
? Serait-ce possible?Integer
quelque sorte, et c'est le résultat de l'autoboxing?ALOAD 0
référencethis
? Il serait donc logique (pas vraiment) que le compilateur ajoute un nullcheckthis
? Super: /javac
de commande pour vérifier demain; et si cela montre également ce comportement, je pense que ce pourrait être un bug javac?Tout d'abord, voici un exemple reproductible minimal de ce comportement:
Le comportement est dû à la façon dont le compilateur Java optimise les constantes au moment de la compilation .
Notez que dans le code d'octet
foo()
aucune référence d'objet n'est accessible pour obtenir la valeur debar
. C'est parce que c'est une constante de temps de compilation et donc la JVM peut simplement exécuter leiconst_5
opération pour renvoyer cette valeur.En changeant
bar
en constante de temps non compilée (soit en supprimant lefinal
mot - clé, soit en ne l'initialisant pas dans la déclaration mais à l'intérieur du constructeur), vous obtenez:où
aload_0
pousse la référence dethis
sur la pile d'opérandes pour ensuite obtenir lebar
champ de cet objet.Ici, le compilateur est assez intelligent pour remarquer que
aload_0
(lathis
référence en cas de fonctions membres) ne peut logiquement pas êtrenull
.Votre cas est-il en fait une optimisation de compilateur manquante?
Voir la réponse @maaartinus.
la source