Multi-Processing Template Library

Dimension: px
Commencer à balayer dès la page:

Download "Multi-Processing Template Library"

Transcription

1 Travail de master Multi-Processing Template Library Didier Baertschiger sous la direction de Bastien Chopard et Jonas Latt 2006 Université de Genève

2

3 Table des matières Préface vii 1 Notions importantes du C++ et aperçu de la STL Fonctionnalités du C Templates Espaces de nommage (namespaces) Vue d ensemble de la STL Conteneurs Itérateurs Algorithmes Résumé Utilisation de la MPTL Fonctions utilisateurs Nombre de threads Exécution d un algorithme Parallélisation des algorithmes de la STL Limitations de la MPTL Liste des algorithmes parallélisés Exemples d utilisation Construction d un nouvel algorithme CSPA Création d une classe de réduction Fonction de convenance

4 iv Table des matières Exemples complets Récapitulation Implémentation de la MPTL Modèle de programmation Modèle retenu pour la MPTL Autres possibilités Eléments essentiels de la programmation Exécution d un algorithme Opérations multithreads Fichiers sources de la MPTL Thread-safety Résultats Conditions des tests Algorithmes de la STL Ensembles de Julia Distribution statique des charges Distribution dynamique des charges Tests supplémentaires Equation de Laplace Enoncé du problème Description de l algorithme Résultats des tests Conclusion 119 A Quicksort 121 A.1 Description de l algorithme retenu A.2 Utilisation de l algorithme A.3 Résultats Ressources Internet 127

5 Table des matières v Bibliographie 129

6

7 Préface La MPTL (Multi-Processing Template Library) a été créée dans le cadre d un travail de master. Ce document est le rapport final de ce travail. La MPTL est une librairie qui permet d exécuter des algorithmes en parallèle sur des architectures à mémoire partagée. Ainsi, le but de son utilisation est de diminuer le temps d exécution des algorithmes parallélisés. Initialement, le projet de la MPTL est né dans le but de trouver un moyen efficace de paralléliser les algorithmes d une autre librairie, à savoir la STL. Pour faire simple, la STL est une librairie standard du C++ composée de trois éléments principaux : les conteneurs, les algorithmes et les itérateurs. Les conteneurs sont des structures de données telles que des tableaux dynamiques, des listes chaînées ou des piles. Les algorithmes sont des fonctions qui permettent de manipuler facilement ces conteneurs. Ils sont par exemple capables de rechercher des éléments, de trier ou de supprimer certains éléments contenus dans les conteneurs. Les algorithmes utilisent des itérateurs pour accéder aux éléments contenus dans les conteneurs. Les itérateurs sont pour les conteneurs ce que sont les pointeurs pour les tableaux ordinaires du C ou du C++. Pour en revenir à la MPTL, son intérêt était donc de parvenir à paralléliser les algorithmes de la STL, afin qu ils s exécutent plus rapidement sur des machines multiprocesseurs à mémoire partagée. Pour réussir à paralléliser ces algorithmes, un nouveau modèle de programmation parallèle a été conçu. Ainsi, non seulement la MPTL permet de paralléliser facilement les algorithmes de la STL, mais elle permet également aux développeurs de paralléliser ses propres algorithmes qui utilisent des itérateurs comme ceux de la STL. La parallélisation des algorithmes est basée sur l utilisation du multithreading. Ainsi, la MPTL offre des gains de temps non seulement sur des machines multiprocesseurs à mémoire partagée, mais également sur des machines monoprocesseurs exploitant l hyperthreading. Le modèle de programmation parallèle défini par le MPTL est basé sur la notion d itérateur. Les algorithmes parallélisés s appliquent généralement sur des

8 viii Préface intervalles d éléments dont les limites sont définies par des itérateurs. La parallélisation s effectue alors en découpant ces intervalles en sous-intervalles. Ces derniers sont alors distribués à différents threads qui s occupent ensuite d exécuter dessus l algorithme de façon concurrente. Pour les connaisseurs, ce comportement est très proche de ce que l on obtiendrait en utilisant le standard OpenMP pour paralléliser une boucle for qui parcourt par exemple un tableau pour appliquer à chaque élément une série d opérations. Structure de ce document Voici une courte description des différentes parties que nous allons aborder dans ce document : Chapitre 1 : Notions importantes du C++ et aperçu de la STL La compréhension de la MPTL nécessite certaines connaissances spécifiques du C++ ainsi qu une bonne vision sur l utilisation de la STL. Ce chapitre nous proposera donc d étudier ces différentes fonctionnalités du C++ avec ensuite une introduction sur la STL. Chapitre 2 : Utilisation de la MPTL Nous apprendrons à utiliser la MPTL afin de paralléliser des algorithmes. Dans un premier temps, nous découvrirons la MPTL dans le cadre de la STL. Nous verrons ainsi de quelle manière cette librairie permet de paralléliser les algorithmes de la STL. Par la suite, nous étudierons les possibilités qu offre la MPTL pour paralléliser ses propres algorithmes. Chaptire 3 : Implémentation de la MPTL Cette partie propose une explication sur le fonctionnement interne de la MPTL. Nous étudierons donc sous un angle différent la façon utilisée par la librairie pour paralléliser les algorithmes. Chapitre 4 : Résultats Nous analyserons dans ce chapitre les performances offertes par la librairie. Nous effectuerons une série de tests pour constater le gain de temps que la MPTL est susceptible d apporter. Nous comparerons également les résultats obtenus avec ceux que l on obtient en utilisant OpenMP. Ces comparaisons sont très significatives, car la MPTL utilise très souvent une technique identique à celle qu un utilisateur emploierait pour paralléliser un algorithme avec OpenMP. Une partie annexe sera également proposée sur un algorithme de tri parallèle. Cet algorithme ne fait pas réellement partie de la MPTL, car son fonctionnement

9 est différent des autres algorithmes proposés par la librairie. L importance de cet algorithme fait qu une implémentation indépendante a été ajoutée à la librairie. Son utilisation sera alors décrite et nous étudierons également ses performances. ix

10

11 Chapitre 1 Notions importantes du C++ et aperçu de la STL Dans ce chapitre, nous allons étudier les notions élémentaires pour la compréhension de la MPTL. Il s agit simplement d un préparatif au chapitre suivant. La MPTL est une librairie basée sur la STL. Nous allons donc voir dans ce chapitre un aperçu de cette librairie. Avant cela, nous étudierons dans un premier temps plusieurs fonctionnalités importantes du C++ qui permettent de bien comprendre de quelle manière fonctionne la STL et par conséquent la MPTL. A la fin de cette partie, les notions de template et de namespace devraient être bien comprises. Nous devrions avoir ensuite les connaissances suffisantes sur le fonctionnement de la STL pour aborder le chapitre suivant. 1.1 Fonctionnalités du C++ Brièvement, la STL est une librairie qui offre la possibilité de stocker des collections de données (sous forme de tableaux dynamiques, de listes, de piles,...) et de manipuler leur contenu à l aide d algorithmes. La force de la STL est sa capacité de fonctionner avec tous les types de données. Il est tout à fait possible de travailler sur des entiers tout comme sur un type défini par l utilisateur. Pour offrir cette souplesse tout en conservant une librairie de taille relativement petite, il est nécessaire qu elle soit écrite de manière générique. Le code de la STL ne doit pas spécifier un ou plusieurs types précis pour les algorithmes ou pour la gestion des collections de données. Sans cela, il ne serait plus possible d utiliser la STL avec ses propres types. La possibilité d écrire du code générique en C++ est offerte grâce aux templates. Toute la STL est totalement basée sur la notion de template. Sans elle, il

12 2 Chapitre 1. Notions importantes du C++ et aperçu de la STL aurait été impossible d écrire cette librairie Templates Les templates 1 permettent de créer des fonctions et des classes génériques (templates). Le terme générique signifie que le type de données avec lequel la fonction ou la classe va fonctionner sera spécifié comme un paramètre. Il est donc tout à fait possible d écrire une seule fonction pour plusieurs types de données. Si on désire par exemple écrire une fonction qui recherche l élément le plus petit d un tableau, il n est plus nécessaire d écrire une fonction pour chaque type de données avec lequel on est susceptible d utiliser cette fonction. Grâce aux templates, il suffit d écrire une fonction générique qui fonctionnera avec tous les types. La généricité d une telle fonction repose sur son indépendance par rapport au type. Le mécanisme de template en C++ offre une programmation générique purement statique, car l ensemble des résolutions de types s effectue durant la compilation du programme. Bien que la programmation orientée objet offre aussi une notion de généricité avec le polymorphisme, elle ne peut pas être comparée à la programmation générique. Dans le cas du polymorphisme, la détermination du type réel d un argument est uniquement effectuée pendant l exécution. Fonctions template Le plus simple est de commencer directement avec un petit exemple de fonction template. Voici une fonction qui retourne la valeur minimale entre deux valeurs : template <class T> T minimum(t a, T b) { if (a < b) return a; return b; // a est plus petit // b est plus petit Deux mots clés sont importants dans ce code : template et class. Le premier indique qu il s agit d une fonction template. Le second permet de spécifier un type générique qui va être utilisé dans la fonction. Dans notre exemple, il s agit du type T. Le nom du type est totalement arbitraire, même si T est devenu très usuel. Ainsi, template <class T> 1 Qui peut être traduit par modèle en français.

13 1.1. Fonctionnalités du C++ 3 indique que la fonction qui va suivre est une fonction template qui va utiliser un type générique appelé T. Il existe un autre mot clé du C++ totalement équivalent à class pour définir un type. Il s agit du mot clé typename. L en-tête template <typename T> aurait donc été totalement équivalente. Nous verrons plus tard que typename a également une autre utilité. Revenons à notre exemple, la ligne suivante définit la signature de la fonction : T minimum(t a, T b) Cette signature indique qu une valeur de type T va être retournée et que la fonction prend deux paramètres a et b qui sont tous les deux du type T. Le corps de la fonction est écrit comme s il s agissait d une fonction classique. Bien entendu, le type T est toujours défini dans le corps. Il serait donc tout à fait possible de créer une nouvelle variable de ce type. L utilisation de cette fonction template serait très simple et tout à fait standard. Voici un court exemple : int a1 = 12, a2 = 4; double d1 = , d2 = 753.2; int a = minimum(a1, a2); double d = minimum(d1, d2); // a vaut 4. T = int // d vaut T = double L utilisation de minimum() s effectue comme s il s agissait d une fonction nongénérique. Il n y a même pas besoin de spécifier le type que l on désire utiliser. Essayons de comprendre dans les grandes étapes ce qu effectue le compilateur pour permettre l utilisation d une telle fonction : 1. Il détecte qu une fonction template minimum() existe. Elle utilise un type générique T. 2. Il arrive à l instruction : int a = minimum(a1, a2); Il sait que a, a1 et a2 sont de type int. Du coup, il peut générer sans la moindre ambiguïté une fonction minimum() réellement faite pour le type int : où T signifie int. Il va donc insérer directement dans le code généré une fonction du type : int minimum(int a, int b) { if (a < b) return a; return b;

14 4 Chapitre 1. Notions importantes du C++ et aperçu de la STL 3. Il arrive finalement à la même instruction, mais cette fois-ci pour le type double étant donné que d, d1 et d2 sont des doubles. Il va donc générer un code du genre : double minimum(double a, double b) { if (a < b) return a; return b; Le code généré par le compilateur ne contient donc plus de fonction template, mais uniquement des fonctions prêtent à être directement utilisées pour un type précis de données. Lors de l exécution du programme, tous les types sont totalement définis. Ce type de programmation à savoir la programmation générique assure donc des performances équivalentes à celle que l on obtiendrait en programmant sans les templates (à la fois les fonctions et les classes). Comme si nous avions directement programmé, dans notre exemple, les deux fonctions minimum() pour les types int et double. Notre fonction minimum() utilise qu un seul type générique. Mais il est tout à fait possible d en utiliser plusieurs. La fonction suivante utilise par exemple trois types génériques : T1, T2 et T3. template <class T1, class T2, class T3> void disp(t1 a1, T2 a2, T3 a3) { cout << a1 << " - " << a2 << " - " << a3 << endl; Cette fonction reçoit trois paramètres de types éventuellement différents et les affiche. Nous pourrions appeler cette fonction des manières qui suivent : disp(12, 13.4, "essai"); disp("bonjour", a, ); disp(1, 10, 100); // T1 = int, T2 = float, T3 = char* // T1 = char*, T2 = char, T3 = float // T1 = int, T2 = int, T3 = int Le dernier exemple montre qu il est possible de transmettre des paramètres de types identiques et ce, même si la fonction template est prévue pour recevoir trois paramètres de types différents.

15 1.1. Fonctionnalités du C++ 5 Classes template Le C++ ne permet pas uniquement de créer des fonctions template, mais également des classes template. Le principe est très proche. Il suffit de spécifier avant la déclaration de la classe les types génériques que l on désire utiliser dans le corps de la classe. On utilise à nouveau les mots clés template et class (ou typename). Une classe pourrait donc être déclarée ainsi : template <class T> class MyClass { // Corps de la classe : le type T peut-être utilisé dans les fonctions // membres, le constructeur et le destructeur. Il peut également être // utilisé pour définir des variables membres.... Par rapport aux fonctions template, il y a tout de même deux différences importantes : Lorsque l on désire instancier une classe template, il est obligatoire de spécifier le ou les types génériques : MyClass<int> c1; MyClass<char> c2; // T = int // T = char Contrairement à ce que nous avons discuté avec les fonctions template, le compilateur n est pas capable de déduire sans ambiguïté les types génériques d une classe template. Il est donc nécessaire de les lui spécifier. Il est possible de définir des types génériques par défaut. template <class T = int> class MyClass2; Dans ce cas, il est possible d instancier un objet MyClass2 sans lui spécifier le type : MyClass2<> c1; MyClass2<double> c2; // Utilise le type int par défaut // Utilise des doubles à la place des entiers Le mot clé typename Nous avons vu auparavant que le mot clé typename était équivalent à class pour définir un type générique dans le cadre d une fonction ou d une classe template.

16 6 Chapitre 1. Notions importantes du C++ et aperçu de la STL template <typename T> void f(t a1, T a2); Cet exemple revient à donner la signature d une fonction template f() qui utilise un type générique nommé T de la même manière que si nous avions utilisé class. Maintenant, typename a une toute autre fonctionnalité : il peut être utilisé pour spécifier qu un identifieur est un type. Pour bien comprendre, considérons dans un premier temps la classe suivante : class MyClass1 { public: typedef double ValType;... Cette classe définit le type ValType comme un double. Il est tout à fait possible de récupérer, à l extérieur de la classe, ce type avec MyClass1::ValType. Considérons maintenant une classe template qui utilise justement le type ValType : template <class T> class MyClass2 { typename T::ValType * ptr;... Il s agit d une classe template qui doit nécessairement recevoir en paramètre une classe qui définit, à l aide de typedef, le type ValType. Il est donc tout à fait possible de transmettre MyClass1 à MyClass2 avec : MyClass2<MyClass1> c; Dans ce cas, le variable ptr sera un pointeur d un double. Maintenant nous devons comprendre pourquoi il a été nécessaire d utiliser typename pour définir ptr. Pourquoi nous n avons pas simplement pu écrire : T::ValType * ptr; En écrivant typename juste avant T::ValType, on spécifie au compilateur qu il s agit d un type. Ainsi, le compilateur sait que ValType correspond à un type définit dans T. Sans typename, il y aurait eu une ambiguïté possible. En effet, ValType aurait pu être considéré comme un membre statique de T. Du coup, l instruction

17 1.1. Fonctionnalités du C++ 7 T::ValType * ptr; pourrait être vue comme la multiplication entre le membre statique ValType de T et ptr Espaces de nommage (namespaces) Nous allons maintenant discuter une fonctionnalité relativement récente du C++, à savoir les espaces de nommage (namespaces). Dans le cadre de notre projet, cette fonctionnalité est moins importante que les templates, mais elle offre tout de même un avantage certain. L un des buts majeur de la MPTL est d être le plus proche que possible de la STL au niveau de son utilisation. Comme nous l avons déjà entrevu auparavant, la STL propose des algorithmes à appliquer sur des collections de données. La MPTL exécute simplement ces algorithmes de manière différente pour les paralléliser. Maintenant, il y a un grand intérêt à ce que les algorithmes de la MPTL portent les mêmes noms que ceux de la STL. Par exemple, l algorithme de la STL count() qui s occupe de compter le nombre d éléments présents dans une collection doit également s appeler count() dans la MPTL, afin de simplifier au maximum son utilisation. Ainsi, si un développeur a déjà écrit un code qui utilise la STL et qu il désire le paralléliser avec la MPTL, il serait très pratique que tous les algorithmes ne changent pas de nom. Ce choix fait pourtant apparaître un problème. Si les algorithmes de la MPTL ont le même nom et la même signature que les algorithmes de la STL, il peut y avoir des problèmes de conflits. D autant plus qu un utilisateur peut utiliser certains algorithmes de la STL et d autres de la MPTL dans le même programme. Les namespaces sont la solution à ce problème de conflit. Un namespace délimite la recherche des noms des identificateurs. Il groupe les identificateurs dans un bloc nommé. La création d un espace de nom s effectue en utilisant le mot clé namespace. Il faut également spécifier son nom et définir sa portée avec des accolades. namespace test { class MyClass {... void func() {... // Fin du namespace test Pour désigner ensuite précisément un identificateur déclaré dans un namespace, il faut utiliser l opérateur :: de cette manière :

18 8 Chapitre 1. Notions importantes du C++ et aperçu de la STL test::myclass c; test::func(); Regardons maintenant un exemple concret où un problème de conflit pourrait apparaître. Nous allons créer deux fichiers qui contiennent tous les deux une fonction nommée message. // Fichier myfile1.h #include <iostream> using namespace std; namespace space1 { void message(char * msg) { // Affiche myfile1 : et le message reçu en paramètre cout << "myfile1 : " << msg << endl; // Fichier myfile2.cpp #include <iostream> #include "myfile1.h" using namespace std; // On inclut le fichier myfile1.h namespace space2 { void message(char * msg) { // Affiche myfile2 : et le message reçu en paramètre cout << "myfile2 : " << msg << endl; int main() { // Appel de msg() du fichier myfile1.h space1::message("bonjour"); // Appel de msg() du fichier myfile2.cpp space2::message("au revoir!");

19 1.2. Vue d ensemble de la STL 9 myfile1 : Bonjour myfile2 : Au revoir! Cet exemple fait appel à la directive using : using namespace std; Cette directive permet de charger les identificateurs déclaré dans un namespace donné dans la portée courante. Cette fonctionnalité permet d utiliser, si aucun conflit n est présent, les identificateurs d un namespace sans avoir besoin de spécifier à chaque fois le nom du namespace. Dans notre exemple, nous chargeons le namespace std qui correspond au namespace employé dans la librairie standard du C++. Cet appel nous permet d utiliser cout pour l affichage des messages sur la sortie standard. Si nous n avions pas utilisé la directive using pour le namespace std, nous aurions dû écrire : std::cout << "myfile1 : " << msg << std::endl; Comme la STL fait également partie de la librairie standard du C++, tous ses éléments sont déclarés dans le namespace std. 1.2 Vue d ensemble de la STL La STL (Standard Template Library) est issue de l effort de standardisation du C++. Elle est souvent considérée comme l un des enrichissements les plus importants du C++. Il s agit d une librairie générique qui offre des solutions pour gérer des collections de données avec des algorithmes. Son implémentation est essentiellement basée sur les templates. Pour un programmeur, la STL offre un niveau d abstraction supérieur. Elle fournit un framework qui lui permet d oublier la programmation de tableaux dynamiques, de piles ou autres structures de données communes ainsi que la majorité des algorithmes classiques. La construction générique de la STL permet d utiliser ses collections ainsi que ses algorithmes sur tous les types de données. La STL est basée sur la coopération de trois composants : les conteneurs qui sont utilisés pour organiser des collections d objets d un type donné tels que les vecteurs, les piles ou les listes chaînées. les algorithmes qui permettent de manipuler le contenu des conteneurs. Ils peuvent par exemple trier, rechercher ou modifier le contenu. les itérateurs qui jouent le rôle de pointeur. Ils donnent la possibilités d accéder aux éléments d un conteneur.

20 10 Chapitre 1. Notions importantes du C++ et aperçu de la STL Le principe de la STL est de séparer les données (conteneurs) des opérations (algorithmes). Les itérateurs permettent d effectuer la liaison entre les données et les opérations (cf. Fig. 1.1). Conteneur Itérateur Algorithmes Itérateur Conteneur Conteneur Itérateur Fig. 1.1 Les différents composants STL. Nous allons maintenant décrire plus en détails ces différents composants et surtout de quelle manière ils peuvent s utiliser Conteneurs La STL inclut des classes conteneur, dont le but est de contenir d autres objets. Le Tab. 1.1 donne la liste des différents conteneurs inclus dans la STL. Conteneur deque list map multimap set multiset vector Description File à double entrée (double-ended queue) Liste linéaire. Double liste chaînée Contient des éléments qui sont des paires clé/valeur où chaque clé doit être unique Identique à map, mais une clé peut apparaître plusieurs fois Collection ordonnée en fonction de la valeur de chaque élément. Chaque élément doit apparaître qu une seule fois Identique à set, mais les éléments peuvent apparaître plusieurs fois Tableau dynamique Tab. 1.1 Liste des conteneurs de la STL. Il existe encore la classe string qui peut être considérée comme une classe conteneur. Cette classe permet de stocker des chaînes de caractères. Bien qu elle

21 1.2. Vue d ensemble de la STL 11 réponde à toutes les contraintes imposées à une classe conteneur de la STL, la classe string ne fait pas partie de la STL, mais elle est tout de même définie dans la bibliothèque standard du C++. Du fait que son implémentation est identique à celle d un conteneur STL, il est tout à fait possible d utiliser les algorithmes de la STL avec des objets string. Les tableaux ordinaires, souvent appelés C-arrays, peuvent aussi être utilisés comme des conteneurs de la STL. Dans ce cas, les pointeurs jouent le rôle des itérateurs. Les conteneurs peuvent être trié en deux catégories : Les conteneurs séquences qui sont des collections ordonnées où chaque élément possède une certaine position. Cette position dépend uniquement du moment de l insertion et est totalement indépendante de la valeur de l élément. Les classes deque, list et vector sont dans cette catégorie. Les conteneurs associatifs qui sont des collections ordonnées où la position d un élément dépend de sa valeur selon un critère de tri. La STL fournit les conteneurs associatifs suivants : map, multimap, set et multiset. Utilisation standard des conteneurs L utilisation des conteneurs suit généralement un schéma assez classique. Dans un premier temps, il faut choisir le type de conteneur que l on désire utiliser. Ce choix dépend du type de données que l on désire stocker et surtout de quelle manière elles doivent être arrangées pour les futurs accès. Une fois ce choix effectué, il faut prendre connaissance des techniques mises à disposition pour ajouter, modifier ou supprimer des éléments. Les conteneurs adaptent automatiquement leur taille en fonction du nombre d éléments qu ils contiennent. Il y a juste bitset qui fait exception à cette règle. Les conteneurs séquentiels et les conteneurs associatifs possèdent les méthodes insert() et erase() pour respectivement insérer et supprimer un élément. Les conteneurs séquentiels possèdent en plus les méthodes push_back() pour ajouter un élément à la fin et push_front() pour ajouter un élément au début du conteneur. Il est possible d effectuer l opération inverse (suppression d un élément) en faisant appel aux fonction pop_back() et pop_front(). Pour accéder ensuite au contenu d un conteneur, il suffit généralement d utiliser les fonctions membres begin() et end(). La première fonction retourne un itérateur sur le premier élément du conteneur, tandis que la seconde retourne un itérateur sur la fin du conteneur. Les conteneurs associatifs disposent également de la fonction membre find() pour accéder à un élément en fonction de sa clé.

22 12 Chapitre 1. Notions importantes du C++ et aperçu de la STL Nous allons maintenant étudier un exemple concret sur l utilisation du conteneur vector. Vecteurs La classe vector est probablement la classe conteneur la plus employée. Cette classe gère ses éléments dans un tableau dynamique. Ce qui signifie que la taille du tableau peut varier durant l exécution du programme. Ainsi, le programmeur n a pas besoin de connaître à l avance la taille du tableau durant sa création contrairement aux tableaux ordinaires du C ou du C++. Un vecteur permet d accéder à ses éléments de manière aléatoire en employant, si on le désire, la notation classique des tableaux. On dénote par aléatoire, la possibilité de pouvoir accéder à n importe quel élément du conteneur à tout moment, comme c est le cas avec les tableaux standards. Le programme suivant donne un petit aperçu sur l utilisation d un vecteur. Ce programme crée dans un premier temps un vecteur vide et lui ajoute ensuite quelques valeurs. Le contenu du vecteur est finalement affiché. #include <iostream> #include <vector> using namespace std; int main() { vector<int> v; // Vecteur vide pour des entiers v.push_back(5); // Ajoute 5, 2 et 3 dans le vecteur v.push_back(2); v.push_back(3); // Affiche le contenu du vecteur for (int i = 0; i < v.size(); ++i) cout << v[i] << ; cout << endl; // Saut de ligne Pour pouvoir utiliser les vecteurs, il faut dans un premier temps inclure le fichier d en-tête correspondant :

23 1.2. Vue d ensemble de la STL 13 #include <vector> Le moyen le plus simple ensuite pour instancier un vecteur est d utiliser l instruction vector<int> v; Le vecteur v est créé pour contenir des éléments de type int. Pour l instant ce vecteur ne contient aucune valeur et sa dimension vaut 0. L appel à la fonction membre push_back() permet d ajouter un élément à la fin du vecteur. Le vecteur adapte sa taille automatiquement pour contenir ce nouvel élément. La fonction membre size() permet de connaître la taille du vecteur. Ainsi, la boucle suivante permet de parcourir tous les éléments du vecteur for (int i = 0; i < v.size(); ++i)... Comme on peut le constater dans l instruction cout << v[i] << ; il est tout à fait possible d accéder à un élément du vecteur comme s il s agissait d un simple tableau d entiers Itérateurs Les itérateurs sont des objets qui permettent de parcourir et d accéder aux éléments d un conteneur. D une manière générale, les itérateurs sont aux conteneurs ce que sont les pointeurs pour les C-arrays. De la même manière que les pointeurs, les itérateurs satisfont les opérations suivantes : operator* : retourne l élément «pointé» par l itérateur. operator= : spécifie la position de l itérateur. operator++ : déplace l itérateur sur l élément suivant du conteneur. operator== : teste si deux itérateurs se trouvent à la même position. operator!= : teste si deux itérateurs se trouvent à des positions différentes. Un certain nombre d itérateurs satisfont aussi l opérateur -- pour le déplacer sur l élément précédent du conteneur. Tous les conteneurs possèdent un moyen (une fonction membre) pour obtenir un itérateur afin de pouvoir accéder à son contenu. Les fonctions les plus employées sont certainement begin() et end(). begin() : retourne un itérateur sur le premier élément du conteneur.

24 14 Chapitre 1. Notions importantes du C++ et aperçu de la STL end() : retourne un itérateur qui représente la fin du conteneur. Cet élément n est pas le dernier du conteneur, mais l élément qui se trouve juste après. Les deux itérateurs retournés par ces fonctions forment un intervalle semiouvert du type [first, last) où first est l itérateur retourné par begin() et last par end(). Pour nous entraîner à utiliser ces deux fonctions, modifions l exemple de la page 12 en utilisant begin() et end() pour parcourir le vecteur lors de son affichage. #include <iostream> #include <vector> using namespace std; int main() { // Création d un vecteur vide pour des entiers vector<int> v; // Ajoute les entiers 5, 2 et 3 dans le vecteur v.push_back(5); v.push_back(2); v.push_back(3); // Création d un itérateur pour un vecteur d entiers vector<int>::iterator it; // Parcours le conteneur en utilisant l itérateur // On utilise cette fois begin() et end() for (it = v.begin(); it!= v.end(); ++it) cout << *it << ; cout << endl; // Saut de ligne Le premier point important de ce programme par rapport à la version précédente est la déclaration de l itérateur it. vector<int>::iterator it; Le type de l itérateur se trouve dans la classe vector directement (où il est défini à l aide d un typedef).

25 1.2. Vue d ensemble de la STL 15 Le parcours du vecteur s effectue à l aide de la boucle suivante : for (it = v.begin(); it!= v.end(); ++it)... L intervalle à parcourir est délimité par l utilisation des fonctions membres begin() et end(). Le parcours du conteneur est ensuite assuré par l opérateur ++. Il serait possible d utiliser également la syntaxe postfixée : it++. Mais cette solution est généralement plus lente, car un itérateur temporaire doit être créé pour pouvoir retourner l ancienne position de l itérateur : ++it : retourne simplement l itérateur it après avoir été incrémenté ; it++ : copie l itérateur dans une variable temporaire, incrémente it et retourne l itérateur contenu dans la variable temporaire. Il est donc toujours préférable d utiliser la version préfixée pour incrémenter un itérateur. Le test it!= v.end() nous montre bien pourquoi la fonction membre end() retourne l élément qui se trouve juste après le dernier du conteneur. En effet, si end() retournait un itérateur sur le dernier élément, le test ne pourrait plus être aussi simple et direct. Dans notre exemple, l itérateur retourné par vector<int>::iterator permet un accès aléatoire aux éléments du vecteur. Cet itérateur donne ensuite la possibilité de lire et de modifier l élément pointé. La classe vector permet d obtenir un autre type d itérateur qui ne permet pas de modifier ses éléments : vector<int>::const_iterator cit; *cit = 12; // cit est juste capable d itérer en // mode lecture // ERREUR : cit n est pas en mode // écriture! D une manière générale, chaque conteneur de la STL est capable de fournir deux types d itérateurs : container ::iterator : permet un accès aux éléments en mode lecture et écriture ; container ::const_iterator : permet un accès aux éléments uniquement en mode lecture. Catégories des itérateurs Nous avons vu qu un itérateur permettait de parcourir un conteneur en garantissant un certain comportement par rapport à un certain nombre d opérations (*, ++, =, ==,!=). Maintenant, il existe plusieurs sortes d itérateurs qui possèdent des

26 16 Chapitre 1. Notions importantes du C++ et aperçu de la STL capacités différentes. Il est par exemple évident qu un itérateur associé à une pile ne permet pas un accès aléatoire aux éléments, contrairement à l itérateur d un vecteur. Cette différence est aussi importante lorsqu on utilise les algorithmes de la STL. En effet, certains algorithmes sont uniquement efficaces s ils reçoivent en paramètre des itérateurs à accès aléatoire. Le tri est un bon exemple d algorithme de ce genre. La STL propose cinq catégories d itérateurs qui suivent un ordre hiérarchique comme le montre la Fig Ainsi, un itérateur de type bidirectional iterator est également un forward iterator. Si un algorithme nécessite par exemple un forward iterator, il est tout à fait possible de lui transmettre un bidirectional iterator ou un random access iterator. Par contre, l algorithme ne va pas fonctionner si on lui transmet uniquement un input iterator. Input iterator Output iterator Forward iterator Bidirectional iterator Random access iterator Fig. 1.2 Catégories des itérateurs. Le Tab. 1.2 donne les capacités de chaque catégorie d itérateur ainsi que leur possibilité de déplacement ou d accès dans le conteneur. L itérateur que nous avions utilisé pour accéder à un vecteur en appelant vector<int>::iterator appartient à la catégorie random access iterator. Si nous avions par exemple utilisé une liste au lieu d un vecteur, nous aurions déclaré un itérateur avec : list<int>::iterator itlist;

27 1.2. Vue d ensemble de la STL 17 Catégorie d itérateur Input iterator Output iterator Forward iterator Bidirectional iterator Random access iterator Capacité Lecture et déplacement avant Ecriture et déplacement avant Lecture et écriture et déplacement avant Lecture et écriture et déplacement avant et arrière Lecture et écriture et accès aléatoire Tab. 1.2 Description des différentes catégories d itérateurs. Dans ce cas, étant donné qu il s agit d une double liste chaînée, l itérateur itlist aurait appartenu à la catégorie bidirectional iterator. Nous verrons dans le chapitre 2, sur l utilisation de la MPTL, que la catégorie d un itérateur joue un rôle important dans la parallélisation d un algorithme. Fonctions pratiques La STL propose deux fonctions utiles pour la gestion des itérateurs. Il s agit des fonctions distance() et advance(). Ces deux fonctions sont des fonctions template. Dist distance(inputiterator first, InputIterator last) : retourne la distance qui sépare first et last. void advance(inputiterator& it, Distance n) : permet de faire avancer l itérateur it d une distance n. L utilisation de ces fonctions est avantageuse dans la mesure où elles vont ellesmêmes s occuper d appliquer la manière la plus optimale pour effectuer leur travail. Prenons le cas par exemple de advance(). La manière de faire avancer un itérateur d une certaine distance dépend de la catégorie de l itérateur. S il s agit d un random access iterator, il suffit d employer l opérateur + : it = it + 5; Dans ce cas, l itérateur it va avancer de cinq éléments. Par contre, si it était de type forward iterator, il faudrait effectuer une boucle pour effectuer le même travail : for (int i = 0; i < 5; ++i) ++it; On remarque donc que la manière de coder ce déplacement dépend de la catégorie. En utilisant les fonctions distance() et advance(), il n y a plus besoin de se

28 18 Chapitre 1. Notions importantes du C++ et aperçu de la STL préoccuper de cette différence Algorithmes Comme nous l avons expliqué auparavant, les algorithmes sont des outils pour agir sur les conteneurs par le biais des itérateurs. Les conteneurs possèdent déjà un certain nombre de fonctions membres qui permettent d agir sur leur contenu. Cependant les algorithmes offrent encore plus de possibilités avec des actions plus complexes. L utilisation des algorithmes dans un programme nécessite l en-tête #include <algorithm> Tous les algorithmes sont des fonctions template pour conserver l aspect générique de la STL. Il est donc possible d employer tous les algorithmes avec tous les conteneurs. Les implémentations actuelles de la STL proposent près de 80 algorithmes. Le Tab. 1.3 donne la description de quelques algorithmes représentatifs. Pour se faire une petite idée sur l utilisation des algorithmes de la STL, étudions un petit exemple qui fait appel à plusieurs algorithmes : #include <iostream> #include <vector> #include <algorithm> using namespace std; // Fonction pour afficher un intervalle donné [first, last). // Affiche avant le message spécifié. template <typename Iterator> void printcontainer(iterator first, Iterator last, char* msg = "") { cout << msg; for ( ; first!= last; ++first) cout << *first << ; cout << endl; int main() { // Initialise un vecteur d entiers de taille 5 vector<int> v(5); // Initialise un itérateur pour le vecteur vector<int>::iterator it;

29 1.2. Vue d ensemble de la STL 19 Algorithme adjacent_find copy count equal fill find for_each generate max_element merge min_element remove replace search sort transform Description Recherche le plus proche élément correspondant, et retourne un itérateur sur le premier élément trouvé. Copie une séquence ordonnée. Retourne le nombre d éléments d un intervalle. Détermine si deux intervalles sont identiques. Remplit un intervalle avec la valeur spécifiée. Recherche une valeur dans un intervalle, et retourne un itérateur sur la première occurrence trouvée. Applique la fonction à un intervalle d éléments. Affecte aux éléments d un intervalle les valeurs retournées par une fonction de génération. Retourne un itérateur sur le plus grand élément d un intervalle. Fusionne deux séquences ordonnées, et place le résultat dans une troisième. Retourne un itérateur sur le plus petit élément d un intervalle. Supprime les éléments de l intervalle spécifié. Remplace les éléments dans un intervalle. Recherche une sous-séquence au sein d une séquence. Trie un intervalle. Applique une fonction aux éléments d un intervalle, et stocke le résultat obtenu dans une nouvelle séquence. Tab. 1.3 Description des algorithmes de la STL (inspiré de [Schildt, 2002] page 643). // Remplit le vecteur avec des nombres aléatoires // Fait appel à la fonction rand() de la stdlib generate(v.begin(), v.end(), rand); printcontainer(v.begin(), v.end(), "Generate : "); // Trie le vecteur (ordre croissant) sort(v.begin(), v.end()); printcontainer(v.begin(), v.end(), "Sort : "); // Récupère un itérateur sur le plus petit élément du vecteur it = min_element(v.begin(), v.end());

30 20 Chapitre 1. Notions importantes du C++ et aperçu de la STL cout << "Elément plus petit : " << *it << endl; // Récupère un itérateur sur le plus grand élément du vecteur it = max_element(v.begin(), v.end()); cout << "Elément plus grand : " << *it << endl; Generate : Sort : Elément plus petit : Elément plus grand : Pour utiliser les algorithmes de la STL, on inclut donc le fichier d en-tête algorithm. La fonction printcontainer() s occupe juste d afficher les éléments d un intervalle défini par les itérateurs first et last. Le premier algorithme utilisé est generate(). Cet algorithme permet d assigner à chaque élément d un intervalle les valeurs retournées par une fonction de génération. generate(v.begin(), v.end(), rand); Dans notre cas, l intervalle spécifié est le vecteur v au complet. Pour chaque élément du vecteur, un appel à la fonction rand() (fonction de la librairie standard C qui retourne un nombre aléatoire) est effectué. Le résultat obtenu est alors affecté à l élément. On obtient donc un vecteur de nombres aléatoires. Si nous avions par exemple voulu uniquement appliqué cet algorithme aux deux premiers éléments du vecteur, nous aurions pu faire un appel du genre : generate(v.begin(), v.begin() + 2, rand); L opérateur + fonctionne dans ce cas, car nous utilisons un vecteur et par conséquent, l itérateur retourné par v.begin() est un random access iterator. Le deuxième algorithme employé est sort(). Cet algorithme permet de trier un intervalle donné dans l ordre croissant. Pour l utiliser, il suffit de transmettre les deux itérateurs qui définissent l intervalle. sort(v.begin(), v.end()); Comme pour generate(), nous avons appliqué l algorithme sur tous les éléments de v. Nous avons finalement utilisé les algorithmes min_element() et max_element() qui permettent respectivement d obtenir un itérateur sur le plus petit élément et un itérateur sur le plus grand élément. Comme d habitude, ces algorithmes s utilisent

31 1.2. Vue d ensemble de la STL 21 en spécifiant un intervalle en fournissant deux itérateurs en paramètre. it = min_element(v.begin(), v.end());... it = max_element(v.begin(), v.end());... L affichage du résultat obtenu s effectue ensuite avec : cout << "Elément plus petit : " << *it << endl;... cout << "Elément plus grand : " << *it << endl; Il serait tout à fait possible de condenser la recherche de l élément, soit le plus petit, soit le plus grand, et son affichage avec une instruction du type : cout << "Elément plus petit : " << *min_element(v.begin(), v.end()) << endl; Bien que cet exemple soit relativement court, il nous montre déjà bien que l utilisation d un algorithme se résume généralement à définir, à l aide de deux itérateurs, l intervalle du conteneur sur lequel on désire l appliquer. Ces deux itérateurs forment toujours un intervalle semi-ouvert ([f irst, last)). Ce code nous apporte également une meilleure compréhension de la Fig. 1.1 (page 10), où le comprend mieux maintenant de quelle manière les itérateurs permettent de faire la liaison entre les conteneurs et les algorithmes Résumé Nous arrivons au terme de notre courte présentation de la STL, dans laquelle nous avons appris que : la STL est composée de trois éléments principaux : les conteneurs, les itérateurs et les algorithmes : les conteneurs permettent de stocker une collection de données d un type donné. Les plus utilisés sont peut-être vector, list et map, les algorithmes permettent de manipuler le contenu des conteneurs, les itérateurs sont des pointeurs qui s occupent de faire la liaison entre les conteneurs et les algorithmes ; la majorité des algorithmes s utilisent en définissant un intervalle à l aide de deux itérateurs f irst et last. L intervalle définit est semi-ouvert ; ils existent plusieurs types d itérateurs qui permettent un accès différent au conteneur pointé (Input Iterator, Output Iterator,...). Les algorithmes nécessitent un certain type d itérateur pour fonctionner efficacement ;

32 22 Chapitre 1. Notions importantes du C++ et aperçu de la STL la STL fait partie d un standard et peut être utilisée sans soucis de compatibilité. Ils existent encore d autres éléments dans la STL, tels que les allocateurs ou des fonctions objets prédéfinies. Ces notions sortent du cadre de cette présentation et ne sont pas forcément utiles pour la compréhension de la MPTL. Pour une étude plus approfondie de la STL, l ouvrage [Josuttis, 1999] dont ce chapitre est fortement influencé est une excellente référence. Le guide du développeur de SGI sur la STL permet également d avoir rapidement des informations pratiques sur le sujet. Ce guide se trouve à l URL suivante :

33 Chapitre 2 Utilisation de la MPTL Maintenant que nous avons bien en tête le fonctionnement de la STL, nous allons apprendre à utiliser la MPTL. La MPTL est un modèle de programmation parallèle qui permet aux développeurs de paralléliser facilement ses algorithmes sans avoir besoin par exemple d utiliser des fonctions techniques pour gérer le multithreading. Elle offre donc un niveau d abstraction supérieur afin de paralléliser des algorithmes. Pour réduire le temps de travail des utilisateurs, une extension a été ajoutée à la librairie qui propose directement les algorithmes de la STL parallélisés selon le formalisme de la MPTL. Pour utiliser ces algorithmes parallèles, il faut obligatoirement une version de la STL, car ces derniers se basent sur le code de la STL. Par contre, une fois que cette condition est remplie, il est très facile de paralléliser un programme qui utilise les algorithmes de la STL comme nous le découvrirons dans cette partie. Une fois que nous aurons bien compris comment utiliser la MPTL pour paralléliser les algorithmes de la STL, nous apprendrons à paralléliser nos propres algorithmes selon le formalisme de la MPTL. Nous comprendrons par ailleurs de quelle manière l extension des algorithmes parallèles de la STL a été écrite. 2.1 Fonctions utilisateurs Toutes les fonctions de la MPTL sont déclarées dans le namespace mptl. Il en sera de même avec les algorithmes que nous étudierons plus tard. La librairie met à disposition de l utilisateur deux groupes de fonctions. Le premier groupe permet de définir, durant l exécution du programme, le nombre de threads qui doivent être utilisés pour paralléliser un algorithme. Le deuxième regroupe les fonctions

34 24 Chapitre 2. Utilisation de la MPTL qui s occupent d exécuter un algorithme en parallèle. Toutes les fonctions sont accessibles après avoir inclut le fichier d en-tête mptl.h : #include "mptl.h" Nombre de threads L utilisateur a la possibilité de définir précisément le nombre de threads utilisés durant l exécution d un algorithme. Ce nombre de threads peut varier durant l exécution du programme, ainsi il est possible d exploiter un nombre différent de threads pour exécuter les algorithmes d un programme. Par défaut, la MPTL utilise un seul thread. Cette valeur peut être modifiée en appelant la fonction setnumthreads() : inline void setnumthreads(unsigned int nthreads) où le paramètre nthreads désigne le nombre de threads utilisés durant l exécution des algorithmes suivants. Il est tout à fait possible de récupérer le nombre de threads utilisés avec la fonction : inline unsigned int getnumthreads() Voici un court exemple qui montre de quelle manière ces fonctions peuvent être utilisées : #include "mptl.h" int main() { // Les algorithme sont parallélisés avec 1 thread valeur par défaut //... mptl::setnumthreads(4); // Les algorithmes sont parallélisés avec 4 threads //... mptl::setnumthreads(7); // Les algorithmes sont parallélisés avec 7 threads //...

35 2.1. Fonctions utilisateurs 25 if (mptl::getnumthreads() == 1) { // 1 thread else { // 2, 3, 4,... threads Exécution d un algorithme Dans la partie (page 18) sur la présentation des algorithmes de la STL, nous avions remarqué que les algorithmes prenaient souvent en paramètre un intervalle, définit par deux itérateurs, sur lequel ils devaient effectuer leur travail. Pour paralléliser un algorithme, la MPTL découpe cet intervalle en sousintervalles et les distribue aux différents threads. Chaque thread peut alors appliquer l algorithme sur le sous-intervalle qui lui a été transmis. Dans la MPTL, le découpage et la distribution des sous-intervalles peut s effectuer de deux façons : statique ou dynamique. Distribution statique La politique de distribution la plus simple et probablement la plus efficace avec la majorité des algorithmes de la STL consiste à répartir statiquement l intervalle en sous-intervalles réguliers (Fig. 2.1). Nombre de threads : 4 t 0 t 1 t 2 t 3 Taille de l'intervalle : 17 Fig. 2.1 Distribution statique des charges. Ce partage s effectue en fonction du nombre de threads. L intervalle est réparti équitablement entre les threads. Si la taille de l intervalle n est pas parfaitement

36 26 Chapitre 2. Utilisation de la MPTL divisible avec le nombre de threads, les éléments restants sont distribués aux premiers threads. S il reste par exemple trois éléments à distribuer, les trois premiers threads recevront un sous-intervalle avec un élément en plus. La Fig. 2.1 illustre un partage où la taille de l intervalle n est pas divisible avec le nombre de threads. Il y a un intervalle de 17 éléments avec 4 threads. Chaque thread reçoit alors 4 éléments sauf le premier (t 0 ) qui reçoit un élément en plus : = 17 éléments. Maintenant, s il y avait eu 18 éléments au lieu de 17, les threads t 0 et t 1 auraient reçu 5 éléments tandis que les threads t 2 et t 3 en auraient toujours reçu 4. Ainsi, la surcharge d un thread par rapport à un autre ne dépasse jamais un élément. Cette répartition des tâches est idéale si le temps nécessaire pour effectuer une tâche (appliquer l algorithme à un élément de l intervalle) est constant. Ainsi, tous les threads, ayant à disposition une puissance de calcul équivalente, devraient terminer leur travail en même temps ou du moins dans un temps très proche. Des algorithmes tels que count(), equal() ou fill() devraient se paralléliser très bien avec cette politique de répartition. La MPTL définit deux fonctions template pour paralléliser un algorithme avec ce type de distribution : template <class CSPA> inline void execute(cspa algospec) template <class CSPA> inline void execute(cspa algospec, ResultType& result) Sachant que : la première version s utilise avec les algorithmes qui ne retournent pas de valeur. Elle peut également être utilisée avec un algorithme qui retourne une valeur qu il n est pas nécessaire de conserver pour la suite du programme. la seconde version s utilise avec les algorithmes qui retournent une valeur. A la fin de l exécution de l algorithme, la valeur retournée se trouve dans la variable result. En étudiant la signature de ces deux fonctions, on est en droit de se demander ce que représente le type générique CSPA. Pour l instant, nous allons nous contenter de se dire qu il s agit simplement d une classe qui encapsule toutes les informations nécessaires pour que la librairie puisse correctement paralléliser l algorithme. L algorithme lui-même est également décrit dans cette classe. Dans la partie 2.2, nous découvrirons que la MPTL offre un moyen très simple pour instancier une classe de ce genre quand il s agit d un

37 2.1. Fonctions utilisateurs 27 algorithme de la STL. Plus tard, nous apprendrons à construire une telle classe afin de paralléliser nos propres algorithmes. Pour faire simple, gardons simplement à l esprit que le type générique CSPA représente l algorithme que l on désire paralléliser. Distribution dynamique La distribution dynamique des charges est légèrement plus complexe que la version statique. Les threads reçoivent durant l exécution de l algorithme des sousintervalles dont la taille est spécifiée au préalable par l utilisateur. Dans ce contexte, un sous-intervalle est appelé chunk. Cette technique nécessite l intervention d un processus maître pour distribuer les chunks aux différents threads. Processus maître chunk chunk chunk chunk Réveil Indication sur les données Eligibles t 0, t 2 Activation Elu t 3 Entre en attente Début du travail Indique la fin du travail Actifs t 1 t 4 t 5 Nombre de threads : 6 Taille d'un chunk : 4 Fig. 2.2 Distribution dynamique des charges. Etudions étape par étape le fonctionnement de cette distribution (cf. Fig. 2.2) : 1. Le processus maître reçoit l intervalle à distribuer. Il le découpe en chunks. Tous les chunks ont la même taille à l exception du dernier qui peut être

38 28 Chapitre 2. Utilisation de la MPTL plus petit. Cette différence peut apparaître si la taille de l intervalle n est pas divisible avec la taille des chunks. Il maintient à jour une liste qui contient les informations sur tous les chunks qui n ont pas encore été traités. Cette liste lui permet de savoir à quel moment l algorithme a été exécuté sur tous les éléments de l intervalle. 2. Les threads sont créés et entrent dans un état d attente. Dans cet état, ils attendent de se faire réveiller par le processus maître pour effectuer un travail (consiste à appliquer l algorithme sur les éléments d un chunk). Cet état est appelé éligible dans la Fig Le processus maître réveille un thread et lui transmet les informations nécessaires sur le travail à effectuer. Ces informations sont uniquement les positions (indices) du premier et dernier éléments du chunk puisque l exécution s effectue en mémoire partagée (le processus maître et les threads accèdent directement à tous les élément de l intervalle). 4. Le thread effectue son travail. Pendant ce temps, le processus maître réveille un autre thread s il reste du travail à réaliser (étape 3). 5. Lorsqu un thread termine son travail, il indique tout d abord au processus maître qu il a terminé son travail. Il retourne ensuite en état éligible (étape 2). 6. Le processus maître met à jour sa liste des chunks à traiter. Si tous les chunks ont été traités, ils réveillent tous les threads pour leur indiquer la fin de l algorithme. Dans le cas contraire, il réveille un thread éligible pour lui indiquer un nouveau travail (étape 3). Les fonctions définies par la MPTL pour exécuter un algorithme avec cette méthode de distribution sont les suivantes : template <class CSPA> inline void dynamicexecute(cspa algospec, const unsigned int chunksize) template <class CSPA> inline void dynamicexecute(cspa algospec, const unsigned int chunksize, ResultType& result) Sachant que : la première version s utilise avec les algorithmes qui ne retournent pas de valeur. Elle peut également être utilisée si l algorithme retourne une valeur qui n a pas besoin d être conservée.

39 2.2. Parallélisation des algorithmes de la STL 29 la seconde version s utilise avec les algorithmes qui retournent une valeur. A la fin de l exécution de l algorithme, la valeur retournée se trouve dans la variable result. le paramètre chunksize permet de définir la taille des chunks. Cette politique de distribution devient très avantageuse lorsqu elle est utilisée pour paralléliser un algorithme dont le temps d exécution varie en fonction de la position des éléments dans l intervalle. Avec une distribution statique, certains threads peuvent travailler nettement plus que d autres, car les sous-intervalles qu ils ont reçus contiennent des éléments qui nécessitent plus de temps pour appliquer l algorithme. La répartition dynamique permet de corriger ce problème de déséquilibrage des charges. Si la taille des chunks est choisie correctement les threads devraient pouvoir terminer leur travail en même temps. Maintenant, les situations qui demandent un rééquilibrage des charges en employant une distribution dynamique sont plutôt rares dans le contexte traité par ce travail. Par exemple, la majorité des algorithmes de la STL se parallélisent mieux 1 avec une distribution statique. Ceci est simplement dû au fait que les algorithmes de la STL s appliquent en un temps constant sur tous les éléments d un intervalle donné et que la distribution dynamique nécessite nettement plus d opérations, donc de temps, pour effectuer son travail de répartition des charges. Dans le chapitre 4, nous discuterons tout de même d un algorithme où la distribution dynamique offre un avantage conséquent justifiant ainsi la présence de cette politique de distribution au sein de la MPTL. 2.2 Parallélisation des algorithmes de la STL Comme nous l avons déjà évoqué, la MPTL a initialement été conçue pour permettre aux développeurs de paralléliser facilement les algorithmes de la STL. Cette partie va justement nous apprendre à paralléliser avec la MPTL un programme qui utilise des algorithmes de la STL. Avant de voir des exemples concrets sur l utilisation de la MPTL, nous allons tout d abord discuter de ses limitations. La MPTL n est malheureusement pas suffisamment souple et puissante pour parvenir à paralléliser tous types d algorithmes. 1 On entend par mieux, un temps d exécution plus court pour appliquer l algorithme.

40 30 Chapitre 2. Utilisation de la MPTL Limitations de la MPTL Dans la partie 2.1.2, sur les fonctions qui permettent à l utilisateur d exécuter un algorithme en parallèle, nous avons eu un bon aperçu sur les méthodes qu emploie la MPTL pour distribuer équitablement le travail entre les différents threads. La distribution peut s effectuer statiquement ou dynamiquement. Dans les deux cas, le principe est toujours le même : la librairie découpe l intervalle en sousintervalles, ou en chunks, et les distribue aux threads qui effectuent le travail en parallèle. Ce principe implique des limitations à deux niveaux : sur les algorithmes eux-mêmes et sur les itérateurs transmis à l algorithme. Restriction sur les algorithmes La MPTL propose de paralléliser qu une partie des algorithmes de la STL. Sur les 80 algorithmes que l on peut utiliser dans la STL, la MPTL est capable d en paralléliser uniquement une quarantaine. La liste complète des algorithmes parallélisés se trouve à la page 33. Il existe plusieurs facteurs qui empêchent la parallélisation de certains algorithmes de la STL avec la MPTL : La MPTL définit un formalise basé sur un parallélisme de données. Par là, on entend la possibilité de paralléliser une opération qui doit s effectuer sur chaque élément d une collection de données. Il doit être possible d appliquer l algorithme sur des sous-intervalles sans se préoccuper de leur taille ou de leur position dans la collection. En quelque sorte, le découpage doit pouvoir s effectuer de façon «aléatoire». Il s agit de la première condition. Ensuite, une fois que la distribution des sous-intervalles est effectuée, un thread doit pouvoir appliquer l algorithme sur son sous-intervalle sans avoir besoin de communiquer ou d échanger des éléments avec un autre thread. La taille du sous-intervalle ainsi que sa position dans le conteneur doivent également rester constantes. Par contre, un thread peut éventuellement aller lire des éléments qui se trouvent dans un autre sous-intervalle si nécessaire comme nous l étudierons plus tard en construisant un algorithme parallèle qui résout numériquement le problème de Laplace. Essayons d illustrer un peu ce concept qui peut paraître un peu flou. Dans un premier temps, étudions un algorithme qui est parfaitement parallélisable avec la MPTL. Ceci signifie que l intervalle sur lequel on désire l appliquer peut sans problème être découpé en sous-intervalles. Chaque sousintervalle peut ensuite être attribué à un thread qui va s occuper d appliquer l algorithme dessus. Prenons, par exemple, l algorithme fill() qui permet d assigner une valeur donnée aux éléments d un intervalle. La parallélisation

41 2.2. Parallélisation des algorithmes de la STL 31 s effectue en découpant l intervalle et en transmettant un sous-intervalle à chaque thread. Une fois qu un thread reçoit un sous-intervalle, il lui suffit d appliquer dessus l algorithme fill() de la STL. Il peut donc effectuer son travail sans avoir besoin d échanger des éléments avec d autres threads ou de modifier la taille ou la position des sous-intervalles. Cet algorithme est donc parfaitement parallélisable avec la MPTL, car il suit parfaitement les conditions que nous avions décrites en parlant de parallélisme de données. Le problème est maintenant tout autre si nous prenons un algorithme tel que sort(). Cet algorithme applique un quicksort sur un intervalle donné. Pour effectuer un tel tri, il paraît difficile de découper l intervalle en sousintervalles, de distribuer ces sous-intervalles aux threads et que chaque thread ensuite se contente d appliquer des instructions sur leur sous-intervalle sans avoir besoin d échanger des éléments avec d autres threads ou de modifier la taille ou la position de leur sous-intervalle. Un tel algorithme n est donc pas parallélisable avec la MPTL. Maintenant comme il s agit d un algorithme très répandu, la MPTL propose tout de même une version parallélisée de cet algorithme qui emploie une technique de parallélisation qui sort du code initialement conçu pour la MPTL. Il s agit d un algorithme à part que nous étudierons dans la partie Annexe A, page 121 ; Certains algorithmes perdent en efficacité lorsqu il s agit de les paralléliser avec la MPTL. La version parallèle serait à coup sûr plus lente que la version séquentielle. Un exemple d algorithme serait remove() qui permet de supprimer certains éléments d un intervalle. Si chaque thread effectue cette opération sur un sous-intervalle, il faudrait ensuite reformer un nouvel intervalle en concaténant les sous-intervalles potentiellement modifiés de chaque thread. Cette phase de reconstruction est lourde et pénalise lourdement les performances de l algorithme en parallèle. La STL propose des algorithmes qui ne s appliquent pas à des intervalles. Ces algorithmes effectuent des opérations très simples telles que min() ou max() qui permettent respectivement de retourner la valeur minimale et maximale entre deux éléments du même type. La simplicité de l opération qu ils effectuent et le fait qu ils travaillent uniquement sur deux éléments font qu il n y a aucun intérêt à essayer de les paralléliser. Catégorie des itérateurs Une deuxième limitation de la MPTL se situe au niveau des itérateurs ou plus précisément au niveau de la catégorie des itérateurs utilisés. Pour rappel, il

42 32 Chapitre 2. Utilisation de la MPTL existe cinq types d itérateurs qui peuvent être classés selon un ordre hiérarchique (cf. Fig. 1.2, page 16) : Input iterator : lecture et se déplace uniquement en avant. Output iterator : écriture et se déplace uniquement en avant. Forward iterator : lecture, écriture et se déplace uniquement en avant. Bidirectional iterator : lecture, écriture et déplacement en avant et en arrière. Random access iterator : lecture, écriture et accès aléatoire aux éléments. Parmi ces catégories, deux ne sont pas supportées par la MPTL. Il s agit des itérateurs input et output. Ces types d itérateurs ne permettent pas à la MPTL de paralléliser les algorithmes. En contre partie, si un algorithme reçoit en paramètre des itérateurs au moins du type forward, la librairie pourra le paralléliser. Un itérateur input peut uniquement effectuer une lecture en avant élément par élément. Il se contente de retourner la valeur lue à chaque fois. Il peut lire qu un seul élément à la fois et peut lire qu une seule fois un élément. Un bon exemple d un tel itérateur est un itérateur qui lit l entrée standard (généralement le clavier). Chaque valeur peut uniquement être lue qu une seule fois. Il n est pas possible de revenir en arrière. Une fois que la touche est pressée et que le caractère tapé est lu, l opération de lecture est terminée pour ce caractère. A la prochaine frappe, un autre caractère sera lu. A partir de là, on comprend bien qu il est difficile de paralléliser un tel itérateur et qu il y a par ailleurs peu d intérêt à essayer de le faire. Une justification semblable peut être appliquée aux itérateurs output. Ces itérateurs peuvent uniquement avancer avec un accès en écriture. L écriture s effectue élément par élément vers l avant. Il est donc impossible d utiliser le même itérateur pour itérer deux fois sur le même intervalle. Un exemple typique est un itérateur qui permet d écrire sur la sortie standard (par exemple sur l écran ou sur une imprimante). L écriture doit nécessairement être séquentielle pour conserver l ordre de la séquence que l on désire écrire. L ordre des éléments risquerait de ne plus être conservé avec une écriture concurrente 2. Si un utilisateur essaie de paralléliser un algorithme en transmettant en paramètre un ou plusieurs itérateurs input ou output, une erreur de compilation sera alors générée. Lors de la parallélisation d un algorithme avec la MPTL, les performances 3 peuvent varier en fonction du type d itérateur. Les meilleures performances sont obtenues avec les itérateurs random access. Il n y a par contre aucune différence de performance entre l utilisation d itérateurs forward ou bidirectional. 2 A moins de synchroniser cette écriture, ce qui revient à supprimer la parallélisation. 3 Signifie dans ce contexte le gain de temps.

43 2.2. Parallélisation des algorithmes de la STL 33 L explication de cette différence entre les itérateur random access et les deux autres est assez simple. Lorsque le MPTL parallélise un algorithme, elle utilise la fonction advance() que nous avons étudié dans la partie (page 17). Cette fonction est simplement plus performante avec les itérateurs à accès aléatoire. Résumé Avant de continuer, essayons de faire un court résumé sur les limitations de la MPTL que nous venons de décrire : la MPTL n est pas capable de paralléliser tous les algorithmes de la STL. Sur les 80 de la STL, la MPTL en propose un peu plus de 40 ; la MPTL définit un formalisme de parallélisme de données. C est ce qui l empêche en grande partie de paralléliser certains algorithmes ; l algorithme sort() n est pas parallélisé de la même manière que les autres algorithmes dans la MPTL ; les itérateurs input et output ne peuvent pas être utilisés avec la MPTL. Un programme qui tente de le faire refuse d être compilé ; les itérateurs random access offrent de meilleures performances que les itérateurs forward ou bidirectional Liste des algorithmes parallélisés Comme nous l avons vu auparavant, la MPTL parallélise une quarantaine d algorithmes de la STL. Ces algorithmes peuvent directement être utilisés : ils sont prédéfinis dans la librairie. Nous allons découvrir maintenant la liste complète de ces algorithmes. Nous les avons classifiés dans différentes catégories. Ces catégories sont les mêmes que celles définies dans le guide SGI de la STL ( Algorithmes non-mutating Les algorithmes non-mutating effectuent des opérations sur des conteneurs sans les modifier. L ordre ainsi que la valeur des éléments ne sont pas modifiés. Par rapport aux algorithmes appartenant à cette catégorie proposés par la STL, la MPTL est capable de les paralléliser tous (Tab. 2.1). Ce qui n est pas étonnant vu que ces algorithmes se contentent généralement de lire séquentiellement chaque élément d un intervalle donné.

44 34 Chapitre 2. Utilisation de la MPTL Algorithme Description for_each() Applique une opération sur chaque élément. find() Recherche le premier élément égal à une valeur donnée. find_if() Recherche le premier élément qui correspond à un critère. adjacent_find() Trouve deux éléments adjacents qui sont égaux. find_first_of() Cherche le premier élément parmi plusieurs. count() Retourne le nombre d éléments. mismatch() Retourne le premier élément de deux séquences qui diffère. equal() Vérifie l égalité de deux intervalles. search() Cherche la première occurrence d un sousintervalle. search_n() Cherche les n premiers éléments consécutifs avec certaines propriétés. find_end() Recherche le dernier élément égal à une valeur donnée. Tab. 2.1 Algorithmes non-mutating de la MPTL. Contrairement à la version de la STL, la version de l algorithme for_each() de la MPTL ne retourne aucune valeur. Maintenant, si un utilisateur a réellement besoin d une version parallèle de for_each() avec une valeur de retour, il peut tout de même le faire avec la MPTL. Il doit cependant écrire sa propre version de l algorithme selon les instructions décrites dans la Section Algorithmes mutating Les algorithmes mutating changent la valeur des éléments. Une partie de ces algorithmes peuvent également supprimer des éléments. Le Tab. 2.2 liste tous les algorithmes mutating supportés par la MPTL. En comparant avec les algorithmes disponibles dans la STL, on peut constater qu il manque deux types d algorithmes : les algorithmes qui suppriment des éléments. Ils peuvent supprimer les éléments dans un intervalle seul ou pendant qu ils effectuent une copie dans un autre intervalle. Il s agit des algorithmes remove(), remove_if(), remove_copy(), remove_copy_if(), unique() et unique_copy() ; les algorithmes qui changent l ordre des éléments. Ces modifica-

45 2.2. Parallélisation des algorithmes de la STL 35 Algorithme copy() copy_backward() swap_ranges() transform() replace() replace_if() replace_copy() replace_copy_if() fill() fill_n() generate() generate_n() Description Copie un intervalle. Copie un intervalle commençant par le dernier élément. Echange les éléments de deux intervalles. Modifie et copie des éléments ; combine les éléments de deux intervalles. Remplace les éléments qui ont une valeur spéciale avec une autre valeur. Remplace les éléments qui satisfont un critère avec une autre valeur. Remplace les éléments qui ont une valeur spéciale durant la copie de l intervalle entier. Remplace les éléments qui satisfont un critère durant la copie de l intervalle entier. Remplace élément avec une valeur donnée. Remplace n éléments avec une valeur donnée. Remplace chaque élément avec le résultat d une opérations. Remplace n éléments avec le résultat d une opérations. Tab. 2.2 Algorithmes mutating de la MPTL. tions ne s appliquent pas à la valeur des éléments, mais aux éléments directement. Ce sont les algorithmes reverse(), reverse_copy(), rotate(), rotate_copy(), next_permutation(), prev_permutation(), random_shuffle(), partition() et stable_partition(). Algorithmes de tri Les algorithmes de tri (sorting algorithms) sont en réalité une sous-catégorie spéciale des algorithmes mutating, car ils modifient l ordre des éléments. Cette catégorie d algorithmes est celle qui pose le plus de problème au niveau de la parallélisation. Par rapport à la STL, seulement quelques algorithmes sont parallélisés (Tab. 2.3). Le problème vient du fait que la majorité de ces algorithmes n entrent pas dans le cadre du parallélisme de données comme nous en avons discuté dans la partie sur les restrictions de la MPTL (Section 2.2.1, page 30). L algorithme sort(), qui effectue un quicksort sur une séquence donnée, est

46 36 Chapitre 2. Utilisation de la MPTL Algorithme lower_bound() upper_bound() binary_search() min_element() max_element() sort() Description Trouve le premier élément égal ou plus grand qu une valeur donnée. Trouve le premier élément plus grand qu une valeur donnée. Teste si l intervalle contient un élément. Retourne l élément avec la plus petite valeur. Retourne l élément avec la plus grande valeur. Trie tous les éléments. Tab. 2.3 Algorithmes de tri de la MPTL. suffisamment important pour justifier une implémentation parallèle dans la MPTL. Comme cet algorithme ne peut pas être directement parallélisé avec le formalisme de la MPTL, une version spéciale a été ajoutée à la librairie. Ce tri parallèle s utilise différemment des autres algorithmes de la MPTL. Son utilisation ainsi que ses performances sont décrites en annexe. Voici la liste des algorithmes de tri non parallélisés dans la MPTL : stable_sort(), partial_sort(), partial_sort_copy() ; nth_element() ; equal_range() ; merge(), inplace_merge() ; includes(), set_union(), set_intersection(), set_difference(), set_symmetric_difference() ; push_heap(), pop_heap(), make_heap(), sort_heap(), is_heap() ; min(), max() ; lexicographical_compare(), lexicographical_compare_3way() ; next_permutation(), prev_permutation(). Algorithmes numériques Ces algorithmes permettent d effectuer différentes sortes d opérations numériques sur les éléments. Ces algorithmes sont plus flexibles qu ils n en paraissent. Par exemple, l algorithme accumulate() réalise par défaut la somme de tous les éléments. Si on l applique à une collection de chaînes de caractères, il va les concaténer ensemble. D autre part, il est tout à fait possible de modifier l opération et utiliser par exemple l opérateur *. On obtiendra donc le produit des éléments.

47 2.2. Parallélisation des algorithmes de la STL 37 Algorithme accumulate() adjacent_difference() Description Retourne la somme de tous les éléments. Pour chaque élément, calcule la différence entre lui et l élément adjacent. Tab. 2.4 Algorithmes numériques de la MPTL. L utilisation de adjacent_difference() nécessite une précaution particulière : il ne faut en aucun cas que l itérateur de destination soit compris dans l intervalle source. Dans le cas contraire, le résultat peut être erroné. Le problème vient du fait que les threads ont besoin de lire une valeur (l élément adjacent au premier élément de leur sous-intervalle) qui n appartient pas à leur sous-intervalle. Les algorithmes non-parallélisés sont inner_product() et partial_sum() Exemples d utilisation A ce stade, nous savons qu il est possible de choisir le nombre de threads durant l exécution d un algorithme MPTL avec setnumthreads(). Il est possible de récupérer cette valeur à tout moment avec getnumthreads(). Nous savons également que l exécution d un algorithme parallélisé s effectue avec l une des fonctions prédéfinies execute() ou dynamicexecute(). La première impliquera une distribution des charges statique tandis que la seconde une distribution dynamique. Finalement, nous avons découvert la liste de tous les algorithmes que la MPTL propose de paralléliser. Il nous reste donc plus qu à savoir comment utiliser les fonctions execute() ou dynamicexecute() pour paralléliser un de ces algorithmes. On se souvient que l utilisation des fonctions execute() cache un mystère (Section 2.1.2, page 25). Elles demandent en paramètre une classe template de type CSPA. Nous nous étions contenter de dire que cette classe encapsulait toutes les informations nécessaires pour exécuter l algorithme que l on désire paralléliser, mais rien de plus. Maintenant si l on désire paralléliser un algorithme, nous sommes bien obligés d être capable de fournir une telle classe. Le problème est qu un algorithme de la STL est uniquement une fonction et pas une classe. On ne peut donc pas directement transmettre un algorithme de la STL en paramètre des fonctions execute() ou dynamicexecute(). La MPTL donne une solution à ce problème en encapsulant tous les algorithmes de la STL parallélisables avec des fonctions dont les caractéristiques sont les suivantes : elles prennent exactement les mêmes paramètres que les algorithmes de la STL ;

48 38 Chapitre 2. Utilisation de la MPTL elles s occupent de retourner une classe du type CSPA qui contient toutes les informations nécessaires pour la parallélisation de l algorithme. Concrètement, cela signifie que si l on désire paralléliser un algorithme de la STL, il suffit de transmettre à la fonction execute() une fonction homonyme proposée par la MPTL qui prend exactement les mêmes paramètres. Toutes ces fonctions spéciales sont prédéfinies dans la MPTL. Il est donc possible de paralléliser facilement tous les algorithmes présents dans les différentes listes de la Section Pour illustrer ceci, prenons un simple exemple. Imaginons que nous avons codé un programme qui fait appel à l algorithme suivant : fill(v.begin(), v.end(), 100); Cette instruction utilise l algorithme fill() de la STL pour affecter à tous les éléments du vecteur v la valeur 100. Si on désire maintenant utiliser la MPTL pour paralléliser cet algorithme, il suffit de modifier légèrement cette instruction de la manière suivante : mptl::execute(mptl::fill(v.begin(), v.end(), 100)); L algorithme sera dans cette situation effectué en parallèle avec le nombre de threads retourné par la fonction getnumthreads(). Dans cette dernière instruction, la fonction fill(), qui est définie dans le namespace mptl, prend exactement les mêmes paramètres que la fonction fill() définie dans la STL. La seule différence est que la version de la MPTL retourne une classe qui correspond au type CSPA attendu par la fonction execute(). Cette classe contient ensuite toutes les informations pour paralléliser l algorithme fill() correctement. Exemple complet Nous allons maintenant étudier un programme complet. Nous allons écrire dans un premier temps un programme qui utilise uniquement la STL. Nous essayerons par la suite de le paralléliser avec la MPTL. Voici ce programme séquentiel qui utilise uniquement des algorithmes de la STL : #include <iostream> #include <deque> #include <algorithm> using namespace std;

49 2.2. Parallélisation des algorithmes de la STL 39 // Fonction template pour afficher le contenu d un intervalle spécifié template <typename Iterator> void print_elements(iterator first, Iterator last, char* msg) { cout << msg; for ( ; first!= last; ++first) cout << *first << " "; cout << endl; int main() { deque<int> coll1; // Ajoute les entiers de 1 à 9 for (int i = 1; i <= 9; ++i) coll1.push_back(i); // Affiche les éléments de coll1 print_elements(coll1.begin(), coll1.end(), ""); deque<int> coll2(coll1.size()); // Copie le contenu de coll1 dans coll2 en inversant // le signe des éléments transform(coll1.begin(), coll2.end(), // Source coll2.begin(), // Destination negate<int>()); // Opération // Affiche les éléments de coll2 print_elements(coll2.begin(), coll2.end(), ""); // Remplace tous les éléments inférieurs à -5 avec 10 replace_if(coll2.begin(), coll2.end(), // Source bind2nd(less<int>(), -5), // Critère de remplacement 10); // Nouvelle valeur print_elements(coll2.begin(), coll2.end(), "");

50 40 Chapitre 2. Utilisation de la MPTL Ce programme utilise essentiellement deux algorithmes que nous allons paralléliser avec la MPTL : transform() et replace_if(). Le premier algorithme copie le contenu du conteneur coll1 dans coll2 tout en inversant le signe des éléments. Le deuxième algorithme permet d affecter la valeur 10 à tous les éléments, d un intervalle donné, dont la valeur est inférieure à 5. Voici maintenant le programme identique qui utilise la MPTL pour paralléliser ces deux algorithmes : #include <iostream> #include <deque> #include <algorithm> #include "mptl.h" #include "mptl_algo.h" // Algorithmes prédéfinis de la MPTL // Fonction template pour afficher le contenu d un intervalle spécifié template <typename Iterator> void print_elements(iterator first, Iterator last, char* msg) { cout << msg; for ( ; first!= last; ++first) cout << *first << " "; cout << endl; int main() { // Utilise 4 threads pour paralléliser les algorithmes mptl::setnumthreads(4); deque<int> coll1; // Ajoute les entiers de 1 à 9 for (int i = 1; i <= 9; ++i) coll1.push_back(i); // Affiche les éléments de coll1

51 2.2. Parallélisation des algorithmes de la STL 41 print_elements(coll1.begin(), coll1.end(), ""); deque<int> coll2(coll1.size()); // Copie le contenu de coll1 dans coll2 en inversant // le signe des éléments. mptl::execute(mptl::transform(coll1.begin(), coll1.end(), coll2.begin(), negate<int>())); // Affiche les éléments de coll2 print_elements(coll2.begin(), coll2.end(), ""); // Remplace tous les éléments inférieurs à -5 avec 10 mptl::execute(mptl::replace_if(coll2.begin(), coll2.end(), bind2nd(less<int>(), -5), 10)); print_elements(coll2.begin(), coll2.end(), ""); Discutons des différences qu imposent l utilisation de la MPTL. Tout d abord, il est nécessaire d inclure le fichier d en-tête mptl.h : #include "mptl.h" Ce fichier permet d accéder aux fonctions utilisateurs tels que setnumthreads() et execute(). Il faut également inclure le fichier d en-tête mptl algo.h pour pouvoir utiliser tous les algorithmes parallélisés de la STL (Section 2.2.2). #include "mptl_algo.h" On décide par la suite le nombre de threads que la librairie va utiliser pour paralléliser les algorithmes : mptl::setnumthreads(4); On parallélise ensuite l algorithme transform() :

52 42 Chapitre 2. Utilisation de la MPTL mptl::execute(mptl::transform(coll1.begin(), coll1.end(), coll2.begin(), negate<int>())); Il suffit de faire appel à la fonction execute() avec en paramètre la version de la fonction transform() fournit par la MPTL. On remarque bien que cette fonction est parfaitement identique à celle de la STL au niveau des paramètres. Le principe est exactement le même avec la parallélisation de l algorithme replace_if() : mptl::execute(mptl::replace_if(coll2.begin(), coll2.end(), bind2nd(less<int>(), -5), 10)); Algorithmes avec valeur de retour Nous venons d étudier un programme qui utilisait deux algorithmes sans valeur de retour. Si on désire maintenant utiliser un algorithme qui retourne une valeur, telle que min_element() ou count(), il faut simplement utiliser la deuxième version de execute() ou dynamicexecute() que propose la MPTL. Prenons un simple exemple d utilisation d un algorithme de la STL où on désire obtenir la valeur maximale de la liste d entiers coll : list<int>::iterator pos = max_element(coll.begin(), coll.end()); L itérateur pos donne ainsi la position de l élément maximal de la liste. L utilisation de la deuxième version de execute() nous permet de paralléliser cet algorithme et d obtenir la position dans pos : list<int>::iterator pos; mptl::execute(mptl::max_element(coll.begin(), coll.end()), pos); execute() est une fonction qui ne retourne pas de valeur. Le résultat de l algorithme est affecté au deuxième paramètre transmis à la fonction. Il est donc nécessaire de déclarer la variable qui va contenir le résultat avant l appel de la fonction. C est ce que nous avons fait dans cet exemple avec l itérateur pos. Distribution dynamique Tous les exemples que nous venons d étudier utilisent une distribution statique des charges. Regardons ce qu il en ait avec une distribution dynamique.

53 2.2. Parallélisation des algorithmes de la STL 43 Au lieu d utiliser la fonction execute(), il suffit d utiliser dynamicexecute() en spécifiant en plus la taille des chunks. Prenons par exemple l algorithme copy() pour copier les éléments du conteneur coll1 dans le conteneur coll2. copy(coll1.begin(), coll1.end(), coll2.begin()); // Source // Destination Si on désire maintenant paralléliser cet algorithme avec une distribution dynamique des charges, il suffit d écrire : mptl::dynamicexecute(mptl::copy(coll1.begin(), coll1.end(), coll2.begin()), 25); Dans cet exemple, la taille d un chunk est définie à 25. Les threads recevront donc des sous-intervalles composés de 25 éléments. Pour utiliser la distribution dynamique des charges avec un algorithme qui retourne une valeur, il suffit de transmettre à la fonction dynamicexecute() l algorithme, la taille des chunks ainsi que la variable qui va contenir le résultat. Pour comparer avec la version statique, nous pouvons reprendre l algorithme max_element() : list<int>::iterator pos; mptl::dynamicexecute(mptl::max_element(coll.begin(), coll.end()), 50, // Taille des chunks pos); // Variable pour la valeur de retour Exemple récapitulatif Voici un exemple de programme complet qui couvre les différentes notions que nous venons de décrire : #include <vector> #include <iostream> #include <iterator> #include <algorithm> #include "mptl.h" #include "mptl_algo.h" using namespace std; int main() {

54 44 Chapitre 2. Utilisation de la MPTL // Utilisation de 6 threads mptl::setnumthreads(6); vector<int> coll; for (int i = 0; i < 10; ++i) coll.push_back(i); // Remplace 9 par 12 mptl::execute(mptl::replace(coll.begin(), coll.end(), 9, 12)); // Affiche le vecteur copy(coll.begin(), coll.end(), ostream_iterator<int>(cout, " ")); cout << endl; // Teste si 5 est présent dans le vecteur bool t; mptl::dynamicexecute(mptl::binary_search(coll.begin(), coll.end(), 5), 3, t); if (t) cout << "5 est présent" << endl; else cout << "5 n est pas présent" << endl; // Compte le nombre d élément supérieur à 5 int n; mptl::execute(mptl::count_if(coll.begin(), coll.end(), bind2nd(greater<int>(), 5)), n); cout << "Nombre d éléments plus grand que 5 : " << n << endl; est présent Nombre d éléments plus grand que 5 : 4

55 2.2. Parallélisation des algorithmes de la STL 45 Conseils d utilisation Dans la grande majorité des cas, la distribution statique des charges doit être utilisée pour paralléliser les algorithmes de la STL. Les performances sont généralement meilleures avec cette version pour deux raisons : 1. lorsqu un algorithme de la STL est appliqué sur chaque élément d un intervalle donné, le temps d exécution varie très peu, voire pas du tout, entre les éléments. Tous les threads ont de forte de chance de terminer ensembles ; 2. la version dynamique nécessite plus d opérations pour fonctionner, donc plus de temps. La distribution dynamique peu devenir intéressante lorsque l on parallélise ses propres algorithmes qui peuvent être plus complexes que ceux de la STL. La librairie MPTL ne permet pas de paralléliser des algorithmes qui nécessitent un accès en écriture sur une variable statique. Un tel accès implique que tous les threads vont vouloir écrire dans la même variable de manière concurrente. Les problèmes suivants vont alors apparaître : l opération d écriture ne peut pas s effectuer en parallèle. Certains threads vont devoir attendre sur les autres threads avant de réaliser cette opération. Le gain de temps que pourrait offrir la parallélisation est alors réduit ; la valeur de la variable statique peut devenir incohérente. Prenons un simple exemple pour illustrer ces problèmes de variable statique. Imaginons que nous désirions paralléliser l algorithme suivant : generate(coll.begin(), coll.end(), rand); Cette instruction permet d affecter un nombre aléatoire à chaque élément de la collection coll. Pour cela, elle utilise la fonction rand() de la librairie standard du C. Le standard POSIX propose l implémentation suivante pour cette fonction : static unsigned long next = 1; // si RAND_MAX vaut int rand(void) { next = next * ; return((unsigned)(next/65536) % 32768); void srand(unsigned seed) { next = seed;

56 46 Chapitre 2. Utilisation de la MPTL L implémentation de cette fonction dépend du compilateur utilisé, mais dans tous les cas, elle doit être conforme au standard. La fonction rand() se base sur la variable statique next pour calculer une suite de nombres aléatoires compris entre 0 et RAND_MAX. Chaque nombre aléatoire dépend directement du nombre précédent. Nous pourrions maintenant paralléliser l algorithme generate() avec : mptl::execute(mptl::generate(coll.begin(), coll.end(), rand)); Chaque thread va recevoir un sous-intervalle de coll et va faire des appels à la fonction rand() pour affecter une valeur aléatoire à chaque élément. Un seul thread à la fois pourra écrire la nouvelle valeur calculée dans next. Cette limitation réduit fortement le gain de temps que la parallélisation aurait pu nous offrir. Un autre problème plus important apparaît : pour que rand() retourne des nombres aléatoires générés en accord avec la définition de la fonction, la valeur de next doit évoluer correctement entre chaque appel. Si deux threads commencent à exécuter la fonction en même temps, ils vont tous les deux lire la même valeur de next, ce qui n est pas correct. Pour éviter ce problème, il faudrait que chaque thread exécute cette fonction seul, ce qui tend à rendre le programme séquentiel. Il n y a donc plus d intérêt à paralléliser l algorithme generate() dans cette situation. D une manière générale, la fonction de génération employée avec generate() doit obligatoirement être thread-safe pour que l algorithme soit correctement parallélisé. 2.3 Construction d un nouvel algorithme La partie précédente nous a montré de quelle manière il faut utiliser la MPTL pour paralléliser les algorithmes de la STL. Maintenant, la MPTL permet à l utilisateur de créer facilement ses propres algorithmes selon un formalisme afin de les paralléliser. Les connaissances nécessaires pour parvenir à construire son propre algorithme MPTL se limitent essentiellement à la compréhension du fonctionnement de la classe générique CSPA (classe transmise aux fonctions execute() et dynamicexecute()). Ce type de classe, que nous appellerons CSPA pour Classe de Spécifications Parallèles pour un Algorithme, doit définir toutes les informations nécessaires à la MPTL pour qu elle puisse correctement paralléliser l algorithme qui doit lui-même être implémenté dans cette classe. En plus des CSPA, nous discuterons dans cette section des classes dites de réduction qui sont utilisées lorsqu un algorithme doit retourner une valeur. Fina-

57 2.3. Construction d un nouvel algorithme 47 lement, nous aborderons la notion de fonction de convenance. Dans notre cadre, une telle fonction permet d alléger l écriture lorsqu on désire instancier une CSPA CSPA Dans le formalisme de la MPTL, la parallélisation d un algorithme séquentiel s effectue en l encapsulant dans une classe que nous appelons CSPA. Une telle classe doit être capable de fournir toutes les informations nécessaires à la MPTL pour qu elle puisse exécuter en parallèle cet algorithme. La librairie a besoin des informations suivantes pour paralléliser un algorithme : le nombre d itérateurs ; le type des itérateurs ; les intervalles à découper ; une fonction qui exécute l algorithme ; si l algorithme retourne une valeur : le type de la valeur de retour, la classe réduction. Exemple introductif Avant d analyser en détails cette liste de spécifications, essayons d étudier un simple exemple. Imaginons que nous désirions par exemple paralléliser un algorithme qui ajoute simplement une valeur constante à chaque élément d un intervalle. Une version séquentielle de cet algorithme pourrait être implémentée de la manière suivante : template <typename Iterator, typename T> void addvalue(iterator first, Iterator last, const T value) { for ( ; first!= last; ++first) { *first += value; Pour paralléliser cet algorithme avec la MPTL, il nous faudrait créer une CSPA pour englober dans un tout à la fois l algorithme séquentiel ainsi que ses différentes caractéristiques que la librairie a besoin de connaître pour l exécuter en parallèle. Voici à quoi pourrait ressembler le code de cette classe :

58 48 Chapitre 2. Utilisation de la MPTL template <typename Iterator, typename T> class MPTLAddValue { private: Iterator first, last; // Intervalle const T value; // Valeur constante à ajouter public: // Définition des caractéristiques typedef Iterator iterator1; typedef TwoIterators niterators; // Constructeur de la classe MPTLAddValue MPTLAddValue(Iterator first_, Iterator last_, const T value_) : first(first_), last(last_), value(value_) { // Retourne les itérateurs (pour rendre accessible // à la librairie l intervalle à distribuer aux threads) Iterator getfirst1() { return first; Iterator getlast1() { return last; ; // Permet d appliquer l algorithme uniquement sur le sous-intervalle // [first, last ). Remarquons qu il s agit toujours du même // algorithme. void applyalgorithm(iterator first_, Iterator last_) { for ( ; first_!= last_; ++first_) { *first_ += value; Avec cette classe MPTLAddValue, il est maintenant tout à fait possible d exécuter l algorithme en parallèle. Il n y a strictement rien d autre à coder pour paralléliser la fonction addvalue(). Pour ajouter par exemple 10 aux éléments de la liste coll, il suffirait d utiliser l instruction : mptl::execute(mptladdvalue<typename list::iterator, int>(coll.begin(), coll.end(), 10)); L écriture est un peu lourde, car il faut spécifier tous les types génériques de la classe. Nous découvrirons plus tard de quelle manière il est possible de contourner

59 2.3. Construction d un nouvel algorithme 49 ce problème avec les fonctions de convenance (page 60). Cette classe générique utilise exactement les mêmes types génériques que l algorithme séquentiel. template <typename Iterator, typename T> Ce qui est normal vu qu on désire employer la version parallèle exactement de la même manière que la version séquentielle. Le constructeur de la classe prend les mêmes paramètres que la fonction séquentielle. Les paramètres transmis au constructeur sont conservés afin de pouvoir les utiliser pour appliquer l algorithme ou pour les transmettre à la librairie si nécessaire. private: Iterator first, last; const T value; Les caractéristiques de l algorithme, selon le formalisme de la MPTL, sont ensuite définies à l aide de deux typedef. Le premier permet de rendre public le type des itérateurs utilisés. Ainsi, la librairie peut facilement récupérer ce type avec le label iterator1. typedef Iterator iterator1; Le type générique Iterator est donc totalement arbitraire et nous aurions pu choisir un tout autre nom, seul le label iterator1 est imposé par la MPTL afin de rendre cette information accessible à l extérieur de la classe. Le deuxième type définit le nombre d itérateurs qui délimitent des intervalles à partager entre les threads. Dans notre exemple, il y a deux itérateurs qui définissent un intervalle à partager. Cette caractéristique est donc spécifiée avec la déclaration : typedef TwoIterators niterators; Le type TwoIterators est en réalité une structure vide définie dans la librairie qui permet de désigner un algorithme qui utilise deux itérateurs qui forment un intervalle à partager. Certains algorithmes peuvent être parallélisés en partageant plusieurs intervalles. Dans ce cas, une autre structure sera alors employée dans cette déclaration. Maintenant, dans tous les situations, la librairie pourra connaître cette caractéristique de l algorithme avec niterators. Le constructeur de la classe s occupe juste de conserver les paramètres qu il reçoit dans les variables membres de la classe. MPTLAddValue(Iterator first_, Iterator last_, const T value_) : first(first_), last(last_), value(value_) { Deux fonctions sont ensuite déclarées :

60 50 Chapitre 2. Utilisation de la MPTL Iterator getfirst1() { return first; Iterator getlast1() { return last; Elles permettent simplement de retourner les itérateurs qui délimitent l intervalle sur lequel doit s appliquer l algorithme. La fonction applyalgorithm() exécute l algorithme sur un intervalle donné. Elle prend en paramètre un nouvel intervalle, afin de permettre à chaque thread d exécuter l algorithme sur leur sous-intervalle. Il est important de noter que l algorithme est identique à la version séquentielle. La MPTL permet donc de paralléliser un algorithme sans avoir besoin de le modifier. Cet exemple nous a donné un bon aperçu sur la construction d une CSPA. Il faut bien garder à l esprit que l écriture de cette classe est totalement suffisante pour paralléliser cet algorithme avec la MPTL. Ainsi, nous pouvons relever les deux points importants suivants : aucune fonction liée à la gestion du multithreading n est utilisée par l utilisateur pour paralléliser un algorithme. La MPTL s occupe de le faire ellemême ; l algorithme défini dans la CSPA est simplement l algorithme séquentiel. Nous allons maintenant étudier plus en détails tous les éléments nécessaires qui doivent être définis dans une CSPA. Liste des composants d une CSPA Comme nous l avons découvert avec l exemple précédent, une CSPA doit être capable de fournir des informations à la librairie. Certaines de ces informations sont définies à l aide de typedef (p. ex. niterators et iterator1) tandis que d autres sont fournies à l aide de fonctions membres (p. ex. getfirst1()). Le nombre d itérateurs Nous avons pu remarquer dans la STL que les algorithmes prennent en paramètre des itérateurs pour spécifier un ou plusieurs intervalles. Par exemple, l algorithme generate() prend deux itérateurs afin de définir l intervalle sur lequel il faut appliquer l algorithme comme l illustre la Fig L algorithme equal() quant à lui prend trois itérateurs en paramètre pour définir deux intervalles. Il n est pas nécessaire d utiliser quatre itérateurs comme le montre la Fig Il compare les éléments entre les intervalles [first1, last1) et [first2, first2 + (last1 first1)). Ainsi, la taille du second intervalle est définie par la taille du premier.

61 2.3. Construction d un nouvel algorithme 51 first last Fig. 2.3 Intervalle défini par deux itérateurs. first1 last1 1 er intervalle first2 2 ème intervalle Fig. 2.4 Deux intervalles définis par trois itérateurs. La MPTL a besoin de connaître le nombre d itérateurs qui définissent les intervalles à découper et à distribuer aux threads. Pour définir cette information, la librairie fournit à l utilisateur différentes structures (cf. Tab. 2.5). Ces structures sont totalement vides, elles sont juste définies dans la MPTL afin de permettre à l utilisateur de spécifier ce nombre d intervalles à découper. Cette information doit être fournie à l aide d un typedef avec le label niterators : typedef StructureType niterators; où StructureType est l une des structures proposées dans le Tab Par exemple, si un algorithme prend en paramètre deux itérateurs qui définissent un intervalle à distribuer aux threads, il faudrait placer une instruction de ce type dans sa CSPA : public: typedef TwoIterators niterators;

62 52 Chapitre 2. Utilisation de la MPTL Structure TwoIterators ThreeIterators FourIterators Description Utilisation de deux itérateurs. Un intervalle est découpé et distribué aux threads. Utilisation de trois itérateurs. Deux intervalles sont découpés et distribués aux threads. Utilisation de quatre itérateurs. Trois intervalles sont découpés et distribués aux threads. Tab. 2.5 Structures de la MPTL pour définir le nombre d itérateurs. Ainsi, la librairie peut facilement récupérer le nombre d itérateurs avec : typename CSPA::nIterators si CSPA est un type générique qui représente une CSPA. La Fig. 2.5 montre le découpage des intervalles en fonction de la structure employée. Dans cet illustration, les intervalles sont distribués statiquement à 3 threads. Si plusieurs intervalles doivent être découpés, chaque intervalle est découpé identiquement et les sous-intervalles sont distribués aux mêmes threads. Prenons un exemple simple avec l algorithme de la STL equal() qui prend trois itérateurs pour définir deux intervalles. Cet algorithme compare élément par élément ces deux intervalles. En cas de parallélisation, les deux intervalles peuvent être découpés et distribués de façon identique aux threads. La CSPA comporterait alors une instruction du type : public: typedef ThreeIterators niterators; Il faut donc bien comprendre que la MPTL a uniquement besoin de connaître le nombre d itérateurs qui définissent le nombre d intervalles à découper. Pour bien comprendre cette subtilité, prenons par exemple l algorithme search() de la STL. Sa signature est la suivante : template <class Iterator1, class Iterator2> ForwardIterator1 search(iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2);

63 2.3. Construction d un nouvel algorithme 53 Nombre d'itérateurs : 2 Nombre d'intervalles découpés : 1 Structure : TwoIterators first last t 0 t 1 t 2 Nombre d'itérateurs : 3 Nombre d'intervalles découpés : 2 Structure : ThreeIterators first1 last1 t 0 t 1 t 2 first2 t 0 t 1 t 2 Nombre d'itérateurs : 4 Nombre d'intervalles découpés : 3 Structure : FourIterators first1 last1 t 0 t 1 t 2 first2 t 0 t 1 t 2 first3 t 0 t 1 t 2 Fig. 2.5 Utilisation des structures de la MPTL pour le nombre d itérateurs.

64 54 Chapitre 2. Utilisation de la MPTL Cet algorithme permet de rechercher un sous-intervalle dans l intervalle [f irst1, last1) qui est identique à l intervalle [f irst2, last2). Il prend en paramètre quatre itérateurs pour définir deux intervalles. Cependant, si nous voulions le paralléliser avec la MPTL, il faudrait utiliser la structure TwoIterators, car il n y a qu un seul intervalle à partager entre les threads, à savoir [f irst1, last1). En effet, le second intervalle ne doit pas être découpé, car chaque thread doit pouvoir accéder à cet intervalle dans sa totalité. Ainsi, il est nécessaire de spécifier uniquement le nombre d itérateurs qui définissent les intervalles à découper et pas le nombre total d itérateurs utilisés par l algorithme. Type des itérateurs Les algorithmes parallélisés par la MPTL s appliquent sur des intervalles définis à l aide d itérateurs. Nous venons de voir que le nombre d itérateurs et d intervalles peuvent varier en fonction des algorithmes. En plus du nombre d itérateurs qui définissent les intervalles à partager, il est aussi nécessaire de déclarer chaque type d itérateurs utilisés avec des typedef. Le nombre de types d itérateurs à déclarer dépend directement du nombre d itérateurs défini avec TwoIterators, ThreeIterators ou FourIterators. Un algorithme de type TwoIterators sera parallélisé en partageant un intervalle limité par deux itérateurs. Ces deux itérateurs sont obligatoirement du même type, il est donc nécessaire de déclarer un seul type d itérateur, comme nous l avions effectué dans l exemple de la page 47 : typedef Iterator iterator1; où Iterator était un type générique de la CSPA. Ce nom de type est bien entendu arbitraire, seul le label iterator1 est imposé par la librairie. typedef IteratorType iterator1; La librairie peut ainsi à tout moment accéder au type des itérateurs avec CSPA::iterator1. Si elle a par exemple besoin de créer un nouvel itérateur du même type que ceux utilisés dans l algorithme parallélisé, elle peut le faire avec : typename CSPA::iterator1 newiterator; Dans le cas d un algorithme du type ThreeIterators, il faudra alors déclarer deux types d itérateurs : typedef Iterator1 iterator1; typedef Iterator2 iterator2; La librairie pourra alors accéder aux types avec iterator1 et iterator2. Pour un algorithme FourIterators, on aura alors une déclaration du type :

65 2.3. Construction d un nouvel algorithme 55 typedef Iterator1 iterator1; typedef Iterator2 iterator2; typedef Iterator3 iterator3; Pour résumer, chaque type d itérateur qui est susceptible d être utilisé par la librairie à l extérieur de la CSPA doit être redéfini à l aide d un typedef dont le nouveau nom est iterator1, iterator2 ou iterator3. Intervalles à partager Pour effectuer le découpage et la distribution des intervalles, la MPTL a besoin de connaître leur position exacte. Une CSPA doit donc être capable de retourner les itérateurs qui délimitent chaque intervalle qui doit être partagé. Ces informations doivent être fournies sous la forme de fonctions membres. Le nombre de ces fonctions présentes dans la classe dépend directement du type de niterators (avec quelle structure ce type a été défini). Le Tab. 2.6 donne la liste des fonctions à implémenter en fonction de la structure choisie. Type de niterators TwoIterators ThreeIterators FourIterators Fonctions membres IteratorType1 getfirst1() IteratorType1 getlast1() IteratorType1 getfirst1() IteratorType1 getlast1() IteratorType2 getfirst2() IteratorType1 getfirst1() IteratorType1 getlast1() IteratorType2 getfirst2() IteratorType3 getfirst3() Tab. 2.6 Fonctions membres à implémenter en fonction du type défini pour niterators. Dans l exemple étudié à la page 47, nous avions dû définir niterators avec la structure TwoIterators. Ainsi, les deux fonctions getfirst1() et getlast1() sont définies afin de fournir à la librairie les limites de l intervalle à partager : Iterator getfirst1() { return first; Iterator getlast1() { return last; La Fig. 2.5 (page 53) donne un bon aperçu des itérateurs attendus par la librairie en fonction du type de niterators. Type de la valeur retournée Dans notre exemple, l algorithme parallélisé ne retournait pas de valeur. Pour cette raison, la fonction séquentielle de l algorithme

66 56 Chapitre 2. Utilisation de la MPTL ainsi que la fonction applyalgorithm() de la CSPA étaient de type void. Maintenant, lorsque nous avions étudié les fonctions execute() et dynamicexecute() de la MPTL, deux versions étaient à chaque fois proposées afin de pouvoir utiliser des algorithmes qui retournent ou non une valeur. Pour paralléliser un algorithme qui retourne une valeur, il est nécessaire de définir le type de la valeur de retour avec un typedef : typedef Type resulttype; Imaginons que nous désirions paralléliser un algorithme qui retourne un entier. Dans ce cas, il faudrait déclarer le type int comme valeur de retour de cette manière : typedef int resulttype; Ainsi la librairie peut utiliser le type de retour avec le nom resulttype sans avoir besoin de connaître réellement ce type. Classe de réduction Lors de la parallélisation d un algorithme qui retourne une valeur, chaque thread qui exécute cet algorithme sur un sous-intervalle va obtenir une valeur de retour. Afin de pouvoir retourner une valeur unique, il est nécessaire d effectuer une réduction avec les différentes valeurs obtenues. Cette étape de réduction est propre à chaque algorithme. Pour cette raison, il est nécessaire de créer une classe qui va s occuper de l effectuer. Nous étudierons plus tard dans ce chapitre comment créer une classe de réduction (page 58). La Fig. 2.6 illustre le principe de réduction. Dans un premier temps, l intervalle est distribué aux différents threads. Chaque thread applique l algorithme en parallèle et obtient un résultat (r 0, r 1, r 2 ou r 3 ). Finalement, la réduction est effectuée à partir des résultats obtenus par les threads afin d obtenir un résultat unique R qui peut alors être retourné. La valeur de R doit bien entendu être identique à celle que nous aurions obtenu en exécutant l algorithme séquentiellement. Prenons un exemple avec un algorithme qui retourne la somme des éléments contenus dans un intervalle. Chaque thread va alors calculer la somme des éléments présents dans son sous-intervalle. La réduction revient ensuite uniquement à additionner chaque valeur retournée par les threads. On obtient ainsi la somme de chaque élément de l intervalle. La MPTL propose déjà un certain nombre de classes de réduction qui peuvent directement être utilisées. Pour cela, il est nécessaire d inclure un fichier d en-tête : #include "mptl_reduction.h"

67 2.3. Construction d un nouvel algorithme 57 t 0 t 1 t 2 t 3 r 0 r 1 r 2 r 3 R Fig. 2.6 Principe de réduction. Pour indiquer la classe de réduction à utiliser pour effectuer la réduction, il faut utiliser un typedef : typedef ReductionClass reduction; La fonction applyalgorithm() Le dernier élément qui doit être défini dans une CSPA est la fonction membre applyalgorithm(). Cette fonction est appelée par chaque thread afin d appliquer l algorithme sur leur sous-intervalle. La signature de cette fonction peut varier au niveau du nombre de paramètres ainsi qu au niveau du type de la valeur retournée. Pour faire simple, si l algorithme ne retourne pas de valeur, cette fonction sera alors de type void comme nous l avions définie dans l exemple introductif. Maintenant, si l algorithme retourne une valeur, cette fonction doit retourner une valeur de type resulttype. Le nombre de paramètres dépend une fois de plus de la manière dont le type niterators a été défini. Avec TwoIterators, la fonction prendra deux itérateurs en paramètre, avec ThreeIterators elle en prendra trois et finalement avec FourIterators quatre paramètres seront nécessaires. Dans le cas où l algorithme ne retourne pas de valeur, cette fonction doit être déclarée avec l une des signatures suivantes : TwoIterators :

68 58 Chapitre 2. Utilisation de la MPTL void applyalgorithm(iteratortype1 first, IteratorType1 last) ThreeIterators : void applyalgorithm(iteratortype1 first1, IteratorType1 last1, IteratorType2 first2) FourIterators : void applyalgorithm(iteratortype1 first1, IteratorType1 last1, IteratorType2 first2, IteratorType3 first3) Pour un algorithme qui retourne une valeur, la signature des fonctions est parfaitement identique si ce n est que la valeur de retour n est plus void mais resulttype. TwoIterators : resulttype applyalgorithm(iteratortype1 first, IteratorType1 last) ThreeIterators : resulttype applyalgorithm(iteratortype1 first1, IteratorType1 last1, IteratorType2 first2) FourIterators : resulttype applyalgorithm(iteratortype1 first1, IteratorType1 last1, IteratorType2 first2, IteratorType3 first3) Création d une classe de réduction Comme nous l avons décrit auparavant, une classe de réduction permet de calculer le résultat final d un algorithme à partir des résultats intermédiaires obtenus par les threads (Fig. 2.6, page 57). Nous avons également exposé la méthode à employer pour préciser une telle classe dans une CSPA à l aide d un typedef. Bien que la MPTL propose dans le fichier d en-tête mptl reduction.h plusieurs classes de réduction directement utilisables, il peut tout de même être nécessaire de créer sa propre classe de réduction. La construction d une classe de réduction est relativement simple : il suffit de créer une classe qui possède une fonction membre statique nommée reduce(). Cette fonction sera alors appelée par la librairie pour effectuer la phase de réduction. Une classe de réduction doit suivre le modèle (template) suivant :

69 2.3. Construction d un nouvel algorithme 59 class MyReductionClass { public: template <typename T, typename CSPA> static T reduce(const std::vector<t>& results, CSPA algospec) { //... ; La fonction reduce() est donc une fonction générique qui reçoit deux paramètres : const std::vector<t>& results : il s agit d une référence au vecteur qui contient tous les résultats intermédiaires obtenus par les threads. Dans la Fig. 2.6, ce vecteur contiendrait les valeurs r 0, r 1, r 2 et r 3. CSPA algospec : correspond à la CSPA transmise à la librairie. En d autres termes, il s agit de l argument transmis par l utilisateur à la fonction execute() ou dynamicexecute(). Le type générique CSPA représente évidemment une CSPA, tandis que T correspond au type de la valeur retournée par l algorithme. Exemple Lors de la parallélisation de l algorithme equal() de la STL avec la MPTL, chaque thread va appliquer cet algorithme sur un sous-intervalle et va retourner un résultat (un booléen) qui va être stocker dans le vecteur results. La réduction consiste à retourner true si chaque élément du vecteur vaut true et false si au moins un des éléments est à false. class EqualReductionClass { public: template <typename T, typename CSPA> static T reduce(const std::vector<t>& results, CSPA algospec) { typename std::vector<t>::const_iterator it = results.begin();

70 60 Chapitre 2. Utilisation de la MPTL ; for ( ; it!= results.end(); ++it) { if (!(*it)) { return false; return true; Cette réduction parcourt simplement le vecteur results en vérifiant la valeur de chaque élément. Si un élément vaut false, alors elle retourne directement false. Si le parcours arrive à la fin du vecteur, elle retourne true car aucun élément est égal à false dans le vecteur Fonction de convenance La création d un nouvel algorithme pour la MPTL nécessite uniquement l implémentation d une CSPA et d une classe de réduction si l algorithme retourne une valeur. L étape que nous allons décrire dans cette partie est facultative. Elle permet juste d alléger l écriture lorsqu on désire exécuter l algorithme en parallèle avec execute() ou dynamicexecute(). Dans l exemple introductif de la Section sur les CSPA, nous avons décrit la création d un algorithme qui permet d ajouter une valeur à chaque élément d un intervalle. Nous avons ainsi créé une CSPA nommée MPTLAddValue. Si on désire exécuter cet algorithme en parallèle pour ajouter par exemple 12.5 à chaque élément d un vecteur coll, il est nécessaire d écrire : mptl::execute(mptladdvalue<typename vector::iterator, double>(coll.begin(), coll.end(), 12.5)); La création d une instance de MPTLAddValue nécessite la spécification explicite des deux types génériques. Cependant, lorsque nous avons étudié l utilisation de la MPTL pour paralléliser les algorithmes de la STL, nous n étions pas obligés de spécifier les types génériques 4 : mptl::execute(mptl::count(coll.begin(), coll.end(), 10)); Pour pouvoir utiliser MPTLAddValue de la même manière que mptl::count() nous devons comprendre la différence qu il y a entre les deux. Cette différence 4 Comme c est le cas lorsque l on utilise directement les algorithmes de la STL sans les paralléliser avec la MPTL.

71 2.3. Construction d un nouvel algorithme 61 est en réalité très simple : MPTLAddValue est une classe générique tandis que mptl::count() est une fonction générique qui retourne une classe générique. Comme nous l avons appris dans la Section sur les templates, il est obligatoire de spécifier les types génériques lors de l instanciation d une classe générique. Par contre, le compilateur est capable de déduire lui-même les types génériques d une fonction générique. Il n est donc plus nécessaire de les spécifier. La fonction mptl::count() est appelée fonction de convenance (convenience function). Une telle fonction permet de créer une classe générique sans avoir besoin de spécifier les types génériques. Elle doit simplement prendre les mêmes arguments que le constructeur de la classe pour être capable de retourner une instance de la classe correctement initialisée. La signature du constructeur de la classe MPTLAddValue est la suivante : MPTLAddValue(Iterator first_, Iterator last_, const T value_) où Iterator et T sont deux types génériques de la classe. Une fonction de convenance pour cette classe serait alors implémentée ainsi : template <typename Iterator, typename T> MPTLAddValue<Iterator, T> addvalue(iterator first, Iterator last, const T value) { return MPTLAddValue<Iterator, T>(first, last, value); La fonction addvalue() permet donc d instancier un objet de type MPTLAddValue<Iterator, T> sans avoir besoin de spécifier les types génériques Iterator et T. De façon analogue à count(), on peut donc maintenant utiliser la fonction execute() de la manière suivante : mptl::execute(addvalue(coll.begin(), coll.end(), 12.5)); Il n est donc plus nécessaire de spécifier les types génériques, car addvalue() est une fonction générique. On comprend bien que les fonctions de convenance ne sont pas obligatoire dans le cadre de la MPTL, mais elles permettent d alléger l écriture Exemples complets Nous connaissons maintenant tous les éléments nécessaires pour construire correctement des nouveaux algorithmes parallélisable avec la MPTL. Pour nous exercer, nous allons encore discuter de deux exemples complets sur la construction d algorithmes parallèles.

72 62 Chapitre 2. Utilisation de la MPTL Algorithme search Ce premier exemple montre de quelle manière il est possible de paralléliser l algorithme search() de la STL avec la MPTL. Bien que cet algorithme soit directement disponible dans la librairie MPTL, nous allons tout de même essayer de l implémenter, car sa construction est un excellent exemple qui va nous obliger à mettre en pratique tout ce que nous venons d apprendre. L algorithme search() reçoit en paramètre quatre itérateurs, f irst1, last1, first2 et last2, qui forment les intervalles [first1, last1) et [first2, last2). L algorithme essaie de trouver dans le premier intervalle une sous-séquence identique à [first2, last2). Il retourne un itérateur qui pointe sur le premier élément de la sous-séquence trouvée ou l itérateur last1 si aucune sous-séquence n est trouvée. La signature de l algorithme search() de la STL est la suivante : template <typename Iterator1, typename Iterator2> Iterator1 search(iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2); La version parallèle que nous allons écrire devra prendre exactement les mêmes paramètres et devra être capable de transmettre l itérateur trouvé. Les types génériques seront également identiques. CSPA Nous allons commencer par écrire la CSPA de notre algorithme. Pour cela, nous devons définir les caractéristiques de cet algorithme : Seul l intervalle [f irst1, last1) doit être distribué aux threads. Il s agit donc d un algorithme TwoIterators. typedef TwoIterators niterators; Le point précédent implique que le type des itérateurs first1 et last1 doit être défini : typedef Iterator1 iterator1; La valeur de retour est de type Iterator1 : typedef Iterator1 resulttype; Comme il s agit d un algorithme avec une valeur de retour, il est nécessaire de spécifier une classe de réduction : typedef SearchReductionClass reduction; Nous discuterons plus tard de l implémentation de la classe de réduction SearchReductionClass.

73 2.3. Construction d un nouvel algorithme 63 La CSPA doit encore définir les fonctions membres getfirst1() et getlast1() afin de pouvoir spécifier à la librairie l intervalle à distribuer aux threads : Iterator1 getfirst1() { return first1; Iterator1 getlast1() { return last1; Finalement, il est nécessaire de définir la fonction applyalgorithm() qui applique l algorithme sur un sous-intervalle donné. resulttype applyalgorithm(iterator1 first, Iterator1 last) { typename std::iterator_traits<iterator1>::difference_type size1 = std::distance(last, last1); typename std::iterator_traits<iterator2>::difference_type size2 = std::distance(first2, last2); std::advance(last, std::min(size1, size2)); Iterator1 it = std::search(first, last, first2, last2); if (it!= last) return it; return last1; Cette fonction applique donc l algorithme sur le sous-intervalle défini par first et last. On utilise directement l algorithme search de la STL : Iterator1 it = std::search(first, last, first2, last2); Avant cette instruction, l itérateur last est déplacé, car il est possible que la sousséquence recherchée 5 chevauche deux sous-intervalles successifs comme le montre la Fig Dans cette illustration, si l itérateur last n était pas déplacé, ni le thread t 0 ni le thread t 1 trouveraient la sous-séquence recherchée. Par contre, en déplaçant l itérateur last du nombre d éléments présents dans la sous-séquence recherchée 6, le thread t 0 est capable de trouver la sous-séquence. Dans cette figure, on peut remarquer que l itérateur last ne doit pas toujours être déplacé du nombre d éléments présents dans la sous-séquence recherchée si ce déplacement fait sortir l itérateur last de l intervalle [f irst1, last1). C est ce qu il se produit avec les threads t 1 et t 2. Cette opération de déplacement est assurée par les instructions : typename std::iterator_traits<iterator1>::difference_type size1 = std::distance(last, last1); typename std::iterator_traits<iterator2>::difference_type 5 Qui est définie par first2 et last2. 6 Ce déplacement est de 5 éléments dans l illustration 2.7.

74 64 Chapitre 2. Utilisation de la MPTL Sous-séquence recherchée : (Dimension : 5) a c e z t Distribution statique des sous-intervalles : t 0 t 1 t 2 b c u a c e z t k n t i Déplacement de l'itérateur last pour chaque sousintervalle : t 0 b c u a c e z t k n t i b c u a c e z t k n t i t 1 b c u a c e z t k n t i t 2 Fig. 2.7 Déplacement de l itérateur last dans la fonction applyalgorithm(). size2 = std::distance(first2, last2); std::advance(last, std::min(size1, size2)); Lorsque la sous-séquence n est pas trouvée dans un sous-intervalle, l itérateur retourné n est pas last, ce qui serait pourtant en accord avec le comportement de search(), mais last1. Cette subtilité permet de construire une classe de réduction plus simple comme nous le discuterons plus tard. Classe de réduction On se souvient que la classe de réduction reçoit en paramètre deux éléments : un vecteur contenant les résultats obtenus par les threads et la CSPA. En appliquant applyalgorithm(), un thread retourne soit un itérateur appartenant à son sous-intervalle car une sous-séquence a été trouvée soit l itérateur last1. La réduction revient donc à parcourir le vecteur et à rechercher le premier itérateur différent de last1. La fonction reduce() de la classe de

75 2.3. Construction d un nouvel algorithme 65 réduction SearchReductionClass sera donc : template <typename T, typename CSPA> static T reduce(const std::vector<t>& results, CSPA algospec) { typename std::vector<t>::const_iterator it = results.begin(); for ( ; it!= results.end(); ++it) { if (*it!= algospec.getlast1()) { return *it; return algospec.getlast1(); Cette fonction retourne bien entendu l itérateur last1 si tous les itérateurs du vecteur results sont identiques à last1. Fonction de convenance Nous allons créer une fonction de convenance nommée searchalgo() pour la CSPA. Cette fonction se contentera de prendre en paramètre les quatre itérateurs nécessaires pour l exécution de l algorithme et retournera une CSPA. Listing Voici le code complet de notre algorithme parallèle search : la classe de réduction, la CSPA et la fonction de convenance. #include <vector> #include <algorithm> #include "mptl.h" #include "mptl_base.h" // Classe de réduction class SearchReductionClass { public: template <typename T, typename CSPA> static T reduce(const std::vector<t>& results, CSPA algospec) {

76 66 Chapitre 2. Utilisation de la MPTL ; typename std::vector<t>::const_iterator it = results.begin(); for ( ; it!= results.end(); ++it) { if (*it!= algospec.getlast1()) { return *it; return algospec.getlast1(); // CSPA template <typename Iterator1, typename Iterator2> class MPTLSearchAlgo { private: Iterator1 first1, last1; // 1er intervalle Iterator2 first2, last2; // 2ème intervalle public: // Caractéristiques typedef TwoIterators niterators; typedef Iterator1 iterator1; typedef Iterator1 resulttype; typedef SearchReductionClass reduction; // Constructeur MPTLSearchAlgo(Iterator1 first1_, Iterator1 last1_, Iterator2 first2_, Iterator2 last2_) : first1(first1_), last1(last1_), first2(first2_), last2(last2_) { // Accès intervalle distribué Iterator1 getfirst1() { return first1; Iterator1 getlast1() { return last1; resulttype applyalgorithm(iterator1 first, Iterator1 last) { typename std::iterator_traits<iterator1>::difference_type

77 2.3. Construction d un nouvel algorithme 67 size1 = std::distance(last, last1); typename std::iterator_traits<iterator2>::difference_type size2 = std::distance(first2, last2); std::advance(last, std::min(size1, size2)); ; Iterator1 it = std::search(first, last, first2, last2); if (it!= last) return it; return last1; // Fonction de convenance template <typename Iterator1, typename Iterator2> MPTLSearchAlgo<Iterator1, Iterator2> searchalgo(iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2) { return MPTLSearchAlgo<Iterator1, Iterator2>(first1, last1, first2, last2); Il serait maintenant possible d exécuter cet algorithme avec une instruction du genre : execute(searchalgo(coll1.begin(), coll1.end(), coll2.begin(), coll2.end()); Il serait bien entendu tout à fait possible d effectuer une distribution dynamique des charges en utilisant dynamicexecute() à la place de execute(). Ensembles de Julia Pour ce deuxième exemple, nous allons essayer de paralléliser un algorithme qui n appartient pas à la STL. Le but de cet algorithme est de calculer les ensembles de Julia. Les ensembles de Julia sont des objets fractals du plan complexe qui sont définis en considérant la famille de polynômes quadratiques suivante : P c : C C z z 2 + c

78 68 Chapitre 2. Utilisation de la MPTL où c désigne un nombre du plan complexe C. On dénote par K c l ensemble des points z C tel que la suite des itérés P n c (z) est bornée : K c = {z C sup P n c (z) <. Essayons maintenant d aborder le problème des ensembles de Julia avec un angle plus informatique. L algorithme que nous allons écrire doit être capable de calculer l ensemble K c pour un rectangle du plan complexe défini par x min, x max, y min et y max comme le montre la Fig Ce rectangle du plan complexe est alors discrétisé : il est représenté sous la forme d un tableau carré de booléens. Ainsi, chaque élément de ce tableau représente un point du plan complexe discrétisé. Pour chacune des valeurs complexes représentées dans le tableau, appelons-les z ij0, on détermine si la suite des itérés z ijn+1 = P (z ijn ) est bornée. Une bonne heuristique est obtenue en effectuant un certain nombre d itérations n iter pour vérifier si la suite sort du disque de rayon 2. Bien entendu, n iter représente une borne maximale. Ainsi, on peut interrompre l itération dès que z ijn sort du disque. Plan complexe C Espace discrétisé (tableau de booléens) x min y max x max R Discrétisation du plan complexe n n - 1 y min Précision : n Fig. 2.8 Discrétisation du plan complexe pour le calcule d un ensemble de Julia. L algorithme consiste à parcourir entièrement le tableau carré de booléens. Appelons ce tableau espace et définissons sa dimension à n n. Pour chaque élément espace ij : 1. les coordonnées du complexe z ij qu il représente sont calculées en effectuant deux interpolations : (a) la coordonnée réelle a ij s obtient avec : a ij = x max x min n 1 i + x min

79 2.3. Construction d un nouvel algorithme 69 (b) et la coordonnée imaginaire b ij s obtient finalement avec : b ij = y max y min n 1 j + y min z ij est donc égal à a ij + b ij i. 2. le calcul des itérés débute (initialisation du compteur : iter = 0) : (a) si iter n iter, les itérations sont interrompues : étape 3. (b) si z ij sort du disque de rayon 2, les itérations sont stoppées : étape 3. Ce test s effectue avec la comparaison suivante : a 2 ij + b2 ij 2 (c) l itéré suivant est calculé avec : z ij = z 2 ij + c (d) le compteur iter est incrémenté : iter = iter + 1. Retour à l étape 2a. 3. une valeur booléenne est affectée à espace ij en fonction de la valeur de z ij : { true si a 2 ij espace ij = + b2 ij 2, f alse sinon. En C++, les nombres complexes sont directement supportés. On peut également calculer avec la fonction abs() la valeur absolue d un nombre complexe ce qui nous permet de savoir immédiatement si un itéré appartient ou non au disque de rayon 2. Voici de quelle manière il est alors possible d implémenter l algorithme que nous venons de décrire en C++ : double a, b; std::complex<double> z; for (int i = 0; i < n; ++i) { a = i * (xmax - xmin) / (n - 1.0) + xmin; for (int j = 0; j < n; ++j) { b = j * (ymax - ymin) / (n - 1.0) + ymin; z = std::complex<double>(a, b); for (int iter = 0; iter < niter && std::abs(z) <= 2; ++iter) { z = z*z + c; espace[i][j] = std::abs(z) <= 2;

80 70 Chapitre 2. Utilisation de la MPTL Un problème de lenteur d exécution apparaît avec cet algorithme. Il est dû au fait que les opérations std:abs(z)$ et z*z sont très lentes en C++ sur des nombres complexes. Pour remédier à ce problème, nous n allons plus employer de nombre complexe dans cet algorithme. Cette solution est envisageable, car le test du disque de rayon 2 peut tout d abord être effectué ainsi : a 2 ij + b 2 ij 4. Et que le calcul de l itéré suivant peut être réalisé en utilisant deux formules différentes pour chaque coordonnée : a ij = a 2 ij b 2 ij + c r, b ij = 2a ij c i + c i, où c r et c i sont respectivement les coordonnées réelle et imaginaire du complexe c. A partir de là, il n est donc plus nécessaire d utiliser de nombre complexe pour effectuer ces opérations, car a ij, b ij, c r et c i peuvent être représentés sous la forme de nombres réels. L algorithme en C++ devient alors : double a, zr, zi, tmp; double cr = c.real(); // Partir réelle de c double ci = c.imag(); // Partir imaginaire de c for (int i = 0; i < n; ++i) { a = i * (xmax - xmin) / (n - 1.0) + xmin; for (int j = 0; j < n; ++j) { zr = a; zi = j * (ymax - ymin) / (n - 1.0) + ymin; for (int iter = 0; iter < niter && zr*zr + zi*zi <= 4; ++iter) { zrtmp = zr; zr = zr*zr - zi*zi + cr; zi = 2.0*zrTmp*zi + ci; // Conserver pour le calcul de zi espace[i][j] = zr*zr + zi*zi <= 4; La Fig. 2.9 est une image binaire qui représente le tableau espace pour un ensemble de Julia particulier. Un élément du tableau qui vaut true est représenté par un point blanc tandis qu un élément à false est affiché par un point noir. Maintenant que nous avons bien compris le problème des ensembles de Julia

81 2.3. Construction d un nouvel algorithme 71 Fig. 2.9 Exemple de l affichage du tableau espace (c = 1.3, niter = 2000, x min = 2, x max = 2, y min = 2 et y max = 2). ainsi que l algorithme séquentiel qui permet de résoudre ce problème, nous allons essayer de paralléliser cet algorithme avec la MPTL. Le principe de la parallélisation est simple : il faut distribuer les éléments du tableau espace aux différents threads. Ainsi chaque thread va effectuer des itérations sur un sous-espace distinct. Comme d habitude, l algorithme que doit appliquer un thread sera spécifié dans la fonction applyalgorithm() de la CSPA que nous allons écrire. Cependant, deux questions peuvent immédiatement être posées : La MPTL est faite pour travailler sur des intervalles à une dimension. Alors comment travailler avec espace qui est un tableau à 2 dimensions? Comment préciser le sous-espace à un thread sachant qu il doit être spécifié sous la forme d itérateurs 7 et pas sous la forme d indices de tableau? Essayons alors de trouver des réponses à ces deux questions afin de pouvoir écrire une CSPA qui permettra de paralléliser efficacement ce problème des ensembles de Julia. Une première idée serait de représenter la matrice espace sous la forme d un vecteur (en concaténant les colonnes de espace). Cependant, cette solution oblige les threads à effectuer des calculs supplémentaires pour effectuer une transfor- 7 Les paramètres de la fonction applyalgorithm() doivent nécessairement être des itérateurs.

INITIATION AU LANGAGE C SUR PIC DE MICROSHIP

INITIATION AU LANGAGE C SUR PIC DE MICROSHIP COURS PROGRAMMATION INITIATION AU LANGAGE C SUR MICROCONTROLEUR PIC page 1 / 7 INITIATION AU LANGAGE C SUR PIC DE MICROSHIP I. Historique du langage C 1972 : naissance du C dans les laboratoires BELL par

Plus en détail

TP n 2 Concepts de la programmation Objets Master 1 mention IL, semestre 2 Le type Abstrait Pile

TP n 2 Concepts de la programmation Objets Master 1 mention IL, semestre 2 Le type Abstrait Pile TP n 2 Concepts de la programmation Objets Master 1 mention IL, semestre 2 Le type Abstrait Pile Dans ce TP, vous apprendrez à définir le type abstrait Pile, à le programmer en Java à l aide d une interface

Plus en détail

TD3: tableaux avancées, première classe et chaînes

TD3: tableaux avancées, première classe et chaînes TD3: tableaux avancées, première classe et chaînes de caractères 1 Lestableaux 1.1 Élémentsthéoriques Déclaration des tableaux Pour la déclaration des tableaux, deux notations sont possibles. La première

Plus en détail

Introduction à la programmation Travaux pratiques: séance d introduction INFO0201-1

Introduction à la programmation Travaux pratiques: séance d introduction INFO0201-1 Introduction à la programmation Travaux pratiques: séance d introduction INFO0201-1 B. Baert & F. Ludewig Bruno.Baert@ulg.ac.be - F.Ludewig@ulg.ac.be Qu est-ce que la programmation? Programmer Ecrire un

Plus en détail

Premiers Pas en Programmation Objet : les Classes et les Objets

Premiers Pas en Programmation Objet : les Classes et les Objets Chapitre 2 Premiers Pas en Programmation Objet : les Classes et les Objets Dans la première partie de ce cours, nous avons appris à manipuler des objets de type simple : entiers, doubles, caractères, booléens.

Plus en détail

1/24. I passer d un problème exprimé en français à la réalisation d un. I expressions arithmétiques. I structures de contrôle (tests, boucles)

1/24. I passer d un problème exprimé en français à la réalisation d un. I expressions arithmétiques. I structures de contrôle (tests, boucles) 1/4 Objectif de ce cours /4 Objectifs de ce cours Introduction au langage C - Cours Girardot/Roelens Septembre 013 Du problème au programme I passer d un problème exprimé en français à la réalisation d

Plus en détail

Programmation C++ (débutant)/instructions for, while et do...while

Programmation C++ (débutant)/instructions for, while et do...while Programmation C++ (débutant)/instructions for, while et do...while 1 Programmation C++ (débutant)/instructions for, while et do...while Le cours du chapitre 4 : le for, while et do...while La notion de

Plus en détail

Cours d Algorithmique-Programmation 2 e partie (IAP2): programmation 24 octobre 2007impérative 1 / 44 et. structures de données simples

Cours d Algorithmique-Programmation 2 e partie (IAP2): programmation 24 octobre 2007impérative 1 / 44 et. structures de données simples Cours d Algorithmique-Programmation 2 e partie (IAP2): programmation impérative et structures de données simples Introduction au langage C Sandrine Blazy - 1ère année 24 octobre 2007 Cours d Algorithmique-Programmation

Plus en détail

Cours d initiation à la programmation en C++ Johann Cuenin

Cours d initiation à la programmation en C++ Johann Cuenin Cours d initiation à la programmation en C++ Johann Cuenin 11 octobre 2014 2 Table des matières 1 Introduction 5 2 Bases de la programmation en C++ 7 3 Les types composés 9 3.1 Les tableaux.............................

Plus en détail

1. Structure d un programme C. 2. Commentaire: /*..texte */ On utilise aussi le commentaire du C++ qui est valable pour C: 3.

1. Structure d un programme C. 2. Commentaire: /*..texte */ On utilise aussi le commentaire du C++ qui est valable pour C: 3. 1. Structure d un programme C Un programme est un ensemble de fonctions. La fonction "main" constitue le point d entrée pour l exécution. Un exemple simple : #include int main() { printf ( this

Plus en détail

1.6- Génération de nombres aléatoires

1.6- Génération de nombres aléatoires 1.6- Génération de nombres aléatoires 1- Le générateur aléatoire disponible en C++ 2 Création d'un générateur aléatoire uniforme sur un intervalle 3- Génération de valeurs aléatoires selon une loi normale

Plus en détail

Table des matières PRESENTATION DU LANGAGE DS2 ET DE SES APPLICATIONS. Introduction

Table des matières PRESENTATION DU LANGAGE DS2 ET DE SES APPLICATIONS. Introduction PRESENTATION DU LANGAGE DS2 ET DE SES APPLICATIONS Depuis SAS 9.2 TS2M3, SAS propose un nouveau langage de programmation permettant de créer et gérer des tables SAS : le DS2 («Data Step 2»). Ces nouveautés

Plus en détail

Chap III : Les tableaux

Chap III : Les tableaux Chap III : Les tableaux Dans cette partie, on va étudier quelques structures de données de base tels que : Les tableaux (vecteur et matrice) Les chaînes de caractères LA STRUCTURE DE TABLEAU Introduction

Plus en détail

INFO-F-105 Language de programmation I Séance VI

INFO-F-105 Language de programmation I Séance VI INFO-F-105 Language de programmation I Séance VI Jérôme Dossogne Année académique 2008 2009 Un grand merci à Yves Roggeman pour ses relectures et remarques des codes et commentaires qui ont contribuées

Plus en détail

Cours de C++ François Laroussinie. 2 novembre 2005. Dept. d Informatique, ENS de Cachan

Cours de C++ François Laroussinie. 2 novembre 2005. Dept. d Informatique, ENS de Cachan Cours de C++ François Laroussinie Dept. d Informatique, ENS de Cachan 2 novembre 2005 Première partie I Introduction Introduction Introduction Algorithme et programmation Algorithme: méthode pour résoudre

Plus en détail

Bases de programmation. Cours 5. Structurer les données

Bases de programmation. Cours 5. Structurer les données Bases de programmation. Cours 5. Structurer les données Pierre Boudes 1 er décembre 2014 This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License. Types char et

Plus en détail

Cours d Algorithmique et de Langage C 2005 - v 3.0

Cours d Algorithmique et de Langage C 2005 - v 3.0 Cours d Algorithmique et de Langage C 2005 - v 3.0 Bob CORDEAU cordeau@onera.fr Mesures Physiques IUT d Orsay 15 mai 2006 Avant-propos Avant-propos Ce cours en libre accès repose sur trois partis pris

Plus en détail

Utilisation d objets : String et ArrayList

Utilisation d objets : String et ArrayList Chapitre 6 Utilisation d objets : String et ArrayList Dans ce chapitre, nous allons aborder l utilisation d objets de deux classes prédéfinies de Java d usage très courant. La première, nous l utilisons

Plus en détail

03/04/2007. Tâche 1 Tâche 2 Tâche 3. Système Unix. Time sharing

03/04/2007. Tâche 1 Tâche 2 Tâche 3. Système Unix. Time sharing 3/4/27 Programmation Avancée Multimédia Multithreading Benoît Piranda Équipe SISAR Université de Marne La Vallée Besoin Programmes à traitements simultanés Réseau Réseau Afficher une animation en temps

Plus en détail

Éléments d informatique Cours 3 La programmation structurée en langage C L instruction de contrôle if

Éléments d informatique Cours 3 La programmation structurée en langage C L instruction de contrôle if Éléments d informatique Cours 3 La programmation structurée en langage C L instruction de contrôle if Pierre Boudes 28 septembre 2011 This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike

Plus en détail

Génie Logiciel avec Ada. 4 février 2013

Génie Logiciel avec Ada. 4 février 2013 Génie Logiciel 4 février 2013 Plan I. Généralités II. Structures linéaires III. Exceptions IV. Structures arborescentes V. Dictionnaires I. Principes II. Notions propres à la POO I. Principes Chapitre

Plus en détail

Introduction à la Programmation Parallèle: MPI

Introduction à la Programmation Parallèle: MPI Introduction à la Programmation Parallèle: MPI Frédéric Gava et Gaétan Hains L.A.C.L Laboratoire d Algorithmique, Complexité et Logique Cours du M2 SSI option PSSR Plan 1 Modèle de programmation 2 3 4

Plus en détail

EPREUVE OPTIONNELLE d INFORMATIQUE CORRIGE

EPREUVE OPTIONNELLE d INFORMATIQUE CORRIGE EPREUVE OPTIONNELLE d INFORMATIQUE CORRIGE QCM Remarque : - A une question correspond au moins 1 réponse juste - Cocher la ou les bonnes réponses Barème : - Une bonne réponse = +1 - Pas de réponse = 0

Plus en détail

et Programmation Objet

et Programmation Objet FACULTE POLYTECHNIQUE DE MONS Service d Informatique et Programmation Objet Mohammed Benjelloun 2 ème Bachelier Année académique 2008-2009 Table des matières Avant-propos--------------------------------------------------------------------------

Plus en détail

Licence ST Université Claude Bernard Lyon I LIF1 : Algorithmique et Programmation C Bases du langage C 1 Conclusion de la dernière fois Introduction de l algorithmique générale pour permettre de traiter

Plus en détail

Plan du cours. Historique du langage http://www.oracle.com/technetwork/java/index.html. Nouveautés de Java 7

Plan du cours. Historique du langage http://www.oracle.com/technetwork/java/index.html. Nouveautés de Java 7 Université Lumière Lyon 2 Faculté de Sciences Economiques et Gestion KHARKIV National University of Economic Introduction au Langage Java Master Informatique 1 ère année Julien Velcin http://mediamining.univ-lyon2.fr/velcin

Plus en détail

Chapitre VI- La validation de la composition.

Chapitre VI- La validation de la composition. Chapitre VI- La validation de la composition. Objectifs du chapitre : Expliquer les conséquences de l utilisation de règles de typage souples dans SEP. Présenter le mécanisme de validation des connexions

Plus en détail

Le langage C++ est un langage de programmation puissant, polyvalent, on serait presque tenté de dire universel, massivement utilisé dans l'industrie

Le langage C++ est un langage de programmation puissant, polyvalent, on serait presque tenté de dire universel, massivement utilisé dans l'industrie Chapitre I : Les bases du C++ Le langage C++ est un langage de programmation puissant, polyvalent, on serait presque tenté de dire universel, massivement utilisé dans l'industrie du logiciel, et ce depuis

Plus en détail

as Architecture des Systèmes d Information

as Architecture des Systèmes d Information Plan Plan Programmation - Introduction - Nicolas Malandain March 14, 2005 Introduction à Java 1 Introduction Présentation Caractéristiques Le langage Java 2 Types et Variables Types simples Types complexes

Plus en détail

Projet L1, S2, 2015: Simulation de fourmis, Soutenance la semaine du 4 mai.

Projet L1, S2, 2015: Simulation de fourmis, Soutenance la semaine du 4 mai. Projet L1, S2, 2015: Simulation de fourmis, Soutenance la semaine du 4 mai. 1 Introduction On considère une grille de 20 lignes 20 colonnes. Une case de la grille peut être vide, ou contenir une et une

Plus en détail

Conventions d écriture et outils de mise au point

Conventions d écriture et outils de mise au point Logiciel de base Première année par alternance Responsable : Christophe Rippert Christophe.Rippert@Grenoble-INP.fr Introduction Conventions d écriture et outils de mise au point On va utiliser dans cette

Plus en détail

Approche Contract First

Approche Contract First Exemple HelpDesk Approche Contract First Développement d un premier web service en utilisant l approche contract first (ou WSDL First) Écriture du wsdl avant d écrire le code java Autre possibilité implementation

Plus en détail

Logiciel Libre Cours 3 Fondements: Génie Logiciel

Logiciel Libre Cours 3 Fondements: Génie Logiciel Logiciel Libre Cours 3 Fondements: Génie Logiciel Stefano Zacchiroli zack@pps.univ-paris-diderot.fr Laboratoire PPS, Université Paris Diderot 2013 2014 URL http://upsilon.cc/zack/teaching/1314/freesoftware/

Plus en détail

CHAPITRE V. Recherche et tri

CHAPITRE V. Recherche et tri Cherchez et vous trouverez,... car qui cherche trouve. Matthieu 7 7-8 et Luc 11 9-10 CHAPITRE V Recherche et tri Objectif. Comprendre les techniques de base pour organiser des données ordonnées. Ce chapitre

Plus en détail

Les structures de données. Rajae El Ouazzani

Les structures de données. Rajae El Ouazzani Les structures de données Rajae El Ouazzani Les arbres 2 1- Définition de l arborescence Une arborescence est une collection de nœuds reliés entre eux par des arcs. La collection peut être vide, cad l

Plus en détail

Introduction au langage C

Introduction au langage C Introduction au langage C Cours 1: Opérations de base et premier programme Alexis Lechervy Alexis Lechervy (UNICAEN) Introduction au langage C 1 / 23 Les premiers pas Sommaire 1 Les premiers pas 2 Les

Plus en détail

Java Licence Professionnelle CISII, 2009-10

Java Licence Professionnelle CISII, 2009-10 Java Licence Professionnelle CISII, 2009-10 Cours 4 : Programmation structurée (c) http://www.loria.fr/~tabbone/cours.html 1 Principe - Les méthodes sont structurées en blocs par les structures de la programmation

Plus en détail

IN 102 - Cours 1. 1 Informatique, calculateurs. 2 Un premier programme en C

IN 102 - Cours 1. 1 Informatique, calculateurs. 2 Un premier programme en C IN 102 - Cours 1 Qu on le veuille ou non, les systèmes informatisés sont désormais omniprésents. Même si ne vous destinez pas à l informatique, vous avez de très grandes chances d y être confrontés en

Plus en détail

Projet Active Object

Projet Active Object Projet Active Object TAO Livrable de conception et validation Romain GAIDIER Enseignant : M. Noël PLOUZEAU, ISTIC / IRISA Pierre-François LEFRANC Master 2 Informatique parcours MIAGE Méthodes Informatiques

Plus en détail

Algorithmique, Structures de données et langage C

Algorithmique, Structures de données et langage C UNIVERSITE PAUL SABATIER TOULOUSE III Algorithmique, Structures de données et langage C L3 IUP AISEM/ICM Janvier 2005 J.M. ENJALBERT Chapitre 1 Rappels et compléments de C 1.1 Structures Une structure

Plus en détail

I. Introduction aux fonctions : les fonctions standards

I. Introduction aux fonctions : les fonctions standards Chapitre 3 : Les fonctions en C++ I. Introduction aux fonctions : les fonctions standards A. Notion de Fonction Imaginons que dans un programme, vous ayez besoin de calculer une racine carrée. Rappelons

Plus en détail

INTRODUCTION A JAVA. Fichier en langage machine Exécutable

INTRODUCTION A JAVA. Fichier en langage machine Exécutable INTRODUCTION A JAVA JAVA est un langage orienté-objet pur. Il ressemble beaucoup à C++ au niveau de la syntaxe. En revanche, ces deux langages sont très différents dans leur structure (organisation du

Plus en détail

C++ Programmer. en langage. 8 e édition. Avec une intro aux design patterns et une annexe sur la norme C++11. Claude Delannoy

C++ Programmer. en langage. 8 e édition. Avec une intro aux design patterns et une annexe sur la norme C++11. Claude Delannoy Claude Delannoy Programmer en langage C++ 8 e édition Avec une intro aux design patterns et une annexe sur la norme C++11 Groupe Eyrolles, 1993-2011. Groupe Eyrolles, 2014, pour la nouvelle présentation,

Plus en détail

DE L ALGORITHME AU PROGRAMME INTRO AU LANGAGE C 51

DE L ALGORITHME AU PROGRAMME INTRO AU LANGAGE C 51 DE L ALGORITHME AU PROGRAMME INTRO AU LANGAGE C 51 PLAN DU COURS Introduction au langage C Notions de compilation Variables, types, constantes, tableaux, opérateurs Entrées sorties de base Structures de

Plus en détail

Travaux pratiques. Compression en codage de Huffman. 1.3. Organisation d un projet de programmation

Travaux pratiques. Compression en codage de Huffman. 1.3. Organisation d un projet de programmation Université de Savoie Module ETRS711 Travaux pratiques Compression en codage de Huffman 1. Organisation du projet 1.1. Objectifs Le but de ce projet est d'écrire un programme permettant de compresser des

Plus en détail

Algorithmique et Programmation, IMA

Algorithmique et Programmation, IMA Algorithmique et Programmation, IMA Cours 2 : C Premier Niveau / Algorithmique Université Lille 1 - Polytech Lille Notations, identificateurs Variables et Types de base Expressions Constantes Instructions

Plus en détail

Chapitre 2. Classes et objets

Chapitre 2. Classes et objets Chapitre 2: Classes et Objets 1/10 Chapitre 2 Classes et objets Chapitre 2: Classes et Objets 2/10 Approche Orientée Objet Idée de base de A.O.O. repose sur l'observation de la façon dont nous procédons

Plus en détail

TP : Gestion d une image au format PGM

TP : Gestion d une image au format PGM TP : Gestion d une image au format PGM Objectif : L objectif du sujet est de créer une classe de manipulation d images au format PGM (Portable GreyMap), et de programmer des opérations relativement simples

Plus en détail

Une introduction à Java

Une introduction à Java Une introduction à Java IFT 287 (Semaine 1) UNIVERSITÉ DE SHERBROOKE 1 Java - Historique Développé par Sun Microsystems en 1994 Inventeur James Gosling (canadien!) Objectif langage sûr (fortement typé)

Plus en détail

PROJET ALGORITHMIQUE ET PROGRAMMATION II

PROJET ALGORITHMIQUE ET PROGRAMMATION II PROJET 1 ALGORITHMIQUE ET PROGRAMMATION II CONTENU DU RAPPORT A RENDRE : o Fiche signalétique du binôme o Listing des différents fichiers sources o CD sources o Il sera tenu compte de la présentation du

Plus en détail

Centre CPGE TSI - Safi 2010/2011. Algorithmique et programmation :

Centre CPGE TSI - Safi 2010/2011. Algorithmique et programmation : Algorithmique et programmation : STRUCTURES DE DONNÉES A. Structure et enregistrement 1) Définition et rôle des structures de données en programmation 1.1) Définition : En informatique, une structure de

Plus en détail

# let rec concat l1 l2 = match l1 with [] -> l2 x::l 1 -> x::(concat l 1 l2);; val concat : a list -> a list -> a list = <fun>

# let rec concat l1 l2 = match l1 with [] -> l2 x::l 1 -> x::(concat l 1 l2);; val concat : a list -> a list -> a list = <fun> 94 Programmation en OCaml 5.4.8. Concaténation de deux listes Définissons maintenant la fonction concat qui met bout à bout deux listes. Ainsi, si l1 et l2 sont deux listes quelconques, concat l1 l2 constitue

Plus en détail

IRL : Simulation distribuée pour les systèmes embarqués

IRL : Simulation distribuée pour les systèmes embarqués IRL : Simulation distribuée pour les systèmes embarqués Yassine El Khadiri, 2 ème année Ensimag, Grenoble INP Matthieu Moy, Verimag Denis Becker, Verimag 19 mai 2015 1 Table des matières 1 MPI et la sérialisation

Plus en détail

Introduction au pricing d option en finance

Introduction au pricing d option en finance Introduction au pricing d option en finance Olivier Pironneau Cours d informatique Scientifique 1 Modélisation du prix d un actif financier Les actions, obligations et autres produits financiers cotés

Plus en détail

Claude Delannoy. 3 e édition C++

Claude Delannoy. 3 e édition C++ Claude Delannoy 3 e édition Exercices Exercices C++ en en langage langage delc++ titre 4/07/07 15:19 Page 2 Exercices en langage C++ AUX EDITIONS EYROLLES Du même auteur C. Delannoy. Apprendre le C++.

Plus en détail

Langage et Concepts de ProgrammationOrientée-Objet 1 / 40

Langage et Concepts de ProgrammationOrientée-Objet 1 / 40 Déroulement du cours Introduction Concepts Java Remarques Langage et Concepts de Programmation Orientée-Objet Gauthier Picard École Nationale Supérieure des Mines de Saint-Étienne gauthier.picard@emse.fr

Plus en détail

1 Recherche en table par balayage

1 Recherche en table par balayage 1 Recherche en table par balayage 1.1 Problème de la recherche en table Une table désigne une liste ou un tableau d éléments. Le problème de la recherche en table est celui de la recherche d un élément

Plus en détail

Introduction à la programmation orientée objet, illustrée par le langage C++ Patrick Cégielski cegielski@u-pec.fr

Introduction à la programmation orientée objet, illustrée par le langage C++ Patrick Cégielski cegielski@u-pec.fr Introduction à la programmation orientée objet, illustrée par le langage C++ Patrick Cégielski cegielski@u-pec.fr Mars 2002 Pour Irène et Marie Legal Notice Copyright c 2002 Patrick Cégielski Université

Plus en détail

Principes. 2A-SI 3 Prog. réseau et systèmes distribués 3. 3 Programmation en CORBA. Programmation en Corba. Stéphane Vialle

Principes. 2A-SI 3 Prog. réseau et systèmes distribués 3. 3 Programmation en CORBA. Programmation en Corba. Stéphane Vialle 2A-SI 3 Prog. réseau et systèmes distribués 3. 3 Programmation en CORBA Stéphane Vialle Stephane.Vialle@supelec.fr http://www.metz.supelec.fr/~vialle 1 Principes 2 Architecture 3 4 Aperçu d utilisation

Plus en détail

Définitions. Numéro à préciser. (Durée : )

Définitions. Numéro à préciser. (Durée : ) Numéro à préciser (Durée : ) On étudie dans ce problème l ordre lexicographique pour les mots sur un alphabet fini et plusieurs constructions des cycles de De Bruijn. Les trois parties sont largement indépendantes.

Plus en détail

Programmer en JAVA. par Tama (tama@via.ecp.fr( tama@via.ecp.fr)

Programmer en JAVA. par Tama (tama@via.ecp.fr( tama@via.ecp.fr) Programmer en JAVA par Tama (tama@via.ecp.fr( tama@via.ecp.fr) Plan 1. Présentation de Java 2. Les bases du langage 3. Concepts avancés 4. Documentation 5. Index des mots-clés 6. Les erreurs fréquentes

Plus en détail

Recherche dans un tableau

Recherche dans un tableau Chapitre 3 Recherche dans un tableau 3.1 Introduction 3.1.1 Tranche On appelle tranche de tableau, la donnée d'un tableau t et de deux indices a et b. On note cette tranche t.(a..b). Exemple 3.1 : 3 6

Plus en détail

Cours 1: Java et les objets

Cours 1: Java et les objets Ressources Les interface homme-machine et le langage Java DUT première année Henri Garreta, Faculté des Sciences (Luminy) Cyril Pain-Barre & Sébastien Nedjar, IUT d Aix-Marseille (Aix) Cours 1: infodoc.iut.univ-aix.fr/~ihm/

Plus en détail

Présentation du PL/SQL

Présentation du PL/SQL I Présentation du PL/ Copyright Oracle Corporation, 1998. All rights reserved. Objectifs du Cours A la fin de ce chapitre, vous saurez : Décrire l intéret du PL/ Décrire l utilisation du PL/ pour le développeur

Plus en détail

Prénom : Matricule : Sigle et titre du cours Groupe Trimestre INF1101 Algorithmes et structures de données Tous H2004. Loc Jeudi 29/4/2004

Prénom : Matricule : Sigle et titre du cours Groupe Trimestre INF1101 Algorithmes et structures de données Tous H2004. Loc Jeudi 29/4/2004 Questionnaire d'examen final INF1101 Sigle du cours Nom : Signature : Prénom : Matricule : Sigle et titre du cours Groupe Trimestre INF1101 Algorithmes et structures de données Tous H2004 Professeur(s)

Plus en détail

Composants génériques de calcul scientifique

Composants génériques de calcul scientifique Composants génériques de calcul scientifique T. Géraud et A. Duret-Lutz RAPPORT TECHNIQUE 9901 MARS 1999 Laboratoire de Recherche et Développement d EPITA 14-16, rue Voltaire 94276 Le Kremlin-Bicêtre cedex

Plus en détail

Cours Informatique Master STEP

Cours Informatique Master STEP Cours Informatique Master STEP Bases de la programmation: Compilateurs/logiciels Algorithmique et structure d'un programme Programmation en langage structuré (Fortran 90) Variables, expressions, instructions

Plus en détail

Initiation à la programmation en Python

Initiation à la programmation en Python I-Conventions Initiation à la programmation en Python Nom : Prénom : Une commande Python sera écrite en caractère gras. Exemples : print 'Bonjour' max=input("nombre maximum autorisé :") Le résultat de

Plus en détail

Les chaînes de caractères

Les chaînes de caractères Les chaînes de caractères Dans un programme informatique, les chaînes de caractères servent à stocker les informations non numériques comme par exemple une liste de nom de personne ou des adresses. Il

Plus en détail

Le langage C. Séance n 4

Le langage C. Séance n 4 Université Paris-Sud 11 Institut de Formation des Ingénieurs Remise à niveau INFORMATIQUE Année 2007-2008 Travaux pratiques d informatique Le langage C Séance n 4 But : Vous devez maîtriser à la fin de

Plus en détail

Travaux Dirigés n 1 : chaînes de caractères

Travaux Dirigés n 1 : chaînes de caractères UE LE315 Travaux Dirigés n 1 : chaînes de caractères Exercice 1 Ecrire une fonction int nombre_caract(char *chaîne) qui retourne la taille d une chaîne de caractères. Exercice 2 Ecrire la fonction void

Plus en détail

TP1 : Initiation à Java et Eclipse

TP1 : Initiation à Java et Eclipse TP1 : Initiation à Java et Eclipse 1 TP1 : Initiation à Java et Eclipse Systèmes d Exploitation Avancés I. Objectifs du TP Ce TP est une introduction au langage Java. Il vous permettra de comprendre les

Plus en détail

Structure d un programme et Compilation Notions de classe et d objet Syntaxe

Structure d un programme et Compilation Notions de classe et d objet Syntaxe Cours1 Structure d un programme et Compilation Notions de classe et d objet Syntaxe POO 1 Programmation Orientée Objet Un ensemble d objet qui communiquent Pourquoi POO Conception abstraction sur les types

Plus en détail

Introduction à l héritage en C++

Introduction à l héritage en C++ Algorithmique/Langage 1ère année Introduction à l héritage en C++ Yacine BELLIK IUT d Orsay Yacine.Bellik@iut-orsay.fr 1 Bibliographie Ce cours est basé sur le livre suivant : Programmer en C++, 5ème édition

Plus en détail

Java Licence Professionnelle CISII, 2009-2010. Cours 2 : Classes et Objets

Java Licence Professionnelle CISII, 2009-2010. Cours 2 : Classes et Objets Licence Professionnelle CISII, 2009-2010 Cours 2 : Classes et Objets 1 Classes et Objets Objectifs des LOO : - Manipuler des objets - Découper les programmes suivant les types des objets manipulés - Regrouper

Plus en détail

Info0101 Intro. à l'algorithmique et à la programmation. Cours 3. Le langage Java

Info0101 Intro. à l'algorithmique et à la programmation. Cours 3. Le langage Java Info0101 Intro. à l'algorithmique et à la programmation Cours 3 Le langage Java Pierre Delisle, Cyril Rabat et Christophe Jaillet Université de Reims Champagne-Ardenne Département de Mathématiques et Informatique

Plus en détail

Programmation en Java IUT GEII (MC-II1) 1

Programmation en Java IUT GEII (MC-II1) 1 Programmation en Java IUT GEII (MC-II1) 1 Christophe BLANC - Paul CHECCHIN IUT Montluçon Université Blaise Pascal Novembre 2009 Christophe BLANC - Paul CHECCHIN Programmation en Java IUT GEII (MC-II1)

Plus en détail

Quelques patterns pour la persistance des objets avec DAO DAO. Principe de base. Utilité des DTOs. Le modèle de conception DTO (Data Transfer Object)

Quelques patterns pour la persistance des objets avec DAO DAO. Principe de base. Utilité des DTOs. Le modèle de conception DTO (Data Transfer Object) Quelques patterns pour la persistance des objets avec DAO Ce cours présente des modèles de conception utilisés pour effectuer la persistance des objets Université de Nice Sophia-Antipolis Version 1.4 30/8/07

Plus en détail

LMI 2. Programmation Orientée Objet POO - Cours 9. Said Jabbour. jabbour@cril.univ-artois.fr www.cril.univ-artois.fr/~jabbour

LMI 2. Programmation Orientée Objet POO - Cours 9. Said Jabbour. jabbour@cril.univ-artois.fr www.cril.univ-artois.fr/~jabbour LMI 2 Programmation Orientée Objet POO - Cours 9 Said Jabbour jabbour@cril.univ-artois.fr www.cril.univ-artois.fr/~jabbour CRIL UMR CNRS 8188 Faculté des Sciences - Univ. Artois Février 2011 Les collections

Plus en détail

Cours intensif Java. 1er cours: de C à Java. Enrica DUCHI LIAFA, Paris 7. Septembre 2009. Enrica.Duchi@liafa.jussieu.fr

Cours intensif Java. 1er cours: de C à Java. Enrica DUCHI LIAFA, Paris 7. Septembre 2009. Enrica.Duchi@liafa.jussieu.fr . Cours intensif Java 1er cours: de C à Java Septembre 2009 Enrica DUCHI LIAFA, Paris 7 Enrica.Duchi@liafa.jussieu.fr LANGAGES DE PROGRAMMATION Pour exécuter un algorithme sur un ordinateur il faut le

Plus en détail

Une bibliothèque de templates pour CUDA

Une bibliothèque de templates pour CUDA Une bibliothèque de templates pour CUDA Sylvain Collange, Marc Daumas et David Defour Montpellier, 16 octobre 2008 Types de parallèlisme de données Données indépendantes n threads pour n jeux de données

Plus en détail

Structure fonctionnelle d un SGBD

Structure fonctionnelle d un SGBD Fichiers et Disques Structure fonctionnelle d un SGBD Requetes Optimiseur de requetes Operateurs relationnels Methodes d acces Gestion de tampon Gestion de disque BD 1 Fichiers et Disques Lecture : Transfert

Plus en détail

Cours 7 : Utilisation de modules sous python

Cours 7 : Utilisation de modules sous python Cours 7 : Utilisation de modules sous python 2013/2014 Utilisation d un module Importer un module Exemple : le module random Importer un module Exemple : le module random Importer un module Un module est

Plus en détail

Introduction à MATLAB R

Introduction à MATLAB R Introduction à MATLAB R Romain Tavenard 10 septembre 2009 MATLAB R est un environnement de calcul numérique propriétaire orienté vers le calcul matriciel. Il se compose d un langage de programmation, d

Plus en détail

Algorithmique et programmation : les bases (VBA) Corrigé

Algorithmique et programmation : les bases (VBA) Corrigé PAD INPT ALGORITHMIQUE ET PROGRAMMATION 1 Cours VBA, Semaine 1 mai juin 2006 Corrigé Résumé Ce document décrit l écriture dans le langage VBA des éléments vus en algorithmique. Table des matières 1 Pourquoi

Plus en détail

OCL - Object Constraint Language

OCL - Object Constraint Language OCL - Object Constraint Language Laëtitia Matignon laetitia.matignon@univ-lyon1.fr Département Informatique - Polytech Lyon Université Claude Bernard Lyon 1 2012-2013 Laëtitia Matignon SIMA - OCL - Object

Plus en détail

TP Bases de données réparties

TP Bases de données réparties page 1 TP Bases de données réparties requêtes réparties Version corrigée Auteur : Hubert Naacke, révision 5 mars 2003 Mots-clés: bases de données réparties, fragmentation, schéma de placement, lien, jointure

Plus en détail

COMMENT REDIGER UN RAPPORT TECHNIQUE?

COMMENT REDIGER UN RAPPORT TECHNIQUE? COMMENT REDIGER UN RAPPORT TECHNIQUE? Christiaens Sébastien Université de Liège Département PROMETHEE Institut de Mécanique et de Génie Civil, Bât. B52 Chemin des Chevreuils, 1 B-4000 Liège, Belgique Janvier

Plus en détail

ECR_DESCRIPTION CHAR(80), ECR_MONTANT NUMBER(10,2) NOT NULL, ECR_SENS CHAR(1) NOT NULL) ;

ECR_DESCRIPTION CHAR(80), ECR_MONTANT NUMBER(10,2) NOT NULL, ECR_SENS CHAR(1) NOT NULL) ; RÈGLES A SUIVRE POUR OPTIMISER LES REQUÊTES SQL Le but de ce rapport est d énumérer quelques règles pratiques à appliquer dans l élaboration des requêtes. Il permettra de comprendre pourquoi certaines

Plus en détail

Sage 100 CRM - Guide de la Fusion Avancée Version 8. Mise à jour : 2015 version 8

Sage 100 CRM - Guide de la Fusion Avancée Version 8. Mise à jour : 2015 version 8 Sage 100 CRM - Guide de la Fusion Avancée Version 8 Mise à jour : 2015 version 8 Composition du progiciel Votre progiciel est composé d un boîtier de rangement comprenant : le cédérom sur lequel est enregistré

Plus en détail

Cours d introduction à l informatique. Partie 2 : Comment écrire un algorithme? Qu est-ce qu une variable? Expressions et instructions

Cours d introduction à l informatique. Partie 2 : Comment écrire un algorithme? Qu est-ce qu une variable? Expressions et instructions Cours d introduction à l informatique Partie 2 : Comment écrire un algorithme? Qu est-ce qu une variable? Expressions et instructions Qu est-ce qu un Une recette de cuisine algorithme? Protocole expérimental

Plus en détail

Argument-fetching dataflow machine de G.R. Gao et J.B. Dennis (McGill, 1988) = machine dataflow sans flux de données

Argument-fetching dataflow machine de G.R. Gao et J.B. Dennis (McGill, 1988) = machine dataflow sans flux de données EARTH et Threaded-C: Éléments clés du manuel de références de Threaded-C Bref historique de EARTH et Threaded-C Ancêtres de l architecture EARTH: Slide 1 Machine à flux de données statique de J.B. Dennis

Plus en détail

TP1. Outils Java Eléments de correction

TP1. Outils Java Eléments de correction c sep. 2008, v2.1 Java TP1. Outils Java Eléments de correction Sébastien Jean Le but de ce TP, sur une séance, est de se familiariser avec les outils de développement et de documentation Java fournis par

Plus en détail

PROJET 1 : BASE DE DONNÉES REPARTIES

PROJET 1 : BASE DE DONNÉES REPARTIES PROJET 1 : BASE DE DONNÉES REPARTIES GESTION D UNE BANQUE Elèves : David Bréchet Frédéric Jacot Charles Secrétan DONNÉES DU PROJET SSC - Bases de Données II Laboratoire de Bases de Données BD réparties

Plus en détail

UEO11 COURS/TD 1. nombres entiers et réels codés en mémoire centrale. Caractères alphabétiques et caractères spéciaux.

UEO11 COURS/TD 1. nombres entiers et réels codés en mémoire centrale. Caractères alphabétiques et caractères spéciaux. UEO11 COURS/TD 1 Contenu du semestre Cours et TDs sont intégrés L objectif de ce cours équivalent a 6h de cours, 10h de TD et 8h de TP est le suivant : - initiation à l algorithmique - notions de bases

Plus en détail

LES TYPES DE DONNÉES DU LANGAGE PASCAL

LES TYPES DE DONNÉES DU LANGAGE PASCAL LES TYPES DE DONNÉES DU LANGAGE PASCAL 75 LES TYPES DE DONNÉES DU LANGAGE PASCAL CHAPITRE 4 OBJECTIFS PRÉSENTER LES NOTIONS D ÉTIQUETTE, DE CONS- TANTE ET DE IABLE DANS LE CONTEXTE DU LAN- GAGE PASCAL.

Plus en détail

Cours 1 : Introduction. Langages objets. but du module. contrôle des connaissances. Pourquoi Java? présentation du module. Présentation de Java

Cours 1 : Introduction. Langages objets. but du module. contrôle des connaissances. Pourquoi Java? présentation du module. Présentation de Java Langages objets Introduction M2 Pro CCI, Informatique Emmanuel Waller, LRI, Orsay présentation du module logistique 12 blocs de 4h + 1 bloc 2h = 50h 1h15 cours, 45mn exercices table, 2h TD machine page

Plus en détail

Programmation stochastique

Programmation stochastique Programmation stochastique (Partie 1) IFT-6512 Hiver 2008 Présentation succinte COIN-OR? COmputational INfrastructure for Operations Research. Préalablement, COIN-OR tenait pour Common Optimization INterface

Plus en détail

Initiation. àl algorithmique et à la programmation. en C

Initiation. àl algorithmique et à la programmation. en C Initiation àl algorithmique et à la programmation en C Initiation àl algorithmique et à la programmation en C Cours avec 129 exercices corrigés Illustration de couverture : alwyncooper - istock.com Dunod,

Plus en détail

Langage Java. Classe de première SI

Langage Java. Classe de première SI Langage Java Table des matières 1. Premiers pas...2 1.1. Introduction...2 1.2. Mon premier programme...2 1.3. Les commentaires...2 2. Les variables et les opérateurs...2 3. La classe Scanner...3 4. Les

Plus en détail