Université Joseph Fourier Département Licence Sciences & Technologies Rapport de stage Simulation d'algorithmes auto-stabilisants DIAKITE Moussa Laboratoire d'accueil : Verimag Directeur du laboratoire : Nicolas HALBWACHS Responsables du stage : Karine ALTISEN et Stéphane DEVISMES Licence Sciences et Technologies 2ème année - Mathématiques et Informatique Année Universitaire : 2014 2015
Table des matières Remerciements :... 3 Introduction :... 4 Présentation du laboratoire :...4 1- L algorithmique distribuée auto-stabilisante :...5 2- Le modèle à état:...5 3- Le simulateur :...6 4- Quelques algorithmes simulés et étudiés :...7 4.1- L'algorithme de Dijkstra :...7 4.2- L'arbre couvrant en largeur (BFS):...8 4.3- Un exemple de composition d'algorithme :...10 4.4- La l-exclusion :...11 Conclusion :... 11 Annexe... 13
Remerciements : Tout d'abord j'adresse mes remerciements à l'administration de l'université Joseph Fourier pour s'être occupée des démarches administratives relatives à ce stage. J'adresse toute ma reconnaissance à mes encadrants Karine ALTISEN et Stéphane DEVISMES pour leur accueil, leurs conseils et leur disponibilité durant toute la durée de mon stage. Enfin Je tiens à remercier Anaïs Durand, doctorante pour ses conseils et sa disponibilité et le laboratoire Verimag pour son accueil.
Introduction : J'ai toujours eu un intérêt pour les preuves d'algorithmes. Dans cette optique j'ai voulu effectuer un stage qui me permettait d'avoir une première approche sur les preuves d'algorithmes. Ainsi l'objectif de mon stage était la simulation d'algorithmes distribués auto-stabilisants. La simulation des algorithmes me permettait de mieux comprendre leur preuve. Afin de mieux décrire mon travail durant mes 2 mois de travail à Verimag, il me paraît logique une brève présentation de ce laboratoire, ensuite de donner quelques définitions importantes à la compréhension de ces algorithmes, de décrire le simulateur et certains des algorithmes que j 'ai simulés. Enfin je conclurai par les apports que j'ai pu tirer de ce stage. Présentation du laboratoire : Verimag est un laboratoire de pointe dans le domaine des systèmes embarqués situé au centre équation dans le campus universitaire de Grenoble. C'est une unité de recherche commune à l Université Joseph Fourier (UJF - Grenoble 1) au CNRS (INS2I) et à Grenoble-INP Verimag joue un rôle important dans l enseignement à l Université Joseph Fourier et à Grenoble- INP et dans la formation de doctorants. Verimag est organisé en trois équipes : Synchrone : Langages Synchrones et Systèmes Réactifs DCS : Systèmes Répartis et Complexes Tempo : Systèmes Temporisés et Hybrides
1- L algorithmique distribuée auto-stabilisante : Un processus ou nœud est une entité autonome de calcul (ordinateur, capteurs etc...) Un système distribué ou réseau est un ensemble de processus capables d'échanger des informations entre elles à travers des liens de communication Un algorithme distribué est algorithme déployé dans un système distribué. Une configuration est l'ensemble des états des processus à une exécution. Un algorithme distribué auto-stabilisant est un algorithme qui à partir d'une configuration quelconque du système, atteint en un temps fini une configuration où tous les suffixes d'exécutions sont correctes pour la tâche pour laquelle il a été conçu. Un algorithme distribué auto-stabilisant est silencieux s'il atteint une configuration où les variables de communication restent fixes pour toutes exécutions. La topologie d'un réseau est la forme du réseau. Un algorithme auto-stabilisant ne nécessite donc pas d'initialisation du système. Il est donc tolérant aux pannes(perturbations du système). Le réseau est représenté par un graphe où les nœuds sont les sommets du graphe et les liens de communications sont les arcs entre les sommets du graphe. 2- Le modèle à état: C'est un modèle théorique dans lequel la mémoire des processus est localement partagée. Dans un graphe, les canaux de communication désignent la possibilité d'un processus de lire dans la mémoire d'un autre. Les nœuds sont considérés comme des processus concurrents s'exécutant sur une seule unité de traitement. Dans la pratique ce modèle n'est pas utilisable. On utilise donc le modèle à passage de message où les processus peuvent s'envoyer des messages entre eux. Durant mon stage j'ai travaillé principalement sur le modèle à état. Dans ce modèle le programme de chaque processus est représenté par un ensemble fini d'actions ou de règles de la manière suivante : ( <étiquette> :: ) <prédicat> <affectations> L'étiquette permet de nommer une action. Le prédicat ou garde est un booléen sur les variables d'un processus et de ses voisins. Les affectations ou actions modifient les variables des processus. Si le prédicat est vrai, la règle est activable. Une règle est exécutée si et seulement si elle est activable. Un processus est activable s'il a au moins une règle activable. Les processus exécutent simultanément à chaque configuration une de leur règles activables. La configuration est dite terminale si aucun des processus n'est activable. Le démon est un oracle qui sélectionne une partie des processus activables et les exécute tant que la configuration n'est pas terminale. Plusieurs types de démons ont été défini dans la littérature, parmi lesquels :
Le démon central active un seul processus par étape et il n'y a pas de processus voisin activés simulatanément Le démon localement central active au moins un processus par étape. Le démon synchrone active tous les processus à chaque étape. Le démon asynchrone active aléatoirement au moins un processus par étape. On distingue aussi les démons localement central et distribué selon leur équité : Le démon est fortement équitable si un processus activable infiniment souvent finit par être exécuté. Le démon est faiblement équitable si un processus toujours activable finit par être exécuté. Le démon est inéquitable si il n'a aucune restriction à part celles relatives aux démons localement central ou au démon distribué. 3- Le simulateur : Afin de simuler les différents algorithmes distribués j'ai utilisé un simulateur codé en C++. C'est un simulateur du modèle à états. Pour comprendre le simulateur il m'a d'abord fallut comprendre son implémentation. Cela m'a permis d'apprendre le C++, que je ne connaissais pas auparavant. Pour chaque algorithme que l'on souhaite simuler, il faut créer un fichier cpp. Tous les fichiers ont une structure commune basée sur le modèle à état. Un exemple avec l'algorithme de dijkstra donné en annexe. On peut choisir les différents paramètres du réseau(topologie, unidirectionnalité des liens, etc ) dans la fonction main. Voici une figure montrant les différentes parties du simulateur :
4- Quelques algorithmes simulés et étudiés : L'objectif principal de mon stage était de simuler un algorithme distribué complexe. Pour mieux comprendre les algorithmes distribués j'ai d'abord implémenté quelques algorithmes simples : Dans les parties qui suivent on considérera n > 0 processus p 0,..., p n-1. 4.1- L'algorithme de Dijkstra : Spécification : faire circuler un unique jeton dans un anneau orienté. Ici un processus a le jeton s'il est activable. Lorsque l'algorithme stabilise il n'y a plus qu'un seul processus activable. On peut donc se contenter d'un démon distribué inéquitable. Chaque processus p i a accès à la mémoire de son prédécesseur p i-1. Il existe un processus particulier appelé racine. On désigne arbitrairement le processus p 0 comme racine. Les autres processus sont les processus non racine. Tous les processus ont une unique variable v de domaine de définition {0,, K-1} avec K n. Programme de la racine p 0 : vp 0 = vp n-1 vp 0 (vp 0 + 1) mod K Programme des processus p i non racine : vp i vp i-1 vp i vp i-1 4.2- L'arbre couvrant en largeur (BFS): Spécification : calculer un arbre couvrant en largeur enraciné en R d'un réseau. Cet algorithme est utilisable sur toute topologie pourvue qu'elle soit connexe (c'est à dire qu'il existe un chemin entre 2 nœuds du réseau). On utilise pour cet algorithme un démon distribué inéquitable. Les processus ont accès aux variables de leurs voisins, les liens de communication sont donc bidirectionnels. La racine R possède une seule constante appelée d R = 0. Et les autres processus possèdent 2 variables : d p dont le domaine est {1,, D } avec D une borne supérieure sur le diamètre du réseau. père p, un pointeur dont le domaine est N p l'ensemble des voisins. d p représente la distance à R d'un nœud dans l'arbre. Le processus R n'a aucune règle. Les autres processus ont 2 règles : R 1 :: d p - 1 min({d q, q N p } U {D-1} d p min({d q, q N p } U {D-1} + 1 R 2 :: d p - 1 = min({d q, q N p } d p 1 d pèrep pèrep q N p, d q = d p 1 Quelques figures d'arbres couvrants calculés (La racine est en rouge, les flèches rouges pointent vers le père du noeur) :
Une topologie en grille Une topologie en anneau. Dans e cas on voit bien que l'arbre c'est formé en largeur.
4.3- Un exemple de composition d'algorithme : La composition d'algorithmes : On considère deux algorithmes distribués A et B tels qu'aucune variable de B n'existe dans A. Dans la composition de A et B notée B A, on définit le programme principal de chaque processus p, B(p) A(p) comme suit : les variables de B(p) A(p) sont l'union des variables de A(p) et de B(p) B(p) A(p) contient toutes les actions de A(p) pour toute action G i S i de B(p), B(p) A(p) contient l'action C p G i S i où C p est la disjonction de toutes les gardes des actions de A(p). Exemple : Spécification: Supposons u réseau connexe et enraciné en R. Chaque processus dispose en entrée d'une constante Ep; Chaque processus p connaît son père père p dans l'arbre peut tester si un processus q N q est son fils dans l'arbre c'est à dire père q = p ; L'objectif est d'écrire un algorithme auto-stabilisant silencieux sous l'hypothèse d'un distribué faiblement équitable où tous les processus p calculent dans leur variable de sortie Sortie p la valeur maximale parmi l'ensemble des entrées. En configuration terminale on aura Sortie p = max q V {E q }. Implémentation de l'algorithme : L'algorithme est la composition de trois algorithmes : Un algorithme silencieux qui calcule un arbre couvrant enraciné T en R. Dans notre cas ce sera algorithme du BFS présenté précédemment. Un algorithme silencieux qui affecte la variable MaxT p de chaque processus p à max q T(p) {E q }en supposant que T est bien défini par les sorties pères du premier algorithme. Cet algorithme sera appelé MSA qui contient une seule règle par processus : R 3 ::MaxT p max({e p } U {MaxT q, q N p pèreq = p}) MaxTp max({e p } U {MaxT q, q N p pèreq = p}) Un algorithme silencieux qui diffuse la valeur de MaxT R dans les variables Sortie de tous les processus en supposant que la valeur de MaxT R est constante et que T est bien défini par les sorties pères du premier algorithme. Cet algorithme sera appelé MAX et contient une règle pour la racine et une autre pour les autres processus : règle pour la racine R : R 4 ::Sortie R MaxT R Sortie R MaxT R règle pour les autres processus p : R 5 : Sortie p Sortie pèrep Sortie p Sortie pèrep Pour obtenir l'algorithme on compose d'abord BFS et MSA suivant les règles de composition et on obtient MSA BFS. Puis on réitère l'action de composition avec cette fois ci MAX et MSA BFS.
On obtient alors MAX MSA BFS 4.4- La l-exclusion : C'est l'algorithme complexe que je devais implémenter dans le cadre de mon stage. En fait ce n'est pas un seul algorithmes mais plusieurs algorithmes composés entre eux. L'algorithme de la l-exclusion étant la composition de plusieurs algorithmes, je m'abstiens de donner les règles et j'explique le fonctionnement global de l'algorithme. Si on souhaite partager une ressource pour plusieurs processus par exemple une imprimante, on peut utiliser le premier algorithme de Dijkstra car celui-ci permet de faire circuler un jeton dans un anneau orienté et donc de donner la main à chaque processus. La l-exclusion elle, permet de partager l ressources entre n processus. Dans un premier temps j'ai implémenté l'algorithme pour un anneau orienté et ensuite pour une topologie quelconque. Dans le cas de la topologie quelconque on se sert d'un arbre couvrant. Conclusion : A travers les différentes tâches que j'ai effectuées j'ai appris un nouveau langage de programmation, le C++, et j'ai encore enrichi mon expérience du monde de la recherche. J'ai également appris à travailler de manière rigoureuse, et organisée. Ce stage m'as aussi permis d'approfondir ma connaissance des algorithmes distribués à travers l'étude de leur preuve. J'ai apprécié la dimension mathématique tout en restant proche de l'informatique. J'ai pu lire plusieurs articles écrits par des chercheurs et me faire une idée de la rédaction d'un article scientifique. Ce fut une expérience professionnelle très intéressante et je remercie l'université Joseph Fourier de m'avoir accorder cette chance de pouvoir mettre en pratique les enseignements théoriques reçus.
Annexe
fichier c++ implémentant l'algorithme de dijkstra :