Normalement, Java optimise le virtuel appels sur la base du nombre de mises en œuvre rencontrés sur un appel de côté. Ceci peut être facilement vu dans les résultats de mon test, quand on regarde myCode
, ce qui est une méthode triviale retour d'un stockées int
. Il y a un trivial
static abstract class Base {
abstract int myCode();
}
avec un couple identique de mise en œuvre comme
static class A extends Base {
@Override int myCode() {
return n;
}
@Override public int hashCode() {
return n;
}
private final int n = nextInt();
}
Avec l'augmentation du nombre de mises en œuvre, le calendrier de l'appel de méthode se développe à partir de 0,4 ns à travers 1.2 ns pour les deux implémentations à 11,6 ns, puis se développe lentement. Lorsque la JVM a vu plusieurs de la mise en œuvre, c'est à dire, avec preload=true
les horaires diffèrent légèrement (à cause d'un instanceof
test nécessaire).
Jusqu'à présent tout est clair, cependant, l' hashCode
se comporte assez différemment. En particulier, c'est 8 à 10 fois plus lent que dans trois cas. Aucune idée pourquoi?
Mise à JOUR
J'étais curieux de savoir si les pauvres hashCode
pourrait être aidé par l'envoi manuellement, et il peut beaucoup.
Un couple de branches a fait le travail parfaitement:
if (o instanceof A) {
result += ((A) o).hashCode();
} else if (o instanceof B) {
result += ((B) o).hashCode();
} else if (o instanceof C) {
result += ((C) o).hashCode();
} else if (o instanceof D) {
result += ((D) o).hashCode();
} else { // Actually impossible, but let's play it safe.
result += o.hashCode();
}
Notez que le compilateur permet d'éviter une telle optimisations pour plus de deux mise en œuvre, comme la plupart des appels de méthode sont beaucoup plus cher qu'un simple champ de charge et le gain serait minime par rapport au code de la météorisation.
L'original de la question "Pourquoi ne pas JIT optimiser l' hashCode
comme les autres méthodes" reste et hashCode2
preuves qu'il pouvait, en fait.
Mise à JOUR 2
Il ressemble à bestsss est bon, au moins avec cette remarque
l'appel de hashCode() de la classe de l'extension de Base est la même que l'appel de l'Objet.hashCode() et c'est de cette façon qu'il compile en bytecode, si vous ajoutez un explicite hashCode dans la Base de limiter le potentiel d'attirer les cibles en invoquant de la Base.hashCode().
Je ne suis pas complètement sûr de ce qu'il se passe, mais en déclarant Base.hashCode()
fait hashCode
à nouveau compétitifs.
Mise à JOUR 3
OK, en fournissant un concrètes de mise en œuvre de l' Base#hashCode
aide, cependant, l'équipe doit savoir qu'il n'est jamais appelée, comme toutes les sous-classes définies de leur propre (sauf si une autre sous-classe est chargée, ce qui peut conduire à un deoptimization, mais ce n'est rien de nouveau pour le JIT).
De sorte qu'il ressemble à un manqué d'optimisation de chance #1.
Fournir un résumé de la mise en œuvre de l' Base#hashCode
fonctionne de la même façon. Cela est logique, car il fournit assure que plus de recherche est nécessaire que chaque sous-classe doit fournir sa propre (on ne peut pas simplement hériter de leur grand-parent).
Toujours pour plus de deux implémentations, myCode
est donc beaucoup plus rapide, que le compilateur doit faire quelque chose subobtimal. Peut-être raté une optimisation de la chance #2?