JRat " Java Runtime Analysis Toolkit " - Analyser pour optimiser
Démonstration de l'outils JRat d'analyse de programme Java
JRat est un outils Opensource dont le but est d'aider les développeurs à comprendre le déroulement de leur programme Java. Il permet pour cela :
- créer des logs
- calculer des statistiques de temps
- calculer le nombre de passages dans des méthodes
- calculer les temps de réponse moyen des méthodes
Et enfin, JRat permet de faire tout ceci sans avoir à ajouter une seule ligne de code !
1. Fonctionnement de JRat
Le fonctionnement de JRat se base sur 4 choses :
- L'application Java : c'est notre programme tel quel ,nous n'avons strictement rien ajouté.
- JRat Runtime : c'est le programme JRat qui va s'interfacer avec notre programme java.
- File : ce sont les fichiers de logs générés par JRat.
- JRat Desktop : C'est l'application cliente de JRat qui va lire et interpréter les fichiers de logs.
Pour comprendre plus en détail ce qu'il se passe voici un schéma du flux de données :
Comme on peut le voir, le point de départ est toujours notre application java d'origine qui va être compilée normalement pour générer du Java Bytecode. C'est une fois ce bytecode généré que JRat va intervenir pour générer une 2eme version du Java bytecode dans lequel il aura injecté de quoi le monitorer. Lors de l'exécution les librairies de JRat vont donc pouvoir percevoir toutes les informations nécessaires pour générer des fichiers de log de l'exécution. Il ne restera donc plus qu'à utiliser le JRat desktop ou un autre outils capable d'interpréter les fichiers de log pour lire l'analyse.
2. Installation et utilisation de JRat
Ces quelques aides à l'installation de JRat sont valables pour Java 1.4 ou ultérieur et ont été réalisés sous eclipse. Pour de plus amples informations se reporter au tutorial d'installation de JRat ici.
La première chose à faire pour utiliser JRat est d'inclure les librairies bcel et jrat :
En ce qui concerne l'installation ... ben c'est tout :)
La suite relève de l'utilisation. Pour cela vous aurez besoin du JRat Desktop. Celui-ci est tout simplement dans la
librairie shiftone-jrat.jar que vous avez inclus sur votre projet, et comme c'est un exécutable, vous n'avez
qu'à le lancer (double-click ou java -jar shiftone-jrat.jar).
Une fois que JRat Desktop est lancé, il va falloir lui spécifier vos fichiers compilés (.class) à analyser :
Je conseille le choix récursif afin de tout inclure tout simplement.
A ce stade la, et en indiquant vos fichiers compilés à JRat, vous lui avez permis de
générer une deuxième version du Java bytecode dans lequel il a intégré ses outils
de monitoring (cf partie 2). Il ne faut donc surtout pas oublier cette étape ;)
Il ne reste plus qu'à exécuter votre programme. Pour cela, un paramètre pour la JVM est nécessaire :
C'est lui qui va permettre de déclencher la saisie des logs et d'en spécifier le format. En effet, JRat vous permet d'afficher vos vos données selon différents modes, il faudra donc lui spécifier le paramètre correspondant :
- TreeMethodHandlerFactory : résultats sous forme d'un arbre des appels successifs (utilisé dans notre exemple)
- StatMethodHandlerFactory : résultats sous formes de statistiques d'un certain nombre de paramètre
- RateMethodHandlerFactory : résultats sous forme de graphe
- LogMethodHandlerFactory : traçage plus simpliste des appels successifs
Voila vous pouvez désormais lancer votre application, JRat is watching you.
Une fois terminé, vous aurez à la racine de votre projet un répertoire JRatOutput dans
lequel vous trouverez des répertoires datés contenant vos logs générés. Vous pouvez désormais les
ouvrir avec JRat Desktop pour faire votre analyse...
3. Un exemple d'utilisation : le voyageur de commerce
Pour illustrer l'utilisation de JRat, voici l'exemple du voyageur de commerce. Cet exemple utilise un algorithme dit génétique. Les algorithmes génétiques se basent sur la théorie de l'évolution de l'espèce (Darwin). Pour résumer brièvement leur fonctionnement, on commence avec des couples de solutions potentielles qui constituent une "population" et à laquelle on fait suivre le cycle suivant :
- Reproduction : on prend des solutions et on les "mix" d'une manière choisie pour générer une nouvelle solution issue des deux précédentes.
- Mutation : on prend des solutions au hasard sur lesquels on opère un changement aléatoire.
- Sélection : on écarte de la population les solutions les moins adaptées.
Et on recommence ce cycle un certain nombre de fois ...
J'ai choisi cet exemple car il est utilisé pour résoudre des problèmes dont on ne sait pas trouver une solution autrement qu'en testant toutes les solutions possibles. C'est donc un algorithme sensible que l'on utilise en vue d'obtenir les performances les meilleurs possibles. C'est pourquoi il est très intéressant d'étudier ses performances avec un outils comme JRat.
Supposons maintenant que je sois en train de travailler pour une grande compagnie d'aéroports disposant de plein d'aéroports
dans le monde entier. Cette société m'emploie pour résoudre un problème bien connu : celui du voyageur de commerce. En effet,
la société souhaite souvent programmer des voyages d'aéroports en aéroports pour atteindre un destination lointaine, mais cela
en étant garantie de faire le trajet le plus court possible.
A première vue, seule l'intuition et un aperçu générale de toutes les étapes permettraient d'avoir une vague une idée du
meilleur chemin à employer. Mais cela revient au final à tester toutes les solutions possibles pour en être sûr. Donc
si vous faisiez un programme testant toutes les solutions possibles, alors tester le meilleur chemin pour plus de 100 villes seraient
tout simplement impossible pour votre programme en un temps raisonnable.
C'est pourquoi, vous faites un super programme utilisant les algorithmes génétiques pour répondre au besoin de l'entreprise. Ce programme utilise tout de même un bon nombre d'itération et utilise un algorithme particulier pour l'étape de reproduction, de mutation et éventuellement de sélection. C'est pourquoi, votre conscience professionnelle et votre volonté d'obtenir une prime de résultat pour les performances de votre application vous pousse à effectuer une analyse de performance sur votre programme afin d'en étudier son comportement.
Vous installez donc JRat, lancez JRate Desktop en lui donnant les binaires de votre programe, et lancez votre programme tranquillement :
Votre programme s'est bien exécuté, et vous pouvez ouvrir le fichier de log généré avec JRat Desktop. Voici le résultat :
Premier retour d'information, vous savez désormais que votre programme a mis environ 7,187 secondes pour résoudre votre problème. On peut ensuite constater que JRat indique en rouge les endroits ayant été les plus visités, et donc ceux étant le plus susceptibles de nous intéressez. Analysons :
- C'est tout d'abord la méthode run() que l'on voit en rouge et dans laquelle on a passé 99.8% du temps. Mais c'est tout à fait normal puisque c'est le point de lancement de notre algorithme.
- On voit ensuite que 3 méthodes importantes sont appelés : selectionne() mutation() et reproduction(). On peut donc confirmer que notre le principe de notre algorithme génétique fonctionne. Mais on peut aussi constater que sur ces trois méthodes on passe 88% de notre temps dans la méthode de reproduction !
- Et enfin, au sein même de la méthode de reproduction, on peut constater que l'on y passe la plupart du temps dans la méthode de tri. Etrange !
Cette première analyse est donc assez riche puisque notre programme, bien qu'il marchait très bien et trouvait la
bonne solution, adopte un répartition de sa charge de travail assez bizarre.
A ce stade nous savons donc grâce à notre analyse qu'il serait beaucoup plus judicieux d'aller regarder le code de
notre fonction de reproduction que de notre fonction de mutation... De plus, nous savons qu'au sein même de la méthode de reproduction
il faut faire particulièrement attention à la méthode de tri, qui mange la plupart du temps. Au final on peut donc même affirmer
que soit notre algorithme de reproduction est mal fait soit notre méthode de tri est très mauvaise.
En regardant le code de la reproduction et de ses appels à la méthode de tri on constate finalement dans notre exemple que c'était une erreur algorithmique. En effet, pour chaque reproduction un nouvel élément était ajouté et la population était tout de suite retriée, alors que l'on peut trier une seule et unique fois après avoir ajouté tous les nouveaux éléments.
Pour vérifier la validité de notre correction, la même analyse est refaite, en prenant bien soin de spécifier les nouvelles classes binaires puisqu'elles ont été modifiées. Voici le résultat :
Le résultat est flagrant :
- Le temps d'exécution est maintenant de 1,406 secondes contre 7,187 avant la modification
- On constate maintenant que l'on passe un temps équivalent entre la mutation et la sélection
On peut donc maintenant être beaucoup plus au clair sur les performances de notre programme. Mais on pourrait encore pousser les analyses et tenter de l'optimiser. En effet, si l'on regarde bien les analyses, on voit parfaitement que notre programme passe finalement beaucoup de son temps à trier les données. C'est tout à fait logique et normal dans le cadre de notre algorithme génétique, JRat nous le confirme et ce serait donc sur une méthode de tri plus efficace que nous travaillerons finalement pour toucher notre prime de résultat ...