|
|
Gestion de la mémoire
Exercice 1 : L'écroulement (thrashing)
Préparation
Se placer dans un répertoire de travail
tar xvfz ecroul_4Go.tgz
cd Ecroul_4Go
Question 1
cache_line.c et alea.c initialisent un tableau de N méga octets.
Comparer leur manière de travailler.
Question 2
Lancer les programmes cache_line et alea et
comparer leurs performances. Par défaut ces programmes utilisent
des tableaux de 3 Mo.
Question 3
Déterminer à l'aide de la commande 'free -m', la
quantité
de mémoire physique disponible et la taille de l'espace
disque
disponible pour le swap (variable selon le type de PC utilisé).
Question 4
La commande vmstat permet de voir l'évolution des processus et
du
système dans le temps. Elle affiche à l'intervalle de
temps
choisi divers paramètres utiles pour cet exercice :
- r : nombre de processus "runnable" (vmstat lui-même n'est pas
compté)
- b: nombre de processus bloqués en attente disque
- fre: mémoire libre disponible (en Ko)
- si: "swap in" lecture de pages sur le disque (en Ko/s)
- so: "swap out" écriture de pages sur le disque (en Ko/s)
- id: "idle" pourcentage de temps pendant lequel la CPU est
inutilisée
Dans une fenêtre 1, exécuter la commande vmstat 1 (le 1
signifie
1 affichage par seconde)
Dans une fenêtre 2:
./alea n
Où n correspond à partieEntiere(0.25 x
quantité de mémoire physique
libre)
Commenter l'évolution des champs r, b, free, si, so, id (dans la
fenêtre
1) et les différences de temps d'exécution entre les
différents
processus.
Au vu de la valeur de n, à combien de défauts de
pages
mineurs pouvait-on s'attendre ?
Question 4
Dans la fenêtre 1, exécuter la commande ./use_memory
n (avec n=partieEntiere(0.5 x quantité de mémoire
physique
libre)
Dans la fenêtre 2, lancer la commande ./alea m
(avec m=partieEntiere(0.25 x quantité de mémoire physique
libre)
Même manip avec m=partieEntiere(0,5 x quantité de
mémoire
physique totale).
Exercice 2 : Remplacement de pages
Préparation
Se placer dans un répertoire de travail
tar xvfz remplacementDePages.tgz
cd RemplacementDePages
Question 1
Un processus référence successivement les pages
0, 1, 2,
0, 1, 0, 2, 0. Il dispose d'une mémoire avec 2 cadres de page
initialement
vides.
Indiquer, dans le fichier anomalieDeBelady.txt, les séquences de
défauts de page
générés
par ce processus, pour chacun des trois algorithmes de remplacement de
pages : FIFO, LRU, et Optimal.
Exercice 3 : De l'alignement des structures
Préparation
Se placer dans un répertoire de travail
tar xvfz align.tgz
cd Align
make
Le module useGetrusage.c facilite l'utilisation de la fonction
getrusage.
Etudier le "man getrusage" pour bien comprendre le rôle de cette
fonction.
Puis analyser useGetrusage.c pour comprendre son fonctionnement.
Question 1
Comparer la structure tableElement des différents fichiers
align.c,
nonAlign.c, nonAlignPacked.c
Avec diff, comparer nonAlign.c et nonAlignPacked.c
Question 2
Exécuter align, nonAlign et nonAlignPacked
Interpréter les résultats
Question 3
Dans cette question, au vu du comportement de nonAlign, on cherche
à
comprendre pourquoi, par défaut, le compilateur cherche à
avoir
les feuilles des structures alignées sur des frontières
de
4 octets.
Pour ce faire, il faut étudier la traduction assembleur de
l'instruction
:
t[i].unInt = UNEVALEURQUELCONQUE;
Rechercher dans align.s, nonAlign.s et nonAlignPacked.s l'instruction
assembleur
correspondant à l'instruction C ci-dessus (au fait, quelle
instruction
du Makefile a généré l'assembleur ?).
Au vu de la différence de performances entre align et
nonAlignPacked,
qu'en pensez-vous ?
Question 4
Dans la question 3, on a observé que align et nonAlignPacked
avaient
des performances similaires. Dans cette question, on va montrer que si
on est sur une frontière de 16 octets (liée aux
instructions MMX, cf.
exercice 8), nonAlignPacked est légèrement moins
performant que align.
Analyser le fichier testAlign.c. Que fait-il ?
Exécuter plusieurs fois testAlign.
Dans le Makefile, supprimer -DALIGN, sauvegarder et taper la commande :
make
Exécuter plusieurs fois testAlign.
Commenter les différences de performance observées
Question 5
Quelle règle de programmation pouvez-vous vous fixer par rapport
aux
structures ?
Exercice 4 : Algorithme
des "frères siamois" (Buddy System)
Soit un système utilisant l'agorithme des frères siamois
pour
gérer la mémoire. Initialement, la mémoire
consiste
en un bloc de 256K.
- Représenter l'état d'occupation de la
mémoire
après les placements successifs des processus A(5K), B(25K),
C(35K),
et D(20K) puis les terminaisons successives des processus dans l'ordre
A,
D, C, et B.
Exercice 5 : Allocation
avec le Buddy System
Soit un système utilisant l'algorithme des frères siamois
pour
gérer la mémoire.
- Ecrire de façon schématique la
procédure allocation(T) qui correspond à une
demande d'allocation d'une zone mémoire
de taille T et dont la réponse est soit l'adresse du buddy
alloué,
soit -1 si l'allocation est impossible.
On suppose que le système gère une liste pour chaque
puissance
de i utilisée et on ne demande pas de détailler la
gestion
de cette liste (on se limitera à écrire par exemple
"insérer
l'élément dans la liste i").
Exercice 6 : Anomalie de Belady
On appelle "anomalie de Belady" le fait que le nombre de défauts
de
pages augmente quand on augmente le nombre de cadres de pages
disponibles.
On appelle "algorithme à pile" un algorithme présentant
la
propriété d'inclusion : c'est-à-dire, S(N,C)
inclus
dans S(N+1,C) quelle que soit la chaîne de
références
de pages C. S(N,C) représente l'ensemble des pages
présentes
en mémoire après traitement de la chaîne C lorsque
l'on
dispose de N cadres de page.
Une condition suffisante pour qu'un algorithme ne présente pas
l'anomalie
de Belady est qu'il soit à pile.
- Montrer que LRU est à pile.
- Montrer l'anomalie de Belady avec l'algorithme FIFO avec
N=3 puis N=4, sur l'exemple suivant : C = { 1, 2, 3, 4, 1, 2, 5, 1, 2,
3, 4, 5 }.
Retrouver sur cet exemple des états pour lesquels la
propriété
d'inclusion n'est pas vérifiée.
Exercice 7 : Profiling d'un allocateur "maison"
Préparation
Placez-vous dans un répertoire de travail
tar xvfz cmpAlloc.tgz
cd CmpAlloc
Question 1
Complétez les parties "/* A completer */" dans le fichier
monAllocateur.c
NB : Ne vous occupez pas, pour l'instant, des "* A completer au moment
de la question 2 *". Vous vous en occuperez... au moment de la question
2.
Tapez make pour le compiler
Question 2
Exécutez le programme "bug". La troisième ligne qu'il
affiche signale un bug :
s2 =
"1234567890123456"
s1 =
"1234567890123456"
s2 = ""
==> BUG !
Exécutez : valgrind ./bug
Valgrind ne détecte aucun problème.
Dans monAllocateur.c, complétez les "* A completer au moment de
la question 2 *" pour expliquer : a. l'origine de ce bug, b. la raison
pour laquelle valgrind utilisé sur le programme bug ne
détecte rien, et enfin c. quelle fonction utilisée en
lieu et place de strcpy aurait évité ce bug.
Question 3
Exécutez ./cmpAlloc
100000000 1
Tapez gprof
cmpAlloc
Les
premières lignes vous indiquent le temps cumulé
passé dans chaque fonction. Par exemple, nous avons obtenu
l'affichage suivant sur une machine Dell Precision T3500 computers,
équipée d'un processeur Intel Xeon W3530 (2.80 GHz) et de
4 GiB of RAM, avec un système d'exploitation Linux 3.14.9 SMP :
% cumulative
self
self
total
time seconds seconds
calls s/call s/call
name
50.21 2.06
2.06
1 2.06 2.06
travailAvecMalloc
44.58 3.89
1.83
1 1.83 2.05
travailAvecMonMalloc
2.82 4.00
0.12 50000001 0.00
0.00 monFree
2.69 4.11
0.11 50000001 0.00
0.00 monMalloc
0.12 4.12
0.01
1 0.01 0.01
monAllocateurInit
gprof nous indique qu'avec un total de 2,06 secondes, travailAvecMalloc
est aussi performant que travailAvecMonMalloc qui requiert un total de
2,05 secondes. Ce résultat contre-intuitif est dû au fait
que les fonctions malloc et free n'ont pas été prises en
compte ! Pour qu'elles soient prises en compte, il faut faire une
édition de lien avec la version profilée de la
bibliothèque standard C. Donc :
- Editez le Makefile
- Modifiez la ligne concernant LDFLAGS avec : LDFLAGS = -pg
-static -lc_p
- make cleanall ; make
2 cas peuvent se présenter :
- Vous n'avez pas d'erreur à l'édition de
liens : passez au point 4.
- Vous avez une erreur "/usr/bin/ld: ne peut trouver -lc_p"
: la version profilée de la bibliothèque C n'est pas
installée sur votre système. Nous supposons que vous
n'avez pas les droits root sur la machine que vous utilisez. Nous
allons donc mettre en oeuvre un moyen d'obtenir cette version
profilée de bibliothèque sans avoir besoin de l'installer
:
- cd /tmp
- Récupérez une version récente de
la bibliothèque C de GNU : wget
http://ftp.gnu.org/gnu/glibc/glibc-2.21.tar.gz
- tar xvfz glibc-2.21.tar.gz
- mkdir glibc-build
- cd glibc-build
- ../glibc-2.21/configure --prefix=$HOME/Software/glibc
--enable-profile
- Si cette commande vous affiche :
configure: error:
*** LD_LIBRARY_PATH shouldn't contain the current directory when
*** building glibc. Please change the environment variable
*** and run configure again.
tapez : export LD_LIBRARY_PATH=
puis : ../glibc-2.21/configure --prefix=$HOME/Software/glibc-2.21
--enable-profile
- make # Comptez environ 15 minutes de compilation
- make install
- Revenez dans le répertoire CmpAlloc
- Editez le makefile
- Modifiez la ligne concernant LDFLAGS avec : LDFLAGS =
-pg -static -L${HOME}/Software/glibc-2.21/lib -lc_p
- make cleanall;make
- Exécutez ./cmpAlloc
100000000 1
- Tapez gprof cmpAlloc
Dans monAllocateur.c, sachant que le temps passé en malloc est
le temps passé en malloc plus le temps passé en
_int_malloc (regardez le call graph pour vous en convaincre !) et que
le temps passé en free est le temps passé en _int_free +
le temps passé en cfree, complétez les "* A completer au
moment de la question *3* *"
Livrez votre fichier monAllocateur.c final selon les modalités
qui vous ont été indiquées.
Exercice 8 : Contraintes et gains liés aux
instructions MMX/SSE
Préparation
Se placer dans un répertoire de travail
tar xvfz instructionsSSE.tgz
cd InstructionsSSE
Question 1
Le fonction computeResult
du programme calculV0.c
réalise des calculs
sur les éléments de deux tableaux de flottants pArray1 et pArray2 de
taille SIZE . Le
résultat est stocké dans pResult .
On
se propose d'accélérer l'exécution de ce programme
en utilisant les instructions SSE du processeur Intel pour
faire les calculs 4 flottants par 4 flottants.
SSE est :
- une collection de registres de 128 bits,
- un jeu d'instructions qui permet de voir ces registres
comme 4 éléments (entier, flottant) de 32 bits et faire
des opérations
sur ces 4 entiers simultanément. On peut accéder aux
instructions SSE
soit en programmant directement en assembleur, soit en utilisant des
fonctions intrinsèques (intrinsics),
c'est-à-dire des fonctions que le compilateur sait directement
traduire
en une (ou plusieurs) instruction(s) assembleur (voir http://neilkemp.us/src/sse_tutorial/sse_tutorial.html
pour plus d'informations).
calculV1.c (respectivement calculV2.c )
est une version de calculV0.c
exploitant des instructions SSE. Il travaille sur des tableaux
alloués
statiquement (respectivement dynamiquement). Vérifier que le Makefile
prend en compte le commentaire en tête de calculV1.c et calculV2.c .
Taper make pour compiler tous ces programmes.
Taper /usr/bin/time calculV0
Exécuter calculV1
et calculV2 (sans /usr/bin/time ). Pourquoi est-ce
que ces deux programmes
plantent ?
Question 2
Corriger calculV1.c et calculV2.c pour qu'ils ne
plantent plus.
Taper /usr/bin/time calculV1
Taper /usr/bin/time calculV2
Calculer le gain de performances obtenu par rapport à celles de calculV0 . Commenter.
NB : Quelques liens pour approfondir :
Exercice 9 : Arbre AVL
Un arbre AVL (arbre de
Georgy Adelson-Velsky et Landis, nommé
d'après ses inventeurs) est un arbre binaire de recherche qui
s'auto-équilibre. Dans un arbre AVL, la hauteur de deux arbres
enfant d'un nœud quelconque diffère d'au plus 1. Si
à un moment, leur hauteur diffère de plus de un, un
rééquilibrage est déclenché.
Cela garantit une vitesse de recherche dans l'arbre qui est en log2(n),
n étant le nombre de nœuds.
Par exemple, l'arbre de la figure 1 est un arbre AVL, car, pour
chaque nœud, la différence de hauteur entre le sous-arbre
de
gauche et le sous-arbre de droite est inférieure ou égale
à 1.
Figure1 : exemple d'abre AVL (exemple provenant d' ici)
En revanche, l'arbre de la figure 2 n'est pas un arbre AVL. En effet,
pour le nœud 12, la hauteur du sous-arbre de gauche est 4, tandis
que
la hauteur du sous-arbre de droite est 2.
Figure 2 : exemple d'arbre non-AVL (exemple provenant d' ici)
|
|
|
Dans cet exercice, vous allez coder un arbre AVL que nous utiliserons
pour stocker des paires clé/valeur comme le font les classes
Dictionnary
de Java et std::map de la STL C++.
Remarque préliminaire
Vous devez veiller à ce que :
- votre code compile sans warning ;
- dans votre code, quand vous libérez une zone
mémoire, vous mettez le pointeur sur cette zone mémoire
à NULL.
Préparation
Se placer dans un répertoire de travail
tar xvfz arbreAVL.tgz cd ArbreAVL
mv arbreAVL.c arbreAVL_$USER.c
mv testsUnitaires.c testsUnitaires_$USER.c
Cet exercice s'appuie sur un environnement de tests unitaires
appelé cmocka. Vous allez l'installer pour pouvoir vous en servir..
Téléchargez
l'archive contenant la dernière version de cmoka (à
l'heure où nous écrivons cet énoncé, cette
archive s'appelle cmocka-1.1.1.tar.xz). Dans le terminal dans lequel
vous avez effectué les opérations
précédentes :
tar xvfz cmocka-*.tar.xz
mv cmocka-* cmocka
cd cmocka
mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX=../install -DCMAKE_BUILD_TYPE=Debug ..
make
make install # Ca y est : cmocka est installe
cd ../.. # Vous etes revenu.e. dans le repertoire ou vous allez faire votre exercice
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:cmocka/install/lib
Question 1 : Amélioration des tests unitairesCet exercice est fait selon la méthode TDD (Test Driven Development). L'odée est d'écrire complètement les tests de votre module dans testsUnitaires_usernameUnix.c, avant de passer au codage de votre module dans arbreAVL_usernameUnix.c.
Dans le fichier testsUnitaires_usernameUnix.c, en vous inspirant du code déjà existant, remplacez les lignes signalées par un TODO par un appel à assert_null() ou assert_int_equal() pour tester le cas où l'une des fonctions offertes par arbreAVL_usernameUnix.c est appelée avec un arbre AVL vide (i.e. le paramètre vaut NULL).
Dans votre terminal, tapez make run pour compiler votre code et l'exécuter. Sans surprise, tous les tests échouent.
NB :
- La compilation génère le warning "arbreAVL_usernameUnix.c:57:21:
warning: ‘newNode’ defined but not used [-Wunused-function]
static struct Node* newNode(const char *key)". Vu que vous
n'avez pas encore commencé à coder
arbreAVL_usernameUnix.c, ce warning est acceptable. Les warnings de
compilation ne seront plus acceptés à partir de la
question suivante.
- Si la commande make run affiche le message "./testsUnitaires:
error while loading shared libraries: libcmocka.so.0: cannot open
shared object file: No such file or directory", dans votre terminal, tapez la commande :
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:cmocka/install/lib
Question 2 : Codage des fonctions update_height(), nbNodes(), newNode(), insert() et release()
Dans arbreAVL_usernameUnix.c, complétez tous les TODO des fonctions updateHeight(), nbNodes(), newNode() et insert(). Ne vous occupez pas de release() pour l'instant.
NB : Ne vous préoccupez pas pour l'instant de
construire un arbre équilibré (la fonction insert
ne
réalise donc pas, pour l'instant, d'auto-équilibrage) ou
du stockage de la valeur associée à chaque clé.
Aussi, votre fonction insert doit travailler de la
manière suivante :
- Si
node vaut NULL , insert renvoie newNode(key) - Si
key est
plus petit que node->key ,
insert
insère key dans
une feuille la plus basse possible du
sous-arbre de gauche de node .
Pour ce faire, nous vous recommandons d'appeler récursivement insert . NB : la fonction insert() renvoie un struct node *. Aussi, n'oubliez pas de stocker le noeud renvoyé par insert() dans votre structure de données (ne vous contentez pas d'appeler insert()
sans tenir compte de sa valeur de retour, dit autrement comme si
c'était une procédure ; vous auriez alors probablement
des bugs à cette question ou aux questions suivantes).
- Si
key est
plus grand que node->key ,
insert insère key
dans une feuille la plus basse possible du sous-arbre de droite de node .
Pour ce faire, nous vous
recommandons d'appeler récursivement insert . Même NB qu'au point précédent.
- Si
key est
égal à node->key ,
insert renvoie
simplement node sans rien
faire d'autre (ainsi, il n'y jamais deux valeurs identiques dans
l'arbre). - Puis,
insert
met à jour le champ height
de node.
Une fois que vos fonctions sont codées, tapez make run. Corrigez vos fonctions jusqu'à ce que make run affiche.
[ RUN ] test_insert_et_nbNode
[ OK ] test_insert_et_nbNode
Dit autrement, le test unitaire test_insert_et_nbNode est OK.
Exécutez maintenant valgrind ./testsUnitaires. Valgrind vous signale au minimum que vous avez des fuites mémoires. Complétez release() et éventuellement les autres fonctions codées jusqu'à présent pour que valgrind ./testsUnitaires ne vous signale aucun problème d'accès
mémoire ou de fuite mémoire.
Ainsi, test_insert_et_nbNode et test_release sont OK.
Question 3 : Codage des fonctions getKeyHeight() et getKeyRank()
Codez getKeyHeight() jusqu'à ce que test_getKeyHeight soit OK. N'oubliez pas de vérifier avec valgrind que vous n'avez aucun soucis d'accès mémoire ou de fuite mémoire.
Codez getKeyRank() jusqu'à ce que test_getKeyRank soit OK.
Question 4 : Mise à jour du code pour équilibrer l'arbre au moment de sa construction
Cette question a pour objectif de travailler à
l'équilibrage de l'arbre au moment de sa construction.
Pour ce faire, une fois l'insertion réalisée, pour chacun
des nœuds concernés par l'insertion, il faut calculer sa
"balance", i.e. la
différence de hauteur entre le sous-arbre gauche du nœud
et son sous-arbre droit.
Si la balance d'un nœud est comprise entre -1 et +1, il n'y a rien
à faire.
En revanche, si la balance d'un nœud est strictement
inférieure à -1 ou
strictement supérieure à +1, il faut
rééquilibrer l'arbre au niveau de ce nœud. 4 cas se
présentent
(à traiter dans la fonction insert()
après le calcul de la balance). Dans la présentation de
ces 4 cas, 3 nœuds jouent un rôle particulier :
- z est le nœud le plus profond au niveau duquel la
balance est strictement inférieure à -1 ou
strictement supérieure à +1 ;
- x est le nœud petit-enfant de z, dont l'arbre
contient la clé qui vient d'être insérée ;
- y est le nœud enfant de z, qui est parent de x.
1er cas - Cas Gauche-Gauche : Au niveau de z, l'arbre est déséquilibré
à
gauche. De plus, x est situé à gauche de y. Alors, pour
rééquilibrer l'arbre, il suffit d'effectuer une rotation
droite de z (cf. figure 3).
Figure 3 : Traitement du cas Gauche-Gauche (figure provenant d' ici)
2ème cas - Cas
Droite-Droite : Au niveau de z, l'arbre est déséquilibré
à droite. De plus, x est
situé à droite de y. Alors, pour
rééquilibrer l'arbre, il suffit d'effectuer une rotation
gauche de z (cf. figure 4).
Figure 4 : Traitement du cas Droite-Droite (figure provenant d' ici)
3ème cas - Cas
Gauche-Droite : Au niveau de z, l'arbre est déséquilibré
à
gauche. De plus, x est situé à droite de y. Alors, pour
rééquilibrer l'arbre, il suffit d'effectuer une rotation
gauche de y, puis une rotation droite de z (cf. figure 5).
Figure 5 : Traitement du cas Gauche-Droite (figure provenant d' ici)
4ème cas - Cas
Droite-Gauche : Au niveau de z, l'arbre est déséquilibré
à droite. De plus, x est situé à gauche de y.
Alors, pour rééquilibrer l'arbre, il suffit d'effectuer
une rotation droite de y, puis une rotation gauche de z
(cf. figure 6).
Figure 6 : Traitement du cas Droite-Gauche (figure provenant d' ici)
Gérer
ces 4 cas pour ce nœud suffit à rééquilibrer
l'arbre entier, car l'arbre retrouve sa hauteur d'origine.
Complétez arbreAVL_usernameUnix.c :
- codez
la fonction
static struct Node *rightRotate(struct Node *z) qui réalise
le traitement présenté à la figure 3 et retourne la nouvelle racine du sous arbre (ici, y ); par
ailleurs, cette fonction modifie le champ height des nœuds y et z ;
- codez la fonction
static struct Node *leftRotate(struct Node *z)
qui réalise le traitement
présenté à la figure 4 et retourne la
nouvelle racine du sous-arbre (ici y ); par ailleurs,
cette fonction modifie le champ height des nœuds y et z ;
- ajoutez le calcul de la
balance et le traitement des 4 cas à la fonction insert().
Puis, tapez make run. Corrrigez vos fonctions jusqu'à ce que :
- les tests test_equilibrageGaucheGauche, test_equilibrageDroiteDroite, test_equilibrageGaucheDroite et test_equilibrageDroiteGauche soient OK ;
- valgrind ./testsUnitaires ne vous signale aucun problème d'accès
mémoire ou de fuite mémoire.
Question 5 : Stockage de paires clé/valeur dans l'arbre AVLJusqu'à
présent, notre arbre AVL n'a servi qu'à
stocker des clés. Cette
question a pour objectif d'utiliser cet arbre pour y stocker des paires
clé/valeur, et ainsi obtenir une structure de données de
type dictionnaire (cf. classes Dictionnary
de Java et std::map de la STL C++) nous permettant de retrouver la valeur associée à une
clé avec une complexité en O(log(n)).
Complétez arbreAVL.h et arbreAVL_usernameUnix.c :
- ajoutez un champ
char *value à la structure Node ;
- ajoutez un paramètre
char *value
à la fonction newNode et recopiez le dans le
champ value du nœud créé
à l'aide de ce paramètre ;
- ajoutez un paramètre
char *value à la fonction insert
et utilisez ce paramètre de manière adéquate dans
cette fonction. En particulier, s'il existe déjà un nœud ayant pour clé key dans l'arbre, veillez à remplacer l'ancienne valeur du champ value de ce nœud à l'aide de ce paramètre ;
- Complétez la fonction
getValue()
NB : il est probable que l'algorithme de recherche du nœud dans getValue() ressemble à s'y méprendre à l'algorithme de recherche du nœud dans getKeyHeight() .
Pour éviter une duplication de code (jamais saine dans un
projet), créez une fonction qui centralise ce code et qui est
appelée par getValue() et getKeyHeight() .
Prenez en compte, dans les tests unitaires testsUnitaires_usernameUnix.c, le changement de la signature de la fonction insert() en tapant les commandes suivantes dans un terminal :
cp testsUnitaires_$USER.c /tmp # pour sauvegarder votre fichier initial
sed 's@); //@,@g' testsUnitaires_$USER.c > /tmp/toto.c
cp /tmp/toto.c testsUnitaires_$USER.c
Par précaution, vérifiez que les changements de ligne se sont bien passés en tapant
diff
testsUnitaires_$USER.c
/tmp/testsUnitaires_$USER.c | grep
-v insert
Vous ne devez avoir que des lignes du style
38c38
---
54,58c54,58
---
dit autrement, que des lignes mentionnant des numéros de lignes (comme 38c38 ci-dessus) ou "---". Si ce n'est pas le cas, faites
cp /tmp/testsUnitaires_$USER.c .
afin de récupérer votre fichier initial. Puis, postez un message de SOS sur le forum.
Enfin, tapez make run. Corrrigez vos fonctions jusqu'à ce que :
- le test test_getValue soit OK ;
- valgrind ./testsUnitaires ne vous signale aucun problème d'accès
mémoire ou de fuite mémoire.
Remarque finale : il existe d'autres algorithmes qu'AVL pour obtenir
des arbres binaires équilibrés. Un autre algorithme célèbre
est l'arbre bicolore ou arbre rouge-noir (en anglais : red-black tree).
C'est d'ailleurs ce dernier qui est utilisé dans les classes Dictionnary
de Java et std::map de la STL C++. Pour le langage C, http://libredblack.sourceforge.net/
en propose une implémentation utilisable pour tout type de
données. N'hésitez pas à vous en servir
plutôt que de réinventer la roue !
Livraison de votre travail sous Moodle
Quand vous aurez terminé votre exercice, si vous souhaitez faire
remonter des remarques au correcteur de votre travail, créez un
fichier readme.txt dans le répertoire où se trouve votre
travail et notez dans ce fichier vos remarques.
Tapez ensuite la commande : make gentgz
2 cas peuvent se présenter :
- Cas 1 : Cette commande affiche le message "===> OK, fichier arbreAVL_usernameUnix.tgz genere : vous pouvez le remonter dans Moodle."
La génération du fichier d'archive arbreAVL_usernameUnix.tgz s'est bien passée. Il ne vous reste plus qu'à remonter ce fichier dans Moodle.
- Cas 2 : cette commande affiche le message "make:
*** Aucune règle pour fabriquer la cible
« arbreAVL_usernameUnix.c », nécessaire pour
« gentgz ». Arrêt." (et/ou le même message concernant testsUnitaires_usernameUnix.c). Cela signifie que vous avez oublier de taper la commande cp arbreAVL.c arbreAVL_$USER.c (et/ou la commande cp concernant testsUnitaires.c) dans la phase préliminaire de cet exercice. Faites-le(s), puis tapez à nouveau make gentgz
Page mise à jour le 22 mai 2017
|