Programmation parallèle

Slides:



Advertisements
Présentations similaires
MPI (Message Passing Interface)
Advertisements

Informatique 1A Langage C 6 ème séance 1. Objectifs de la séance 6  Allocation dynamique de mémoire  Application à la création de tableaux 2.
Interactivité et Lingo Interactivité - spécifier le déroulement en fonction des actions de l’usager Ex: Déroulement si l’usager clique Choix dans une liste.
CHAftITREI ARCHITECTURE de BASE. Modèle de Von Neumann Langage d’assemblage1 John Von Neumann est à l'origine d'un modèle de machine universelle de traitement.
1 Programmation Orientée Objet ● Qu'est-ce qu'un objet ● Collaboration des objets ● Les classes ● Relations entre les classes – “Utilise”, “Contient”,
Guide de l'enseignant SolidWorks, leçon 1 Nom de l'établissement Nom de l'enseignant Date.
CINI – Li115 1 Semaine 11 Les pointeurs (suite) ● Tableaux et pointeurs ● Questions sur les pointeurs.
TP 1 BIS Programmation structurée à l’aide de fonctions (FC) et de bloc fonctionnels (FB)
Comment utiliser le débogueur de Visual Studio /8/2015 INF145 1 Créé par Julien Galarneau Allaire, révisé par Eric Thé S.E.G.
1 Programmation en C++ Cycle de vie ● La vie d'un objet ● Destructeur ● Gestion de mémoire dynamique.
UE2 - M22 Licence acoustique
Tableaux en C Mardi 2/05.
Cross-Plateform Cours JavaScript
AMUE – SIFAC Gestion des services fait sur SIFAC WEB
Réduction des communications dans l'outil STEP Alain Muller
JAVA.
Support et Maintenance SIAN : MDEL partenaires
6GEN720 Réseaux d’ordinateurs
LES TABLEAUX EN JAVA.
Session 1 6 mars 2017 Plateforme ICONICS Justine Guégan
Ecriture collaborative d’une dissertation en classe
Détection des erreurs.
CCNP Routage Chapitre 4 - Questionnaire N°1
Algorithmique demander jeu du pendu.
Ajouter le code dans une page html
Collecte de données CAPI
Initiation aux bases de données et à la programmation événementielle
Ce videoclip produit par l’Ecole Polytechnique Fédérale de Lausanne
L’Instruction de Test Alternatif
Suivi de réaction chimique par spectroscopie RMN
Commande show ip route ccnp_cch ccnp_cch.
Javadoc et débogueur Semaine 03 Version A16.
Fonctions.
Architecture de machines Le microprocesseur
Principes de programmation (suite)
Master Réseaux et Systèmes Distribués (RSD) Algorithmique des systèmes
Master Réseaux et Systèmes Distribués (RSD) Algorithmique des systèmes
Algorithmique & Langage C
Programmation Orientée Objet I. Introduction
Cyber-Sphinx Séance 2.
Programmation Impérative II
Semaine #4 INF130 par Frédérick Henri.
Programmation en C++ Classes
Algorithmique & Langage C IUT GEII S1 Notes de cours (deuxième partie)
Documentation technique (Linux)
Notion De Gestion De Bases De Données
PROGRAMMATION INFORMATIQUE D’INGÉNIERIE II
Programmation en C++ C++ de base
Cours N°10: Algorithmiques Tableaux - Matrices
PROGRAMMATION ET ENSEIGNEMENT
Package R Markdown: Un outil pour générer des pages html avec R Studio
Guide Utilisateur. Guide Utilisateur.
Exercice PHP DEUST TMIC
Formation sur les bases de données relationnelles.
Chapitre 3 : Caractéristiques de tendance centrale
Assembleur, Compilateur et Éditeur de Liens
Efficacité des algorithmes
B.Shishedjiev - Informatique
Architecture matérielle des ordinateurs
7 Contraintes d’intégrité en SQL
Langages de programmation TP11
Filière Génie Civil – 2018 Langage C Tableaux – Exercices de révision
Programmation Scratch
Opérateurs et fonctions arithmétiques Opérateurs de relation Opérateurs logiques Cours 02.
Tris Simples/Rapides.
PROGRAMMATION ET ENSEIGNEMENT
Chapter 11: Récursivité Java Software Solutions Second Edition
Délégation académique pour le numérique éducatif
Python Nicolas THIBAULT
Type Tableau Partie 1 : Vecteurs
Transcription de la présentation:

Programmation parallèle MPI & OpenMP Présentée par UCI ADMIN

programmation parallèle l'exécution d'un programme en plusieurs processus L’intérêt de faire de la programmation parallèle Réduire le temps de restitution ; Effectuer de plus gros calculs ; Exploiter le parallélisme des processeurs modernes (multi-cœurs, multithreading). Methode

MPI vs OpenMP OpenMP utilise un schéma à mémoire partagée MPI la mémoire est distribuée.

Modèle de programmation par échange de messages

Modèle de programmation par échange de messages le programme est écrit dans un langage classique (Fortran, C, C++, ...) ; Toutes les variables du programme sont privées et résident dans la mémoire locale allouée à chaque processus ; Une donnée est échangée entre deux ou plusieurs processus via un appel dans le programme

Concepts de l’échange de messages Un message est constitué de paquets de données transitant du processus émetteur au(x) processus récepteur(s) En plus des données à transmettre, un message doit contenir aussi: l’identificateur du processus émetteur ; le type de la donnée ; La longueur ; l’identificateur du processus récepteur.

Concepts de l’échange de messages

Environnement MPI Openmpi Supporte tous type de réseau Mpich2 Ne supporte pas l’InfiniBand Mvapitch MPI over InfiniBand dérivé de mpich supporte l’InfiniBand

l’environnement MPI Inclure le fichier mpi.h MPI_INIT() permet d’initialiser l’environnement MPI MPI_FINALIZE() désactive cet environnement En C/C++: int MPI_Init(int *argc, char ***argv); int MPI_Finalize(void);

Communicateurs Les opérations effectuées par MPI portent sur des communicateurs MPI_COMM_WORLD Le communicateur par défaut Il comprend tous les processus actifs.

Rang et nombre de processus MPI_Comm_size() connaitre le nombre de processus MPI_Comm_size(MPI_COMM_WORLD , alltasks , code); MPI_Comm_rank retourne le rang d’un processus MPI_Comm_rank(MPI_COMM_WORLD , rang , code) 0<=rang<MPI_COMM_WORLD

Rang et nombre de processus Exemple: Ecrire un script ou chaque processus affiche Je suis le processus rang parmi alltasks Changer le script pour que chaque processus affiche aussi le nom de la machine dont il s’exécute.

Rang et nombre de processus Solution: #include <stdio.h> #include <mpi.h> int main (int argc, char* argv[]) { int nbProc, myRank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &nbProc); MPI_Comm_rank(MPI_COMM_WORLD, &myRank); printf("Je suis le processus %d parmi %d \n", myRank , nbProc); MPI_Finalize(); }

Rang et nombre de processus Solution avec nom de la machine : char processor_name[20]; int name_len; MPI_Get_processor_name(processor_name, &name_len); printf("Je suis le processus %d parmi %d machine %s\n", myRank , nbProc, processor_name);

Rang et nombre de processus Exécution : mpicc -o result hello.c mpirun –np 4 result Je suis le processus 0 parmi 4 machine haytham0 Je suis le processus 1 parmi 4 machine haytham0 Je suis le processus 2 parmi 4 machine haytham0 Je suis le processus 3 parmi 4 machine haytham0

Communications point à point

Communications point à point Passe entre deux processus émetteur et récepteur Routines basiques de communication Opération d’envoi MPI_Send() MPI_Send(message,longueur,type,rang_dest,etiquette,comm ,code) Envoi, à partir de l’adresse message, d’un message de taille longueur, de type type, étiqueté etiquette, au processus rang_dest dans le communicateur comm Cette opération est bloquante : l’exécution reste bloquée jusqu’à ce que le contenu de message puisse être réécrit sans risque d’écraser la valeur qui devait être envoyée.

Communications point à point Opération de réception MPI_Recv() MPI_Recv(message,longueur,type,rang_source,etiquette,comm,statut,code) Réception, à partir de l’adresse message, d’un message de taille longueur, de type type, étiqueté etiquette, du processus rang_source. Cette opération est bloquante : l’exécution reste bloquée jusqu’à ce que le contenu de message corresponde au message reçu

Communications point à point Types de données de base C MPI_CHAR signed char MPI_SHORT signed short MPI_INT signed int MPI_LONG signed long int MPI_UNSIGNED_CHAR unsigned char MPI_UNSIGNED_SHORT unsigned short MPI_UNSIGNED unsigned int MPI_UNSIGNED_LONG unsigned long int MPI_FLOAT float MPI_DOUBLE double MPI_LONG_DOUBLE long double

Communications point à point One sided point to point Exercice : Copier le script précédent dans un fichier nommé one_sided.c Modifier le de sorte que le processus master envoie au processus worker un message d’introduction contenant son rang Le worker reçoit le message et l’affiche Indication: Utiliser la fonction sprintf pour générer le buffer du message

Communications point à point Solution : #include <stdio.h> #include <mpi.h> int main (int argc, char* argv[]) { //déclaration de variables int nbProc, myRank; int tag,source,destination,count; char buffer[30], msg[20]="hello my Rank is"; char rbuf[30]; MPI_Status status; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &nbProc); MPI_Comm_rank(MPI_COMM_WORLD, &myRank); // initialisation tag=10; source=0; destination=1; count=30;

Communications point à point Suite : if (myRank == source ){ //code executé par processus master 0 sprintf(buffer,"%s %d",msg,myRank ); MPI_Send(&buffer,count,MPI_CHAR,destination,tag,MPI_COMM_WORLD); } if (myRank == destination){ //code executé par processus worker 1 MPI_Recv(&buffer,count,MPI_CHAR,source,tag,MPI_COMM_WORLD,&status); printf("processor %d got %s\n",myRank,buffer ); MPI_Finalize();

Rang et nombre de processus Exécution : mpicc -o result one_sided.c mpirun –np 4 result processor 1 got hello my Rank is 0

Communications point à point Two sided point to point Exercice : Modifier le script one_sided.c à un autre nommé two_sided.c , ou le worker affiche le message du master et répond par son message d’introduction

Communications point à point Solution: #include <stdio.h> #include <mpi.h> int main (int argc, char* argv[]) { int nbProc, myRank; int tag,source,destination,count; char sbuf[40], rbuf[40], smsg[20]="hello my Rank is", rmsg[30]="Well Received From Processor"; MPI_Status status; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &myRank); tag=10; source=0; destination=1; count=40;

Communications point à point Suite: if (myRank == source ){ //préparer le buffer d’envoie sprintf(sbuf,"%s %d",smsg,myRank ); //envoyer le message au worker 1 MPI_Send(&sbuf,count,MPI_CHAR,destination,tag,MPI_COMM_WORLD); //recevoir la réponse depuis worker 1 MPI_Recv(&rbuf,count,MPI_CHAR,destination,tag,MPI_COMM_WORLD,&status); printf(" processor %d got %s\n ", myRank,rbuf ); }

Communications point à point Suite: if (myRank == destination){ //recevoir le message du master MPI_Recv(&sbuf,count,MPI_CHAR,source,tag,MPI_COMM_WORLD,&status); printf("processor %d got %s\n",myRank,sbuf); //preparer le buffer de la réponse sprintf(rbuf,"%s %d",rmsg,myRank); //envoyer la réponse MPI_Send(&rbuf,count,MPI_CHAR,source,tag,MPI_COMM_WORLD); } MPI_Finalize();

Rang et nombre de processus Exécution : mpicc -o result hello.c mpirun –np 4 result processor 0 got Well Received From Processor 1 processor 1 got hello my Rank is 0

Communications point à point One to all point to point Exercice : Modifier le script one_sided.c à un autre nommé one_to_all.c pour que tous les workers reçoivent et affichent le message du master

Communications point à point Solution : int main (int argc, char *argv[]) { int alltasks, taskid, i, length=20 ; char sbuf[20], rbuf[20] ; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &taskid); MPI_Comm_size(MPI_COMM_WORLD, &alltasks); if (taskid == 0){ sprintf(sbuf, "Hello my Rank is %d", taskid ); for (i=1 ; i< alltasks ; i++) { //le master envoie un message à tous les workers MPI_Send(&sbuf, length, MPI_CHAR, i, 0, MPI_COMM_WORLD) ; }}

Communications point à point Suite : else { MPI_Recv(&rbuf, length, MPI_CHAR, 0, 0, MPI_COMM_WORLD,MPI_STATUS_IGNORE); printf("processor %d got %s\n",taskid, buf); } MPI_Finalize(); Exécution : mpirun -np 4 result processor 2 got Hello my Rank is 0 processor 3 got Hello my Rank is 0 processor 1 got Hello my Rank is 0

Communications point à point All to one point to point Exercice : Modifier le script one_sided.c à un autre nommé all_to_one.c pour que tous les workers envoient un message d’introduction au master

Communications point à point Solution : int main (int argc, char *argv[]) { int nbProc, myRank, tag, source, destination, i, length=30 ; char sbuf[30], rbuf[30] ; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &nbProc); MPI_Comm_rank(MPI_COMM_WORLD, &myRank); //envoie du message par les workers au master if (myRank != 0) { sprintf(sbuf,"Hello From Processor%d",myRank); MPI_Send(&sbuf, length, MPI_CHAR, 0, 0, MPI_COMM_WORLD); }

Communications point à point Suite: else { //reception de tous les messages workers par le master for (i=0 ; i < nbProc ; i++){ MPI_Recv(&rbuf, length,MPI_CHAR, i, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); printf("Processor %d got %s\n" , myRank , rbuf); }} MPI_Finalize(); } Exécution : mpirun -np 4 res Processor 0 got Hello From Processor 1 Processor 0 got Hello From Processor 2 Processor 0 got Hello From Processor 3

Communications point à point Communication et traitement Exercice : Créer un programme MPI ou plusieurs workers envoient un nombre entier (son rang) au master, le master somme les entiers reçus et affiche le résultat. Changer le programme pour que le master envoie la somme calculée à chaque worker.

Communications point à point Solution : int main (int argc, char *argv[]) { int nbProc, myRank, i, sum_recv, sum = 0; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &nbProc); MPI_Comm_rank(MPI_COMM_WORLD, &myRank); if (myRank != 0) { // workers envoient leur rang au master MPI_Send(&myRank,1,MPI_INT,0,0,MPI_COMM_WORLD) ; // workers reçoivent la somme calculée par le master MPI_Recv(&sum_recv,1,MPI_INT,0,1,MPI_COMM_WORLD,MPI_STATUS_IGNORE); printf(“Iam worker %d I receive %d\n", myRank, sum_recv); }

Communications point à point Suite : else { for ( i=1 ; i<nbProc ; i++) { MPI_Recv (&rank,1,MPI_INT, i, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); sum = sum+rank; } printf("My Rank is %d Sum Calculated is %d\n" , myRank , sum); for ( i=1 ; i<nbProc ; i++){ MPI_Send (&sum,1,MPI_INT, i,1, MPI_COMM_WORLD); }} MPI_Finalize();

Communications point à point Exécution : mpirun -np 5 result MyRank is 1 I receive 10 MyRank is 2 I receive 10 MyRank is 3 I receive 10 MyRank is 4 I receive 10 My Rank is 0 Sum Calculated is 10

Communications point à point Empaquetage des données Les fonctions MPI_Pack et MPI_Unpack permettent le transfert de plusieurs variables de types différents lors d'une seule communication MPI. int MPI_Pack ( void *inbuf, int incount, MPI_Datatype datatype, void *outbuf, int outcount, int *position, MPI_Comm comm ) int MPI_Unpack ( void *inbuf, int insize, int *position, void *outbuf, int outcount, MPI_Datatype datatype, MPI_Comm comm )

Communications point à point Exercice : Utilisez le type MPI_PACKED et les primitives MPI_Pack  et MPI_Unpack pour créer un programme ou chaque worker envoie au master son rang ainsi qu’ un autre nombre réel. Le master affiche toutes les données reçues.

Communications point à point Solution : int main (int argc, char *argv[]) { int nbProc, myRank, rank, i, pos ; float a ; char buf[10] ; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &nbProc); MPI_Comm_rank(MPI_COMM_WORLD, &myRank); if (myRank != 0) { a=(float) rand()/(float) RAND_MAX ; // générer un réel avec rand() pos=0 ; // empaqueter le rang et le réel généré MPI_Pack(&myRank,1, MPI_INT,buf,10,&pos,MPI_COMM_WORLD); MPI_Pack(&a,1,MPI_FLOAT,buf,10,&pos,MPI_COMM_WORLD); // envoyer le pack au master MPI_Send(&buf,10,MPI_PACKED,0,0,MPI_COMM_WORLD); }

Communications point à point Suite : else { printf("My Rank is %d I Receive \n ",myRank ); for (i=1 ; i < nbProc ; i++){ // recevoir les packs des workers MPI_Recv (&buf,10, MPI_PACKED, i ,0 , MPI_COMM_WORLD, MPI_STATUS_IGNORE); pos=0; // dépaqueter les nombres MPI_Unpack(&buf,10,&pos,&rank,1,MPI_INT,MPI_COMM_WORLD); MPI_Unpack(&buf,10,&pos,&a,1,MPI_FLOAT,MPI_COMM_WORLD); printf(" %d and %f\n ", rank , a ); }} MPI_Finalize(); }

Communications collectives Permettent de faire en une seule opération une série de communications point à point. Diffusion générale : MPI_Bcast() MPI_BCAST(message, length, type, rang_source, comm) Envoi d’un message à partir de l’adresse message de longueur length de type type, par le processus rang_source, à tous les autres processus du communicateur comm Réception de ce message à l’adresse message pour les processus autre que rang_source.

Communications collectives Exercice : Utiliser la routine MPI_Bcast() pour créer un programme ou le master diffuse un message (Hello from master) à tous les workers du programme, chaque worker y compri le master affiche le message reçu

Communications collectives Solution : int main (int argc, char *argv[]) { int taskid,nbrtask; char buffer[20] ; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &taskid); MPI_Comm_size(MPI_COMM_WORLD, &nbrtask); if (taskid == 0) { // préparer le message à envoyer strcpy(buffer, "Hello from master"); }

Communications collectives Suite : // le master effectue la diffusion du message contenu à l’adresse buffer MPI_Bcast(&buffer,20,MPI_CHAR,0,MPI_COMM_WORLD); // afficher le contenu du buffer après broadcast printf(" processor %d got %s\n",taskid,buffer); MPI_Finalize(); }

Communications collectives Exécution : mpirun -np 4 res processor 3 got Hello from master processor 0 got Hello from master processor 1 got Hello from master processor 2 got Hello from master

Communications collectives Diffusion sélective : MPI_SCATTER()

Communications collectives MPI_SCATTER(message_a_repartir, longueur_message_emis, type_message_emis, message_recu, longueur_message_recu, type_message_recu, rang_source, comm) Distribution, par le processus rang_source, à partir de l’adresse message_a_repartir, d’un message de taille longueur_message_emis, de type type_message_emis, à tous les processus du communicateur comm. Réception du message à l’adresse message_recu, de longueur longueur_message_recu et de type type_message_recu par tous les processus du communicateur comm.

Communications collectives Exercice : Considérons un vecteur à N entier. Le but est de diviser le vecteur à plusieurs partitions autant qu’il y a de processus (m) dans la machine d’exécution parallèle. Chaque processus y compri le master, reçoit une partition du vecteur. Si N n’est pas multiple de m alors le vecteur doit être complété par des éléments artificiels complémentaires. 1- Créer un programme MPI (version1) pour effectuer un tel partitionnement en utilisant MPI_Send et MPI_Recv. 2- Créer un autre programme MPI (version2) pour effectuer un tel partitionnement en utilisant MPI_Scater 3- Utilisez la fonction MPI_Wtime pour mesurer et comparer le temps d’ exécution des deux versions.

Communications collectives Solution avec MPI_Send et MPI_Recv int main (int argc, char *argv[]) { int v[n], n=20; int myRank, step, start, mod, m, i, a; double endtime, starttime; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &m); MPI_Comm_rank(MPI_COMM_WORLD, &myRank); //calculer le nombre d’élément par partition et compléter le vecteur si n n’est pas multiple de m mod = n%m ; if ( mod != 0 ){ a = m-mod; n = n+a; step=n/m; }

Communications collectives Suite : else { step=n/m ; } //le master envoie une partie du vecteur aux processus y compris lui if (myRank == 0) { //Remplir le vecteur v par des valeurs starttime= MPI_Wtime(); // Temps initial for (i=0; i<n; i++) { v[i]=i ; } start = 0 ; for (i=0; i<m; i++) { // envoyer à chaque processus l’adresse début de sa partition MPI_Send(&start,1,MPI_INT, i, 0, MPI_COMM_WORLD); // envoyer à chaque processus sa partition du vecteur v MPI_Send(&v[start], step, MPI_INT, i, 1, MPI_COMM_WORLD); start = start+step; }

Communications collectives Suite : // master reçoit sa partition et l’affiche MPI_Recv(&start,1,MPI_INT,0,0,MPI_COMM_WORLD,MPI_STATUS_IGNORE); MPI_Recv(&v, step, MPI_INT, 0, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); endtime = MPI_Wtime(); // Temps final printf ("Iam %d I Start From I Receive [ %d\n ", myRank, start); for (i=0; i<step; i++) { printf ("%d\n ",v[i]); } printf ("] "); printf("That took %f seconds\n", endtime-starttime); }

Communications collectives Suite : else { // les processus reçoivent leurs partitions du vecteur v et les affichent starttime= MPI_Wtime(); // Temps initial MPI_Recv(&start,1,MPI_INT,0,0,MPI_COMM_WORLD,MPI_STATUS_IGNORE); MPI_Recv(&v, step, MPI_INT, 0,1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); endtime = MPI_Wtime(); // Temps final printf ("Iam %d I Start From %d\n I Receive [ ", myRank, start); for (i=0; i<step; i++) { printf ("%d\n ",v[i]) ; } printf ("] "); MPI_Finalize(); }

Communications collectives Exécution : mpirun -np 4 res Iam Process 3 I Start From 15 I receive [15 , 16 , 17 , 18 , 19 , ] That took 0.000071 seconds Iam Process 0 I Start From 0 I receive [0 , 1 , 2 , 3 , 4 , ] That took 0.000072 seconds Iam Process 1 I Start From 5 I receive [5 , 6 , 7 , 8 , 9 , ] That took 0.000067 seconds Iam Process 2 I Start From 10 I receive [10 , 11 , 12 , 13 , 14 , ] That took 0.000072 seconds

Communications collectives Solution avec MPI_Scatter() : …………………………………..// début de programme inchangé if (myRank == 0) {//Remplir le vecteur v par des valeurs for (i=0; i<n; i++){ v[i]=i ; }} int vr[step]; // adresse du message reçu starttime = MPI_Wtime(); // temps initial // découper v selon le step et envoyer à tous les processus leur part MPI_Scatter(&v, step, MPI_INT, &vr, step, MPI_INT, 0, MPI_COMM_WORLD); endtime = MPI_Wtime(); printf ("I am Process %d\n I Receive [ ",myRank); for (i=0; i<step; i++) { // chaque processus affiche le sous vecteur reçu printf ("%d\n ",vr[i]); } printf ("] "); printf("That took %f seconds\n", endtime-starttime); MPI_Finalize(); }

Communications collectives Exécution : mpirun -np 4 res I am Process 0 I Receive [ 0 ,1 ,2 ,3 ,4 ,]That took 0.000019 seconds I am Process 1 I Receive [ 5 ,6 ,7 ,8 ,9 ,]That took 0.000022 seconds I am Process 2 I Receive [ 10 ,11 ,12 ,13 ,14 ,]That took 0.000025 seconds I am Process 3 I Receive [ 15 ,16 ,17 ,18 ,19 ,]That took 0.000026 seconds

Communications collectives Réduction Une opération appliquée à un ensemble d’éléments pour en obtenir une seule valeur exemple : la somme des valeurs envoyées par les processus, recherche de max ou min des éléments envoyés …

Communications collectives Exercice : Etendre les deux versions précédentes pour que chaque processus calcule la somme partielle de son sous vecteur et envoyer son résultat au processus maitre qui à son tour somme les sommes partielles et affiche le résultat. Pour la version 2 utilisez la fonction MPI_Reduce Comparer le temps d’exécution des deux versions

Communications collectives Solution version1: if (myRank == 0) { ……………………………….. // voir programme version1 // master reçoit son sous vecteur MPI_Recv(&v, step, MPI_INT, 0,1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); printf("Iam Process %d\n", myRank); sumt=0 ; for (i=0 ; i<step ; i++) {// master calcule sa somme partielle et l’affiche sumt=sumt+v[i] ; printf ("%d , ",v[i]); } printf("] my Partial Sum is %d\n", sumt) ;

Communications collectives Suite version1: // le master reçoit les sommes partielles et calcule la somme totale for(i=1; i<m; i++){ MPI_Recv ( &sump,1, MPI_INT, i, 2, MPI_COMM_WORLD, MPI_STATUS_IGNORE); sumt= sumt+sump; endtime = MPI_Wtime(); //Temps final printf(" Partial Sum received from %d is %d\n" , i, sump); else { // workers reçoivent leurs sous vecteurs MPI_Recv ( &v, step, MPI_INT, 0, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);

Communications collectives Suite version1: // workers affichent le sous vecteur reçu et calculent la somme partielle sump=0; printf("Iam %d \n", myRank); for (i=0 ; i<step ; i++) { sump=sump+v[i] ; printf ("%d , ",v[i]) ; } printf ("]\n "); // workers envoient la somme partielle calculée MPI_Send (&sump,1, MPI_INT, 0, 2, MPI_COMM_WORLD); } MPI_Finalize(); }

Communications collectives Exécution : mpirun -np 6 res Iam Process 0 I Receive [ 0 ,1 ,2 ,3 ,] My Parial Sum is 6 Partial Sum received from 1 is 22 Partial Sum received from 2 is 38 Partial Sum received from 3 is 54 Partial Sum received from 4 is 70 Partial Sum received from 5 is 86 Total Sum is 276 That took 0.000101 seconds Iam Process 1 I Receive [ 4 , 5 , 6 , 7 , ] Iam Process 2 I Receive [ 8 , 9 , 10 , 11 , ] Iam Process 3 I Receive [ 12 , 13 , 14 , 15 , ] Iam Process 4 I Receive [ 16 , 17 , 18 , 19 , ] Iam Process 5 I Receive [ 20 , 21 , 22 , 23 , ]

Communications collectives Solution version2 : ………………………….. // voir programme version2 printf ("I am Process %d\n ",myRank); Psum=0; for (i=0 ; i<step ; i++) { //chaque processus affiche le sous vecteur reçu et calcule la somme partielle printf ("I Receive %d\n ", vr[i] ); psum=psum+vr[i]; } printf ("partial sum= %d\n ",psum); //MPI_Reduce pour calculer la somme globale et l’afficher par le master MPI_Reduce ( &psum, &gsum,1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); endtime = MPI_Wtime(); // Temps final if (myRank==0){ printf ("I am %d I Receive the Globale Sum of %d\n ", myRank, gsum); } MPI_Finalize() ; }

Communications collectives Exécution : mpirun -np 6 res I am Process 0 I Receive [ 0 , 1 , 2 , 3 , ] Partial sum= 6 Globale Sum is 276 That took 0.000051 seconds I am Process 1 I Receive [ 4 , 5 , 6 , 7 , ] Partial sum= 22 I am Process 2 I Receive [ 8 , 9 , 10 , 11 , ] Partial sum= 38 I am Process 3 I Receive [ 12 , 13 , 14 , 15 , ] Partial sum= 54 I am Process 4 I Receive [ 16 , 17 , 18 , 19 , ] Partial sum= 70 I am Process 5 I Receive [ 20 , 21 , 22 , 23 , ] Partial sum= 86

Communications collectives MPI_Gather :

Communications collectives MPI_Gather ( message_emis, longueur_message_emis, type_message_emis, message_recu, longueur_message_recu, type_message_recu, rang_dest, comm) Envoi de chacun des processus du communicateur comm, d’un message message_emis, de taille longueur_message_emis et de type type_message_emis Collecte de chacun de ces messages, par le processus rang_dest, à partir l’adresse message_recu, sur une longueur longueur_message_recu et avec le type type_message_recu

Communications collectives Exercice : Etendre la version 2 (sans réduction somme) pour que chaque processus modifie son sous vecteur reçu en ajoutant un nombre fixe à tous les éléments du sous vecteur ensuite le master rassemble les sous vecteurs modifiés. Utilisez les fonctions MPI_Scatter et MPI_Gather.

Communications collectives Solution : starttime= MPI_Wtime(); //temps initial MPI_Scatter ( &v, step,MPI_INT, &vr, step, MPI_INT, 0, MPI_COMM_WORLD); //endtime = MPI_Wtime(); for ( i=0; i<step; i++ ) { //chaque processus reçoit son sous vecteur et ajoute le nombre m à tous ses éléments vr[i]=vr[i]+m; } //master rassemble tous les sous vecteurs modifiés par chaque processus MPI_Gather ( &vr, step, MPI_INT, &back, step, MPI_INT, 0, MPI_COMM_WORLD); endtime = MPI_Wtime(); // Temps final if (myRank == 0){ // master affiche les sous vecteurs rassemblés printf("The Global Vector Received from All Process is\n");

Communications collectives for (i=0; i<(m*step) ; i++) { printf (" %d ", back[i] ) ; } printf("\nThat took %f seconds\n",endtime-starttime); } MPI_Finalize() ; } Exécution : mpirun –np 6 res The Global Vector Received from All Process is 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

OpenMP Un ensemble de procédures et de directives de compilation identifiées par un mot clef initial !$OMP visant à réduire le temps de restitution lors de l’exécution d’un programme sans en changer la sémantique OpenMP est un modèle de programmation parallèle basé sur les architectures à mémoire partagée. Les taches de calcul peuvent accéder à un espace mémoire commun, ce qui limite la redondance des données et simplifie les échanges d’information entre les taches. En pratique, la parallélisation repose sur l’utilisation de processus système légers (ou threads), on parle alors de programme multithreadé

Environnement Compilateurs supportant OpenMP (libgomp) A partir de GCC 4.7.0, OpenMP 3.1 est complètement supporté. Dans GCC 4.9.0, OpenMP 4.0 est supporté pour C et C++, mais pas le Fortran. A partir GCC 4.9.1, OpenMP 4.0 est complètement supporté. Pour plus d’informations sur les compilateurs supportés voir lien: http://openmp.org/wp/openmp-compilers/

Concepts générales

Concepts générales Thread : Entité d’exécution avec une mémoire locale (stack) Les taches d’un même programme partagent l’espace mémoire de la tache initiale (mémoire partagée) mais disposent aussi d’un espace mémoire local : la pile (stack). Il est possible de définir des variables partagées (stockées dans la mémoire partagée) ou des variables privées (stockées dans la pile de chacune des taches).

Fonctionnalités OpenMP proposent des mécanismes pour : Partager le travail entre les taches. Il est par exemple possible de répartir les itérations d’une boucle entre les taches. Partager ou privatiser les variables. Synchroniser les threads.

Construction d’une région parallèle Un programme OpenMP est une alternance de régions séquentielles et parallèles (modèle ≪ fork and join ≫) A l’entrée d’une région parallèle, le thread maitre (celui de rang 0) active (fork) des processus fils Ces processus fils exécutent leur tache implicite puis disparaissent ou s’assoupissent en fin de région parallèle (join). En fin de région parallèle, l’exécution redevient séquentielle avec uniquement l’exécution du thread maitre.

Syntaxe générale d’une directive Pour utiliser les fonctions OpenMP il faut inclure Le module OMP _LIB pour Fortran Le fichier omp.h pour C/C++ Une directive OpenMP possède la forme suivante : Pour C et C++ : #include <omp.h> ... #pragma omp parallel private(a,b) firstprivate(c,d,e) { ... }

Syntaxe générale d’une directive Pour Fortran: !$ use OMP_LIB ... ! $OMP PARALLEL PRIVATE(a,b) & ! $OMP FIRSTPRIVATE(c,d,e) ! $OMP END PARALLEL

Compilation et exécution les options de compilation permettent d’activer l’interprétation des directives OpenMP par certains compilateurs : Compilateur GNU : -fopenmp gfortran -o omp_hellof -fopenmp omp_hello.f gcc -o omp_helloc -fopenmp omp_hello.c Compilateur Intel : -openmp icc -o omp_helloc -openmp omp_hello.c ifort -o omp_hellof -openmp omp_hello.f Exécution : export OMP_NUM_THREADS =4 # Nombre de threads ./ omp_helloc

OpenMP Exemple : Ouvrir un fichier nommé hello_word.c et copier le code suivant de dans #include <omp.h> #include <stdio.h> #include <stdlib.h> Int main (int argc, char *argv[]) { #pragma omp parallel { printf( "Hello OpenMP" ); } } Compiler le code avec la commande : gcc –O3 –o hello_word -fopenmp hello_word.c

OpenMP Suite : Définir le nombre des threads pour l’exécution en parallèle export OMP_NUM_THREADS=4 Exécuter ./hello_word Changer le nombre des threads, ré exécuter et vérifier le output Recompiler votre script sans l’option –fopenmp gcc –O3 –o hello_word hello_word.c Varie le nombre des threads et exécuter en vérifiant que la directive openmp n’est plus utilisée

OpenMP Exécution : ./hello_word Hello OpenMP

Variables privées et partagées #pragma omp parallel private(nthreads, tid) Par défaut, les variables sont partagées. La clause PRIVATE permet de changer le statut d’une variable. Si une variable possède un statut privé, elle est allouée dans la pile de chaque tache. Les variables privées ne sont pas initialisées à l’entrée d’une région parallèle mais grâce à la clause FIRSTPRIVATE, il est possible de forcer l’initialisation d’une variable privée à la dernière valeur qu’elle avait avant l’entrée dans la région parallèle.

Informations des threads La fonction omp_get_thread_num() permet de retourner le id du thread La fonction omp_get_num_threads() retourne le nombre des threads respectivement

Variables privées et partagées Exemple : int main (int argc, char *argv[]) { int nthreads, tid, a=1000 ; // Fork un ensemble de threads en donnant à chacun sa copie de variables #pragma omp parallel private(nthreads, tid, a) { tid = omp_get_thread_num(); nthreads = omp_get_num_threads(); a=a+20; printf("Hello my thread id is = %d out of %d a=%d\n", tid,nthreads,a); } printf("After parallel region a = %d\n",a );

Variables privées et partagées Exécution : ./thread_info Hello my thread id is = 0 out of 4 a=20 Hello my thread id is = 2 out of 4 a=20 Hello my thread id is = 3 out of 4 a=20 Hello my thread id is = 1 out of 4 a=20 After parallel region a = 1000 Forcer l’initialisation de la valeur “a” et ré exécuter le programme Hello my thread id is = 3 out of 4 a=1020 Hello my thread id is = 0 out of 4 a=1020 Hello my thread id is = 2 out of 4 a=1020 Hello my thread id is = 1 out of 4 a=1020

Boucle parallèle un parallélisme par répartition des itérations d’une boucle. La boucle parallélisée est celle qui suit immédiatement la directive DO. Le mode de répartition des itérations peut être spécifié dans la clause SCHEDULE. Les boucles infinies et do while ne sont pas parallélisables avec cette directive Les indices de boucles sont par défaut des variables entières privées, pas besoin de spécifier le statut. Il est possible d’introduire autant de constructions DO (les unes après les autres) qu’il est souhaité dans une région parallèle.

Boucle parallèle Exercice : copier le code suivant dans un fichier nommé loop.c int main (int argc, char *argv[]) { int tid, num_loops, i , n=1000 ; #pragma omp parallel private(tid) { tid = omp_get_thread_num() ; num_loops=0 ; #pragma omp for for (i=1; i<n ; i++) num_loops = num_loops+1; printf ("Hello my thread id is = %d number of loops %d\n", tid,num_loops) ; } printf("total loops %d",num_loops); }

Boucle parallèle Suite : Compiler et exécuter le programme avec 4 threads. Est-ce que le nombre de boucles est cohérent ? Changer le statu de la variable num_loops à private et ré exécuter . La valeur num_loops n’a pas de sens hors la région parallèle , pour avoir la somme du nombre de boucles utilisez la directive reduction qui permet de sommer les valeurs de la variable privée num_loops en fin de la région parallèle Supprimer num_loops de de la directive private et ajouter reduction( +:num_loops) après la directive private

Hello my thread id is = 1 number of loops 250 Boucle parallèle Exécution1 : ./loop Hello my thread id is = 1 number of loops 250 Hello my thread id is = 0 number of loops 250 Hello my thread id is = 2 number of loops 250 Hello my thread id is = 3 number of loops 250 total loops 250 Exécution2 : Hello my thread id is = 3 number of loops 249 total loops 0

Boucle parallèle Exécution 3 : Hello my thread id is = 1 number of loops 250 Hello my thread id is = 2 number of loops 250 Hello my thread id is = 0 number of loops 250 Hello my thread id is = 3 number of loops 249 total loops 999

Scheduling Mode de distribution des itérations d’une boucle parallèle Le choix du mode de répartition permet de mieux contrôler l’équilibrage de la charge de travail entre les threads. STATIC consiste à diviser les itérations en paquets d’une taille donnée et attribuer de façon cyclique à chacun des threads, un ensemble de paquets suivant l’ordre des threads DYNAMIC les itérations sont divisées en paquets de taille donnée. Sitot qu’un thread épuise les itérations de son paquet, un autre paquet lui est attribué GUIDED similaire au dynamic mais la taille des paquets décroit ,tous les paquets ont une taille supérieure ou égale à une valeur donnée à l’exception du dernier dont la taille peut être inférieure(par défaut la valeur vaut nbr_loop/nbr_threa).

Scheduling Exercice : Utiliser le programme précédent en changeant le Scheduling #pragma omp do schedule(run time) Compiler et exécuter le programme en variant le nombre de threads et le Sheduling Export OMP_SCHEDULE=static ,dynamic ou guided Exécution : Static Hello my thread id is = 0 number of loops 250 Hello my thread id is = 2 number of loops 250 Hello my thread id is = 1 number of loops 250 Hello my thread id is = 3 number of loops 249 total loops 999

Scheduling Dynamic Hello my thread id is = 2 number of loops 320 Hello my thread id is = 0 number of loops 253 Hello my thread id is = 1 number of loops 217 Hello my thread id is = 3 number of loops 209 total loops 999 Guided Hello my thread id is = 0 number of loops 429 Hello my thread id is = 2 number of loops 369 Hello my thread id is = 3 number of loops 73 Hello my thread id is = 1 number of loops 128

Réduction Opération associative appliquée à une variable partagée. L’opération peut être : ➳ arithmétique : +, –, ×; ➳ logique : AND , OR , EQV , NEQV ➳ une fonction intrinsèque : MAX, MIN Chaque thread calcule un résultat partiel indépendamment des autres. Ils se synchronisent ensuite pour mettre à jour le résultat final.

π/4 = arctn(1) = ∑ (-1)n / (2n+1) Réduction Exercice : Créer un programme séquentiel pi_serial.c qui calcule la valeur de π avec la formule suivante( augmenter le nombre d’itération nstep pour avoir plus de précision) π/4 = arctn(1) = ∑ (-1)n / (2n+1) Copier le programme séquentiel dans un fichier nommé pi_omp.c et paralléliser avec une reduction de la boucle principale Utilisez la fonction time du c pour calculer le temps écoulé pour calculer π (utiliser la fonction time hors la région parallèle) n=nsteps n=0

Réduction Suite : Augmenter le nombre d’itérations jusqu’à ce que l’exécution du programme séquentiel atteint 60 secondes et comparer le temps en remontant le nombre des threads.

#include <time.h> // fonction qui calcule (-1)n Réduction Solution : Serial #include <time.h> // fonction qui calcule (-1)n int powr (int n) { int i,pui=1; if (n==0) { pui=1; } for (i=1; i<=n; i++) pui = pui * -1; return pui; }

int main (int argc, char *argv[]) { int j, n=100000 ; double sum, pi; Réduction Suite : int main (int argc, char *argv[]) { int j, n=100000 ; double sum, pi; clock_t a, b ; float c ; sum=0; a=time(NULL); // temps de début for(j=0;j<=n;j++){ // calculer la somme pi sum=sum+((double)powr(j)/(double)(2*j+1)); } b=time(NULL); // fin d’exécution c=(float)(b-a); // temps écoulé

printf(" pi=%lf\n" ,pi); } Exécution : Réduction Suite : printf(" c=%f\n" ,c); pi=sum*4; printf(" pi=%lf\n" ,pi); } Exécution : pi value is 3.141603 Elapsed time is 10.000000 sec

Réduction Parallel ……… // inchangé #pragma omp parallel for reduction(+:sum) //#pragma omp for for(j=0 ; j<=n ; j++){ sum=sum+((double)powr(j)/(double)(2*j+1)); } Exécution : pi value =3.141603 Elapsed time is 3.000000

Performance parallèle Pour écrire une application parallèlle il faut vérifier l’éfficacité du parallélisme Pour cela il faut inclure les fonctions du temps en augmentant à chaque fois le nombre de processeurs et vérifier l’éfficacité Exercice : Ecrire un code séquentiel matmul_serial.c qui calcule le produit de deux matrices C=A*B Copier le code séquentiel dans fichier nommé matmul_omp.c et utilisez OpenMP pour paralléliser et vérifier que les résultats des deux codes sont similaires Exécuter avec 1 et 2 threads , comparer les deux

Performance parallèle Suite : Pour avoir plus de puissance , utiliser le cluster pour réserver un noeud à 8 processeurs et exécuter le code en variant la taille du matrice 2000, exécuter avec 1,2,4,6 et 8 processeurs Remplir le tableau suivant : Nombre de processeurs 1 2 4 6 8 Temps Exécution SP Efficacité

Performance parallèle Suite : Déssiner le graphe speedup Sp Le speedup est donné par la formule Sp=T1/ Tp P nbr processeurs T1 temps d’exécution séquentiel Tp temps d’exécution parallel avec p processeurs Un speedup linéaire ou idélae est obtenu quand Sp=p Déssiner le graphe Efficacité Ep=Sp/p = T1/p * Tp / 0<Ep<1

Performance parallèle Solution : Serial #define N 1000 int A[N][N], B[N][N], C[N][N]; // declarer matrices NxN int main () { /* DECLARING VARIABLES */ int i, j, m; // indices de matrix multiplication float t_1; // Execution time measures clock_t c_1, c_2; // remplir A et B avec la fonction rand() srand ( time(NULL) ); // initialise le générateur des nombres rundum for(i=0;i<N;i++) { for(j=0;j<N;j++) { A[i][j]= (rand()%10); B[i][j]= (rand()%10); } }

Performance parallèle Suite: Serial c_1=time(NULL); // temps début for(i=0;i<N;i++) { for(j=0;j<N;j++) { C[i][j]=0; // initialiser la matrice C à 0 for(m=0;m<N;m++) { C[i][j]=A[i][m]*B[m][j]+C[i][j]; } // calculer les C[i][j] } } c_2=time(NULL); // temps final t_1 = (float)(c_2-c_1); // time elapsed for job row-wise printf("Execution time: %f \n",t_1); return 0; }

Performance parallèle Exécution : Serial ./ matmul_serial Execution time : 7.000000

Performance parallèle Solution : Parallel ....................... //inchangé #pragma omp parallel printf("Number of threads: %i \n",omp_get_num_threads()); c_1=time(NULL); // time measure: start mm #pragma omp parallel for private(m,j) for(i=0;i<N;i++) { for(j=0;j<N;j++) { C[i][j]=0; // set initial value of resulting matrix C = 0 for(m=0;m<N;m++) { C[i][j]=A[i][m]*B[m][j]+C[i][j]; } } }

Performance parallèle Exécution : Parallel ./matmul_omp Number of threads: 2 Execution time: 3.000000 Nombre de processeurs 1 2 4 6 8 Temps Exécution 77 39 19 13 9 SP 1.97 4.05 5.92 8.55 Efficacité 0,985 1,0125 0,986 1,06875

Performance parallèle Exécution : Parallel

Merci pour votre attention