J'exécute le code Java suivant sur un ordinateur portable avec un processeur Intel Core i7 à 2,7 GHz. J'avais l'intention de le laisser mesurer le temps qu'il faut pour terminer une boucle avec 2 ^ 32 itérations, ce que je m'attendais à être d'environ 1,48 seconde (4 / 2,7 = 1,48).
Mais en réalité, cela ne prend que 2 millisecondes, au lieu de 1,48 s. Je me demande si cela est le résultat d'une optimisation JVM en dessous?
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
pour voir.javac
fait très peu d'optimisation réelle et en laisse la majeure partie au compilateur JIT.Réponses:
Il y a une des deux possibilités ici:
Le compilateur s'est rendu compte que la boucle était redondante et ne faisait rien, donc il l'a optimisée.
Le JIT (compilateur juste à temps) s'est rendu compte que la boucle était redondante et ne faisait rien, donc il l'a optimisée.
Les compilateurs modernes sont très intelligents; ils peuvent voir quand le code est inutile. Essayez de mettre une boucle vide dans GodBolt et regardez la sortie, puis activez les
-O2
optimisations, vous verrez que la sortie est quelque chose du genreJe voudrais clarifier quelque chose, en Java, la plupart des optimisations sont effectuées par le JIT. Dans certains autres langages (comme C / C ++), la plupart des optimisations sont effectuées par le premier compilateur.
la source
Il semble qu'il a été optimisé par le compilateur JIT. Quand je le désactive (
-Djava.compiler=NONE
), le code s'exécute beaucoup plus lentement:J'ai mis le code d'OP à l'intérieur de
class MyClass
.la source
Je vais juste dire l'évidence - qu'il s'agit d'une optimisation JVM qui se produit, la boucle sera simplement supprimée du tout. Voici un petit test qui montre la différence énorme
JIT
quand il est activé / activé uniquement pourC1 Compiler
et désactivé du tout.Avertissement: n'écrivez pas de tests comme celui-ci - c'est juste pour prouver que la "suppression" réelle de la boucle se produit dans le
C2 Compiler
:Les résultats montrent qu'en fonction de la partie de la
JIT
méthode activée, la méthode devient plus rapide (tellement plus rapide qu'il semble qu'elle ne fait «rien» - suppression de la boucle, ce qui semble se produire dans leC2 Compiler
- qui est le niveau maximum):la source
Comme déjà souligné, le compilateur JIT (juste à temps) peut optimiser une boucle vide afin de supprimer les itérations inutiles. Mais comment?
En fait, il existe deux compilateurs JIT: C1 et C2 . Tout d'abord, le code est compilé avec le C1. C1 collecte les statistiques et aide la JVM à découvrir que dans 100% des cas notre boucle vide ne change rien et est inutile. Dans cette situation, C2 entre en scène. Lorsque le code est appelé très souvent, il peut être optimisé et compilé avec le C2 à l'aide des statistiques collectées.
À titre d'exemple, je vais tester le prochain extrait de code (mon JDK est défini sur slowdebug build 9-internal ):
Avec les options de ligne de commande suivantes:
Et il existe différentes versions de ma méthode run , compilées avec les C1 et C2 de manière appropriée. Pour moi, la variante finale (C2) ressemble à ceci:
C'est un peu brouillon, mais si vous regardez de près, vous remarquerez peut-être qu'il n'y a pas de longue boucle en cours ici. Il y a 3 blocs: B1, B2 et B3 et les étapes d'exécution peuvent être
B1 -> B2 -> B3
ouB1 -> B3
. OùFreq: 1
- fréquence estimée normalisée d'exécution d'un bloc.la source
Vous mesurez le temps nécessaire pour détecter que la boucle ne fait rien, compilez le code dans un thread d'arrière-plan et éliminez le code.
Si vous exécutez cela avec,
-XX:+PrintCompilation
vous pouvez voir que le code a été compilé en arrière-plan vers le compilateur de niveau 3 ou C1 et après quelques boucles vers le niveau 4 de C4.Si vous modifiez la boucle pour utiliser un,
long
il n'est pas aussi optimisé.à la place vous obtenez
la source
long
compteur empêcherait-il la même optimisation de se produire?int
note char et short sont effectivement les mêmes au niveau du code d'octet.Vous considérez l'heure de début et de fin en nanoseconde et vous divisez par 10 ^ 6 pour calculer la latence
cela devrait être
10^9
parce que1
seconde =10^9
nanoseconde.la source