Vous êtes sur la page 1sur 12

Nom : __________________

Matricule : ______________

Programmation parallèle et distribuée (GIF-4104/7104)


Département de Génie électrique et de Génie Informatique
Hiver 2015

EXAMEN PARTIEL
(solutions)
Question Points Score
1 60 Instructions :
2 20 — 1 feuille aide-mémoire manuscrite est permise (recto-verso) ;
— répondre sur le questionnaire ;
3 10 — durée de l’examen : 150 minutes.
4 10 Pondération : Cet examen compte pour 35% de la note finale.

Total: 100

Question 1 (60 points sur 100)


Répondez aux questions ci-dessous en noircissant le meilleur choix de réponse
(2 points par bonne réponse).
(i) À propos de la puissance (en watts) dissipée par un circuit intégré,
celle-ci varie comme :

le carré de la tension d’alimentation ;
le carré de la capacité électrique ;
le carré de la fréquence d’horloge ;
le carré du nombre de transistors ;
aucune des réponses précédentes.
(ii) En programmation parallèle, à quoi sert un pipeline ?
à cacher la latence ;
à diminuer la latence ;
à réduire le débit ;

à augmenter le débit en cachant la latence ;
aucune de ces réponses.
(iii) Que dit la loi de Moore ?
la vitesse des processeurs double à tous les 18 mois ;
la vitesse des processeurs double à tous les deux ans ;

le nombre de transistors double à tous les deux ans ;

Page 1 de 12
GIF-4104/7104 Examen Partiel 11 mars 2015

toutes les réponses précédentes ;


aucune des réponses précédentes.
(iv) Un programme parallèle se distingue d’un programme concurrent surtout par :
l’exécution de plusieurs processus ;
l’exécution de plusieurs fils d’exécution ;
l’exécution de plusieurs processus ou fils d’exécution ;

l’exécution simultanée de plusieurs fils d’exécution ;
aucune de ces réponses.
(v) Le calcul d’une somme peut s’effectuer en temps logarithmique à condition de :
posséder plus d’un processeur ;

posséder un nombre linéaire de processeurs ;
faire usage de OpenMP ;
faire usage de MPI ;
aucune de ces réponses.
(vi) Qu’est-ce qui limite le nombre de transistors que l’on peut intégrer sur un même puce ?
la résolution du procédé de fabrication ;
la surface maximale ;
la densité maximale d’énergie ;

toutes les réponses précédentes ;
aucune des réponses précédentes.
(vii) Comment mesure-t-on le « speedup » ?
en faisant le ratio du temps parallèle sur le temps séquentiel ;
en faisant le produit du temps parallèle et du temps séquentiel ;
en faisant la différence entre le temps parallèle et le temps séquentiel ;
en divisant le temps parallèle obtenu avec p processeurs par celui obtenu avec
un seul processeur ;

aucune de ces réponses.
(viii) Qu’est que l’efficacité d’une application parallèle ?

le ratio du « speedup » sur le nombre de processeurs ;
le produit du « speedup » par le nombre de processeurs ;
le ratio du temps parallèle sur le nombre de processeurs ;
le produit du temps parallèle par le nombre de processeurs ;
aucune de ces réponses.
(ix) En désignant par α la portion du programme qui peut être parallélisée, que dit Amdahl ?
speedup ≤ 1/(α + 1) ;

Page 2 de 12
GIF-4104/7104 Examen Partiel 11 mars 2015

speedup ≥ 1/(α + 1) ;

speedup ≤ 1/(1 − α) ;
speedup ≥ 1/(1 − α) ;
aucune de ces réponses.
(x) Pour une application parallèle faisant appel à P processeurs, la loi de Gustafson suppose
que le temps d’exécution de chaque processeur peut se décomposer comme :
la somme d’un temps séquentiel fixe et d’un temps parallèle également fixe ;

la somme d’un temps séquentiel fixe et d’un temps parallèle qui dépend de
la taille du problème ;
la somme d’un temps séquentiel qui dépend de la taille du problème, et d’un
temps parallèle fixe ;
la somme d’un temps séquentiel et d’un temps parallèle, tout deux dépendant
de la taille du problème ;
aucune de ces réponses.
(xi) Qu’est-ce que la latence d’une opération ?

le délai entre son début et sa fin ;
son nombre de résultats ;
la fréquence de son utilisation ;
la validité de son résultat ;
aucune de ces réponses.
(xii) Qu’est-ce qui caractérise une architecture NUMA ?
plusieurs processeurs sur une même carte mère ;
plusieurs processeurs qui partagent une même mémoire ;

des temps d’accès à la mémoire qui diffèrent d’un processeur à l’autre ;
des temps d’accès qui dépendent du nombre de processeurs ;
aucune de ces réponses.
(xiii) Qu’est-ce que de la mémoire cache ?
de la mémoire rapide qui permet d’augmenter la latence des opérations de lec-
ture et d’écriture ;
de la mémoire disponible en grande quantité qui sert de réserve ;

de la mémoire rapide qui permet de cacher la lenteur relative des accès à
la mémoire principale ;
de la mémoire rapide que l’on ajoute sur la carte mère afin de rendre parallèle
les accès à la mémoire ;
aucune de ces réponses.
(xiv) Qu’est-ce qui distingue un processus d’un fil d’exécution ?

Page 3 de 12
GIF-4104/7104 Examen Partiel 11 mars 2015

un processus contient au moins un fil d’exécution ;
un fil d’exécution contient au moins un processus ;
contrairement aux fils d’exécution, tous les processus partagent les mêmes
plages d’adresse mémoire ;
contrairement aux processus, tous les fils d’exécution partagent les mêmes
plages d’adresse ;
aucune de ces réponses.
(xv) À quoi sert la pile d’un fil d’exécution ?
le fil d’exécution n’a pas de pile, c’est le processus qui en possède une ;
à stocker les variable atomiques ;
à stocker les variables partagées ;
à échanger des messages ;

aucune de ces réponses.
(xvi) Qu’est-ce qu’une situation d’interblocage ?
lorsque deux fils d’exécution effectuent un « join » ;
lorsqu’un fil d’exécution attend après son processus ;
lorsque deux fils d’exécution attendent après une même ressource ;

lorsque deux fils d’exécution possèdent chacun une ressource désirée par
l’autre fil ;
aucune de ces réponses.
(xvii) En programmation parallèle, quel est l’effet d’une « course critique » ?

un comportement non déterministe ;
une erreur lors d’une lecture ;
une erreur lors d’une écriture ;
une erreur lors d’une lecture suivie d’une écriture ;
aucune de ces réponses.
(xviii) Quelle est le rôle de la fonction pthread_join ?
fusionner deux fils d’exécution ;
joindre un fil d’exécution à un processus ;

suspendre un fil d’exécution en attendant la fin d’un autre fil ;
fusionner un fil d’exécution à un processus ;
aucune de ces réponses.
(xix) Qu’est-ce qu’un pthread_t ?
un type de donnée générique dans pthread ;

le type de la variable associée à un fil pthread ;

Page 4 de 12
GIF-4104/7104 Examen Partiel 11 mars 2015

une variable qui contient le temps d’exécution d’un fil pthread ;


le nom de la fonction que le fil pthread doit exécuter ;
aucune de ces réponses.
(xx) Lequel des éléments suivants n’est pas un argument de la fonction pthread_create :

une variable de type pthread_t ;
un pointeur sur une variable de type pthread_attr_t ;
un pointeur sur une fonction qui reçoit un argument de type void* et qui
retourne une variable de type void* ;
un pointeur sur une variable de type void ;
aucune des réponses précédentes.
(xxi) Que retourne la fonction pthread_self ?
le pointeur de la fonction exécutée par le fil d’exécution ;
un pointeur sur un code d’erreur ;
une valeur booléenne qui indique si le fil d’exécution est en cours d’exécution ;

une variable de type pthread_t ;
aucune de ces réponses.
(xxii) En programmation multifilaire, qu’est-ce qu’un mutex ?
un fil d’exécution qui possède l’exclusivité du processeur ;

un mécanisme permettant d’accorder un accès exclusif à une ressource ;
un fil d’exécution qui exclut toute possibilité de mutation ;
un fil d’exécution mutant (l’équivalent du processus « zombie » pour les fils) ;
aucune de ces réponses.
(xxiii) Laquelle des fonctions suivantes n’appartient pas à l’interface POSIX du mutex ?
lock ;
unlock ;

wait ;
trylock ;
aucune de ces réponses.
(xxiv) En programmation multifilaire, qu’est-ce qu’une condition ?
le résultat d’un test permettant de déterminer la priorité d’un fil d’exécution ;

un mécanisme pour synchroniser des fils d’exécution qui attendent la réa-
lisation d’une condition booléenne ;
l’état qui permet à un fil de démarrer son exécution ;
la propriété qui permet à un fil de poursuivre son exécution ;
aucune de ces réponses.

Page 5 de 12
GIF-4104/7104 Examen Partiel 11 mars 2015

(xxv) Laquelle des fonctions suivantes n’appartient pas à l’interface POSIX de la condition ?
wait ;
signal ;
broadcast ;

lock ;
aucune de ces réponses.
(xxvi) Qu’est-ce qu’une sémaphore ?

une condition associée à un compteur ;
un mutex associé à un compteur ;
deux mutex qui dépendent l’un de l’autre ;
un mutex associé à une condition ;
aucune de ces réponses.
(xxvii) Qu’est-ce qu’une variable atomique ?
une variable que l’on peut lire en un seul cycle d’horloge ;
une variable que l’on peut écrire en un seul cycle d’horloge ;
une variable que l’on peut lire, modifier, et réécrire en un seul cycle d’horloge ;

une variable que l’on peut lire, modifier et réécrire sans risque de course
critique ;
aucune de ces réponses.
(xxviii) Qu’est-ce qu’une directive OpenMP ?

un énoncé qui débute par « #pragma omp » ;
un énoncé du langage C qui contient une clause OMP ;
une boucle for qui contient des clauses spéciales ;
un énoncé qui contient un bloc « fork-join » ;
aucune de ces réponses.
(xxix) À quoi sert la clause reduction d’une directive for de OpenMP ?
à spécifier à OpenMP qu’une réduction du nombre d’itérations pour cette boucle
doit être effectuée ;
à définir un opérateur de réduction ;

à spécifier l’opération de réduction ainsi que la liste des variables sur les-
quelles cette opération doit s’appliquer ;
à spécifier uniquement la liste des variables qui doivent être réduites ;
aucune de ces réponses.
(xxx) Dans le cadre d’une communication point-à-point :
la livraison d’un message n’implique pas forcément une coordination entre
l’émetteur et le destinataire ;

Page 6 de 12
GIF-4104/7104 Examen Partiel 11 mars 2015

la livraison du message n’est pas garantie ;



l’émetteur du message doit toujours spécifier le rang du destinataire ;
le destinataire du message doit toujours spécifier le rang de l’émetteur ;
aucune de ces réponses.

Page 7 de 12
GIF-4104/7104 Examen Partiel 11 mars 2015

Question 2 (20 points sur 100)


Soit une boucle que l’on désire paralléliser à l’aide de OpenMP. Soit p le nombre de fils
d’exécution disponibles et n le nombre total d’itérations de la boucle.

(i) (5 points) Pour la clause schedule(static,k) de la directive for de OpenMP, où


k est un nombre entier positif, dites à quel fil d’exécution sera confiée quelle itération de
la boucle ? Numérotez vos fils de 0 à p − 1 et vos itérations de 0 à n − 1. Formulez votre
réponse en fonction de p, n et k.
Solution: Avec la clause « static », le compilateur va découper la séquence des n itéra-
tions en dn/ke blocs de k itérations consécutives et confier à tour de rôle ces blocs aux
différents fils d’exécution. Le premier bloc au premier fil, le bloc suivant au deuxième
fil, et ainsi de suite jusqu’au dernier fil. Si n < pk, il se peut qu’un fil ne reçoive aucun
bloc. Si n > pk, il se peut qu’un fil reçoive plusieurs blocs. De façon générale, l’itéra-
tion i appartiendra au bloc j = bi/kc, et le bloc j sera confié au fil j mod p. L’itération
i sera donc confiée au fil bi/kc mod p.

(ii) (5 points) En supposant que chacune des n itérations demande le même temps d’exécu-
tion, que changerait la clause schedule(dynamic,k) ?
Parmi les stratégies static et dynamic, laquelle offrirait la meilleure performance et
pourquoi ?
Solution: Dans le cas d’une clause « dynamic », l’affectation des blocs aux fils d’exécu-
tion se fera dynamiquement, c’est-à-dire au moment de l’exécution, plutôt que lors de la
compilation. Pour une même valeur de k, le résultat sera donc similaire, dans le sens où
chaque fil recevra un premier bloc, mais il se pourrait que l’ordre des blocs ne soit pas le
même, car la norme ne précise pas cet ordre. Pour les rondes subséquentes d’affectation
de blocs, cela dépend de la durée d’exécution de chaque bloc. Si toutes les itérations
demandent le même temps d’exécution, alors chaque bloc demandera aussi le même
temps d’exécution, et tous les fils compléteront leur travail au même moment puis, le
cas échéant, recevront le bloc suivant. De nouveau, l’ordre d’affectation des blocs au
différents fils est imprévisible, mais, pour les deux stratégies, le nombre de bloc par fil
sera le même. Autrement dit, la performance sera similaire, bien que légèrement infé-
rieure pour la stratégie dynamique, puisque les décisions d’affectation des blocs aux fils
d’exécution se fait pendant l’exécution et que ceci consomme du temps.

Page 8 de 12
GIF-4104/7104 Examen Partiel 11 mars 2015

(iii) (10 points) Soit le code C/C++ d’une fonction qui calcule la moyenne des nombres
contenus dans un tableau de n valeurs :

1 double moyenne(double *valeurs, long n) {


2 double somme = 0;
3 int i = 0;
4 for(i=0; i<n; ++i) {
5 somme += valeurs[i];
6 }
7 return somme/n;
8 }

Modifiez cette fonction afin de la paralléliser avec OpenMP.


Solution:

1 #include <omp.h>
2
3 double moyenne_omp(const double *valeurs, long n) {
4 double somme = 0;
5 #pragma omp parallel
6 {
7 #pragma omp for reduction(+:somme)
8 for(int i=0; i<n; ++i) {
9 somme += valeurs[i];
10 }
11 }
12 return somme/n;
13 }

Page 9 de 12
GIF-4104/7104 Examen Partiel 11 mars 2015

Question 3 (10 points sur 100)


Une grappe de calcul telle que Colosse comporte une multitude de nœuds de calcul reliés
ensemble par une réseautique haute performance (dans le cas de Colosse, Infiniband QDR).

(i) (5 points) Expliquez comment vous procéderiez pour quantifier la latence moyenne (en
secondes) de la réseautique d’une grappe de calcul ?
Solution: Pour mesurer la latence d’un réseau de communication, il s’agit d’échanger
entre deux points du réseau un message le plus court possible et de mesurer le temps de
transmission aller-retour du message. On parle souvent d’un ping-pong : un processus
source transmet un message de 1 octet à un processus destination et celui-ci le retourne
à la source dès sa réception. Le temps mesuré divisé par deux correspond à la latence
d’une communication point-à-point.
En pratique, il est souvent imprécis de mesurer le temps d’un seul ping-pong, car celui-ci
est très court. Pour cette raison, on mesure souvent la durée de n ping-pong et l’on divise
cette mesure par 2n afin d’estimer la latence moyenne. Cette façon de faire, cependant,
ne permet pas de mesurer la variance de la latence. Également, il faut bien choisir la paire
de nœuds entre lesquels on effectue le ping-pong. On peut aussi considérer plusieurs
paires de nœuds et faire une moyenne des résultats.

(ii) (5 points) Expliquez comment vous procéderiez pour quantifier le débit maximum (en
octets par seconde) de la réseautique d’une grappe de calcul ?
Solution: Pour mesurer le débit d’un réseau de communication, il suffit d’échanger
entre deux points du réseau un très long message (mégaoctets) et de mesurer le temps
de transmission aller-retour du message. Deux fois la longueur du message divisée par
le temps mesuré correspond au débit maximum d’une communication point-à-point. Il
peut être avantageux de répéter plusieurs fois l’opération afin d’estimer la moyenne et la
variance du débit. Souvent, on fait varier la longueur du message et on trace une courbe
du débit en fonction de la longueur.
En utilisant un message très court (1 seul octet), la latence est prédominante et on obtient
le débit minimum. Lorsque le message est très long, la latence devient négligeable et on
obtient le débit maximum.

Page 10 de 12
GIF-4104/7104 Examen Partiel 11 mars 2015

Question 4 (10 points sur 100)


Soit une application MPI pour laquelle chaque processus i = 0, · · · , p − 1, possède un tableau
de ni valeurs :

1 double *valeurs;
2 long ni;

Supposez que ces ni valeurs sont déjà dans la mémoire des p processus.
On veut calculer la variance σ 2 de l’ensemble de toutes les valeurs de tous les processus en
utilisant la formule :  
p−1 i −1
nX
(xij − µ)2 
X

i=0 j=0
σ2 = 
p−1

X
 ni  − 1
i=0
Pp−1 Pni −1 Pp−1
où µ = ( i=0 j=0 xij )/( i=0 ni ) désigne la moyenne des valeurs xij .
Il s’agit donc, dans un premier temps, de calculer la moyenne globale µ des xij (valeur j du
processus i), puis de calculer la variance globale σ 2 , dans un deuxième temps.

(i) (5 points) Proposez un algorithme (pseudocode) pour résoudre ce problème en faisant


appel à des opérations de communications collectives. Quel est le nombre minimum de
ces opérations ?
Solution: Voici les étapes de l’algorithme :
Pni −1
1. chaque processus calcule la somme de ses valeurs locales si = j=0 xij ;
2. chaque processus effectue un opération de réduction (MPI_Allreduce) sur
les couples (si , ni ) qui engendre le couple (S, N ) ;
3. chaque processus calcule la moyenne µ = S/N ;
Pni −1
4. chaque processus calcule la somme s2i = j=0 (xij −µ)2 de l’écart à la moyenne
de ses valeurs au carré ;
5. le processus racine (ou tous les processus si la variance est requise par tous)
effectue une opération de réduction sur s2i qui engendre la valeur S2 ;
6. le processus racine (ou tous les processus, le cas échéant) calcule la variance
σ 2 = S2/N .
Cet algorithme nécessite deux opérations collectives de réduction dont au moins une qui
s’applique à tous les processus.

Page 11 de 12
GIF-4104/7104 Examen Partiel 11 mars 2015

(ii) (5 points) Codez votre algorithme en C/MPI.


Solution:

1 #include <mpi.h>
2
3 int main(int argc, char *argv[])
4 {
5 MPI_Init(&argc,&argv);
6 int rang;
7 MPI_Comm_rank(MPI_COMM_WORLD, &rang);
8 double *valeurs;
9 long ni;
10 // ...
11 // lecture des ni valeurs par le processus de rang i
12 // ...
13 // 1. chaque processus calcule la somme de ses valeurs
14 double si = 0;
15 for(int j=0; j<ni; ++j) {
16 si += valeurs[j];
17 }
18 // 2. chaque processus effectue une reduction sur (si, ni)
19 double sbuf[2] = {si, ni};
20 double rbuf[2] = {0, 0};
21 MPI_Allreduce(sbuf, rbuf, 2, MPI_DOUBLE, MPI_SUM,
MPI_COMM_WORLD);
22 // 3. chaque processus calcule la moyenne a partir de rbuf
=(S, N)
23 double mu = rbuf[0] / rbuf[1];
24 // 4. chaque processus calcule la somme de ses ecarts a la
moyenne au carre
25 double s2i = 0;
26 for(int j=0; j<ni; ++i) {
27 s2i += (valeurs[j]-mu)*(valeurs[j]-mu);
28 }
29 // 5. le processus racine effectue une reduction sur s2i
30 double S2 = 0;
31 MPI_Reduce(&s2i, &S2, 1, MPI_DOUBLE, MPI_SUM, 0,
MPI_COMM_WORLD);
32 // 6. le processus racine calcule la variance
33 if(rang == 0) {
34 double sigma2 = S2/(N-1);
35 }
36 MPI_Finalize();
37 return 0;
38 }

Page 12 de 12