Récemment, je lisais ceci article .
Selon cet article, Java Compiler ie javac n'effectue aucune optimisation lors de la génération de bytecode. Est-ce vraiment vrai? Si oui, alors peut-il être implémenté comme un générateur de code intermédiaire pour supprimer la redondance et générer code optimal?
javac
ne fera que très peu d'optimisation, le cas échéant.
Le fait est que le compilateur JIT fait la plupart de l'optimisation - et il fonctionne mieux s'il a beaucoup d'informations, dont certaines peuvent être perdues si javac
a également effectué l'optimisation. Si javac
effectuait une sorte de déroulement de boucle, il serait plus difficile pour le JIT de le faire lui-même d'une manière générale - et il a plus d'informations sur les optimisations qui en fait fonctionneront, comme il connaît la plate-forme cible.
J'ai arrêté de lire en arrivant dans cette section:
Plus important encore, le compilateur javac n'effectue pas d'optimisations simples comme le déroulement de boucle, la simplification algébrique, la réduction de la force et autres. Pour obtenir ces avantages et d'autres optimisations simples, le programmeur doit les exécuter sur le code source Java et ne pas compter sur le compilateur javac pour les réaliser.
Premièrement, faire un déroulement de boucle sur Java n'est presque jamais une bonne idée. La raison pour laquelle javac
ne fait pas grand-chose en termes d'optimisation est que c'est fait par le JIT dans la JVM, ce qui peut prendre de bien meilleures décisions que le compilateur pourrait, car il peut voir exactement quel code est le plus exécuté.
Le compilateur javac
supportait une fois une option pour générer un bytecode optimisé en passant -o
sur la ligne de commande.
Cependant, à partir de J2SE1.3, la machine virtuelle Java HotSpot a été livrée avec la plate-forme , qui a introduit des techniques dynamiques telles que la compilation juste à temps et l'optimisation adaptative des chemins d'exécution courants. D'où le -o
a été ignoré par le compilateur Java commençant cette version.
Je suis tombé sur ce drapeau en lisant la tâche Ant javac
et son attribut optimize
:
Indique si la source doit être compilée avec optimisation; par défaut,
off
. Notez que cet indicateur est simplement ignoré par lejavac
de Sun commençant par JDK 1.3 (car l'optimisation au moment de la compilation n'est pas nécessaire).
Les avantages des optimisations dynamiques de la JVM HotSpot par rapport à l'optimisation au moment de la compilation sont mentionnés dans cette page :
Le serveur VM contient un compilateur adaptatif avancé qui prend en charge bon nombre des mêmes types d'optimisations effectuées en optimisant les compilateurs C++, ainsi que certaines optimisations qui ne peuvent pas être effectuées par les compilateurs traditionnels, telles que les alignements agressifs entre invocations de méthodes virtuelles. Il s'agit d'un avantage concurrentiel et de performances par rapport aux compilateurs statiques. La technologie d'optimisation adaptative est très flexible dans son approche et surpasse généralement même les techniques avancées d'analyse statique et de compilation.
J'ai étudié la sortie Java bytecode dans le passé (en utilisant une application appelée FrontEnd). Il ne fait essentiellement aucune optimisation, sauf pour les constantes en ligne (finales statiques) et le précalcul des expressions fixes (comme 2 * 5 et "ab" + "cd"). Cela explique en partie pourquoi il est si facile à démonter (en utilisant une application appelée JAD)
J'ai également découvert quelques points intéressants pour optimiser votre Java avec. Cela m'a aidé à améliorer la vitesse des boucles internes de 2,5 fois.
Une méthode comporte 5 variables d'accès rapide. Lorsque ces variables sont appelées, elles sont plus rapides que toutes les autres variables (probablement à cause de la maintenance de la pile). Les paramètres d'une méthode sont également comptés jusqu'à ces 5. Donc, si vous avez du code à l'intérieur de la boucle qui est exécuté comme un million de fois, allouez ces variables au début de la méthode et n'avez aucun paramètre.
Les variables locales sont également plus rapides que les champs, donc si vous utilisez des champs à l'intérieur des boucles internes, mettez ces variables en cache en les affectant à une variable locale au début de la méthode. Mettez en cache la référence et non le contenu. (comme: int [] px = this.pixels;)
Pour optimiser votre bytecode, vous pouvez utiliser Proguard .
Comme d'autres l'ont noté, le JIT dans une JVM grand public optimisera le code au fur et à mesure qu'il le compilera et comme il a accès à plus de contexte, il surpassera probablement Proguard. Ce n'est peut-être pas le cas dans les machines virtuelles plus simples. Dans le monde Android Android, il est courant d'utiliser les optimisations Proguard lors du ciblage de Dalvik (le VM fourni avec Android avant Sucette).
Proguard réduit également et obscurcit le bytecode, ce qui est indispensable lors de l'envoi d'applications côté client (même si vous n'utilisez pas les optimisations).
Le compilateur n'optimise pas le bytecode car il est optimisé au moment de l'exécution par l'optimiseur JIT.
Si le type d'exécution que vous ciblez n'a pas d'optimiseur JIT (même s'il avait un compilateur JIT), ou si vous compilez AOT, je recommande d'utiliser un obfuscateur d'optimisation comme Proguard ou Allatori.