L’objectif premier de cet exercice est de savoir instrumenter un code source Java de sorte à pouvoir faire certaines mesures lors de l’exécution du programme :
Un autre objectif de ce TP est de s’initier à la complexité algorithmique, simplement en comptant les instructions arithmétiques et les accès mémoire des programmes analysés.
Pour faire des mesures comme le temps de traitement et le nombre d’appels effectifs à une méthode, pour un programme en cours d’exécution, il existe plusieurs techniques et outils.
<aside> 💡 Quel qu’il soit, un dispositif de mesure influe sur le système mesuré. Donc l’emploi d’outils de profilage a un impact sur l’exécution du programme.
</aside>
Des profileurs sont des programmes capables d’intercepter les communications et appels du programme avec l’OS pour en retirer des informations de toutes sortes (nb d’appels, temps, utilisation mémoire fine : pile, tas, défauts de cache, …).
Dans le cas particulier de Java, le compilateur javac ne produit pas directement un programme exécutable, au contraire du C/C++ par exemple. L’exécution du programme en Java passe par une machine virtuelle et ainsi les profileurs génériques ne sont d’aucune utilité, sauf à profiler la machine virtuelle Java elle même, ce qui n’est a priori pas notre but.
Il existe toutefois des profileurs dédiés à Java, mais leur emploi est souvent trop complexe pour notre niveau d’expertise. Dans Idea, deux profileurs sont proposés par défaut : Java Flight Recorder et Async Profiler.
Sans profileur, on peut simplement instrumenter notre code en y insérant des instructions de mesure/comptage.
Toutefois, on veut modifier le moins possible le code à analyser ; ainsi l’insertion des outils ne compromettra pas ou peu la lisibilité et la remise en configuration de production.
La solution la plus simple pour analyser une méthode est de la passer en paramètre à une méthode profileuse qui pourra ainsi dissimuler les instructions d’analyse. Ainsi, si on imagine par exemple que, dans une classe A, on cherche à analyser une méthode
double oneMethod(double x){...}
alors, au lieu de l’appeler classiquement par exemple avec
double r = oneMethod(0.6);
on l’appellera, dans le cadre du profilage, par
double r = Profiler.analyse(A::oneMethod, 0.6);
On va donc créer une classe Profiler et lui ajouter une méthode analyse() ayant pour entête :