Ce TP introduit la notion d'optimisation.
Optimiser un programme, c'est le rendre plus efficace, donc en
général plus rapide.
Il comporte plusieurs parties, chacune montrant une forme d’optimisation
particulière.
Comme le nom du Tp l'indique, nous allons essayer de modifier le source de programmes écrit en langage d'assemblage afin de les optimiser. On pourra soit enlever des lignes de code "superflues", soit remplacer certaines lignes par d'autres.
Nous allons voir qu'il est possible de jouer sur les registres d'entrée ou de sortie des fonctions afin d'optimiser un programme. Pour cela, nous allons écrire un petit programme en C (in_out.c) qui ne fait pas grand chose, mais qui met en oeuvre deux fonctions.
Saisir le programme suivant (en le comprenant !).
Compiler ce programme en lançant la commande :
$ gcc -Wall inOut.c -o inOut
Lancer le programme et constater ce qu’il
fait (pas grand chose, effectivement).
Traduire ce fichier en langage d’assemblage en
lançant la commande :
$ gcc –S inOut.c
Vous devez obtenir quelque chose ressemblant à ceci :
Analyser ce fichier et l’optimiseren supprimant
les lignes de code qui vous paraissent inutiles.
Tester chaque modification en recompilant le ficher
assembleur (et non plus le programme C !)
Attention à ne pas écraser votre fichier source
lors de vos compilation !!!
Recompiler le programme C avec les commandes :
$ gcc –S –Os inOut.o –o inOut_opt.s
$ gcc –S –O3 inOut.o –o inOut_opt3.s
Comparer les fichiers inOut_opt?.s avec le fichier in_out.s que vous avez optimisé manuellement.
Remarque : l'option -Os
optimise la taille du code,
alors que -O3
optimise la vitesse du code. La seconde permet donc
l'inlining, c'est-à-dire le remplacement d'un appel de
fonction directement par son code, comme pour une macro.
Conclure sur les capacités d'optimisation de GCC.
Nous avons vu précédemment qu’il était possible pour le compilateur d’optimiser un programme en enlevant certaines lignes de codes assembleur superflues. Dans la suite de ce tp, nous allons voir qu’il est possible d’optimiser un programme en prenant quelques "initiatives" (pour le compilateur) et de ne pas tenir compte des lignes de codes C.
Afin de gagner du temps pour tout ce qui est compilation et optimisation, il est préférable d’écrire un petit script que l’on va lancer à chaque modification du code source:
Donner les droits d'exécution à ce script.
Lancer ce script en spécifiant le nom du programme
à tester :
$ Chrono boucle
Ce script vous génère alors 3 fichiers assembleurs et 3 exécutables, compilés respectivement sans optimisation, avec optimisation rapide et avec optimisation complète. Il mesure ensuite les temps d'exécution de ces 3 exécutables.
Comparer ces temps et conclure.
Remarque : Il peut être utile de modifier le
nombre d'itérations de la boucle pour avoir des différences
de temps parlantes
...
Visualiser les fichiers sources en langage d’assemblage et
les analyser de façon à voir ce qu’à
fait l’optimisation O1.
Conclure sur le devenir du code sans effet.
En fonction du type de microprocesseur sur lequel il travaille, le
compilateur va essayer d’utiliser tous les registres encore libres du
microprocesseur afin de diminuer le temps d’exécution d’un
programme, comme on l’avait constaté lors du Tp 4.
Afin de le mettre en évidence, voici deux programmes à
tenter d'optimiser :
Evaluer l'impact de la modification sur les temps
d'exécution.
Visualiser les fichiers assembleur et les
analyser de façon à voir ce qu’à fait
l’optimisation.
Déclarer 5 variables de types int k, l, m, n et o,
et ajouter les lignes k++, l++ etc… dans la boucle.
Comparer à nouveau les temps.
Visualiser le fichier source avec_reg.s et voir si
les 7 variables ont chacune été affectées à un
registre du microprocesseur.
Conclure en vous référant au cours sur les technologies RISC et CISC...