Вы находитесь на странице: 1из 36

1.

Les Multiprocesseurs à Mémoire Partagée


1.1. Architecture des Machines Parallèles

Jean-Paul Sansonnet

Les Multiprocesseurs à Mémoire Partagée sont des machines parallèles de type MIMD,
composées de PEs qui sont reliés à une mémoire commune par un réseau d'interconnexion.
Les applications typiques que l'on exécute sur ces machines sont des Applications
Concurrentes. Un programme est composé de tâches (ou processus), indépendantes les unes
des autres, mais qui ont besoin de coopérer pour se communiquer des informations et qui
entrent souvent en compétition pour accéder aux ressources de la machine : PE, mémoire, E/S
etc.

Le problème de la Communication et de la Compétition entre processus dans les


Multiprocesseurs à Mémoire Partagée est à l'origine d'une discipline importante en
informatique : les Systèmes Concurrents. Plusieurs modèles de calcul et de multiples
mécanismes logiciels ou matériels ont été proposés pour permettre au programmeur de gérer
la communication et la compétition entre les processus de son application. Cependant, jusqu'à
présent on n'a pas su conjurer le spectre des dead locks : blocages mortels de l'application dus
à des causes diverses comme la famine (manque de ressources), l'attente croisée (deux
processus se bloquent réciproquement) etc.

Les "Philosophes mangeurs de Spaghetti" (a) est un exemple de problème de synchronisation


de processus qu'on rencontre dans la programmation des Multiprocesseurs à Mémoire
Partagée. Il s'agit d'un problème classique de synchronisation de processus dans les Operating
Systems Multitâches qui fonctionnent sur des machines séquentielles conventionnelles. Ceci
montre que les modèles et les outils que l'on utilise dans les Multiprocesseurs à Mémoire
Partagée sont très proches des modèles et des outils que l'on utilise dans les Operating
Systems Multitâches.

2. Sommaire

Mécanismes de Contrôle des Processus

Constructeurs de Base.
Les Instructions Fork&Join.
Exemple de Fork&Join.
Les Instructions ParBegin/ParEnd.
L'instruction Doall.
Les Processus.

Mécanismes de Synchronisation des Processus

Le Besoin de Synchronisation dans les Communications.


L'instruction Test&Set et les Sémaphores de Dijkstra.
Barrières de Synchronisation.
L'instruction Fetch&Add.

Gestion des Caches


La Gestion de la Mémoire Partagée dans les Multiprocesseurs.
Mémoire Partagée et Mémoire Distribuée.
Principe de Fonctionnement d'un Cache.
Maintien de la Cohérence des Données dans les Caches.
Multiple Write Back.
Multiple Write Through.
Utilisation de Répertoires Centralisés.
Algorithmes Logiciels de Gestion des Caches.

Machines à Mémoires Multiports

Principe des Mémoires Multiports.


Mémoire Multiports de Tanaka.
La Machine à Mémoire Multiports Série : M3S.

Les Hotspots

Le Phénomène de Hotspot.
Implémentation du Fetch&Add dans le NYU.

Partitionnement des Applications

La Gestion des Processus dans les Multiprocesseurs.


Le Partitionnement dans HEP.
Auto Scheduling dans HEP.
Architecture de HEP.

Quelques Multiprocesseurs

C.mmp.
L'architecture Butterfly de BBN.
La Machine RP3 d'IBM.
La Gestion Mémoire dans la Machine RP3.

3. Constructeurs de Base

1. Lancement et Arrêt de Processus Concurrents

La programmation des Multiprocesseurs à mémoire Partagée met en oeuvre des mécanismes


logiciels qui permettent de gérer les Processus Concurrents qui s'exécutent sur les PEs. Les
constructeurs de base des Processus Concurrents concernent le lancement/arrêt d'un processus
et la synchronisation des processus.

Les constructeurs de base pour le lancement et l'arrêt des processus sont historiquement
hérités des constructeurs de base du contrôle séquentiel de Von Neumann (a-b) : à la séquence
correspond les constructeurs permettant de délimiter un bloc d'instructions à exécuter en
parallèle ; à la conditionnelle correspond les constructeurs permettant de lancer en parallèle
deux blocs d'instructions ; à la répétition correspond les constructeurs permettant de répliquer
une action sur des données similaires ; enfin, à l'instruction d'appel correspond les
constructeurs permettant de désigner les processus.
2. Synchronisation de Processus Concurrents

Les constructeurs de base pour la synchronisation des Processus Concurrents servent à gérer
la Compétition et la Coopération entre processus.

 La Compétition (c) intervient lors du partage de ressources. Les constructeurs de base


permettent d'éviter que plusieurs processus mélangent leurs instructions d'accès à la
ressource : on utilise des instructions spéciales comme le Test&Set ou des structures
de données spéciales comme les Sémaphores.
 La Coopération (d) intervient lorsque les processus ont des informations à se
communiquer. Des constructeurs de base comme les Barrières de Synchronisation
permettent de gérer la chronologie des communications pour éviter les phénomènes de
SurLecture ou de SurEcriture. Des constructeurs de base comme l'instruction
Fetch&Add permettent à la fois de gérer la compétition et la coopération entre
processus.

4. Les Instructions Fork&Join

Les instructions Fork et Join introduites par M.E. Conway en 1963 fournissent un mécanisme
de synchronisation du parallélisme permettant de contrôler l'activation de processus en
parallèle et ensuite de contrôler la poursuite du traitement après l'exécution des processus
parallèles. Ces instruction utilisent une variable de synchronisation CTR qui contient à tout
instant le nombre de processus actifs.

L'instruction Fork étiq. est une instruction de contrôle du parallélisme qui fonctionne comme
un branchement où on prend les deux branches à la fois. Si on rencontre cette instruction, on
lance le morceau de code qui commence à l'adresse spécifiée par l'étiquette de l'instruction
Fork et on continue en séquence. On obtient ainsi deux processus qui travaillent en parallèle.
Sur un ordinateur multiprocesseurs, le temps de mise en place d'un nouveau processus au
moyen d'un Fork peut être assez long : par exemple sur le système Unix-Balance qui tourne
sur Système Sequent, il faut 55 ms soit environ 25000 instructions ! Sur le système Mach, il
faut environ 3000 instructions (sur Vax). Ceci montre que ce mécanisme ne peut être utilisé
que pour lancer des tâches à très gros grain dont le temps d'exécution s'exprime en secondes.

L'instruction Join Ctr, étiq. est une instruction de contrôle du parallélisme qui exécute la
séquence indivisible suivante :

début
-- Ctr est une variable partagée de type entier
Ctr := Ctr - 1 ;
Si Ctr = 0 alors Goto étiq.
sinon exécuter l'instruction en séquence
-- C'est généralement un quit
fin

L'instruction quit a pour rôle d'arrêter un processus. Elle est généralement utilisée en
association étroite avec Join. Il existe des variantes de l'instruction Fork qui permettent de
manipuler le compteur de processus de manière insécable en même temps qu'on lance un
processus. Par exemple : Fork Ctr, étiq est équivalente à :

début
Ctr := Ctr +1 ;
Fork étiq
fin
5. Exemple de Fork&Join

Dans cet exemple, on veut lancer en tout 4 processus : P, Q, R et S puis reprendre l'exécution
dans P quand Q, R et S ont terminé. C'est Q qui lance automatiquement S quand il est appelé.
Avec les instructions Fork et Join utilisées, et en considérant que l'ordre de terminaison des
processus est celui donné ci-dessus, alors P reprend bien le contrôle de l'exécution.
Cependant, on constate que si par exemple Q avait pris du retard, on aurait l'ordre de
terminaison : R, S, P, Q et c'est Q qui aurait repris la main. Les instructions Fork et Join
obligent donc le programmeur à déterminer à l'avance la durée d'exécution des processus
lancés pour maîtriser le contrôle global du parallélisme.
6. Les Instructions ParBegin / ParEnd

Le constructeur Parbegin/ParEnd a un fonctionnement très simple : les blocs compris entre


ParBegin et ParEnd sont exécutés en parallèle et l'exécution est reprise en séquence quand le
dernier des blocs a fini. Les séquences sont indiquées traditionnellement par begin/End.
L'exemple de code (a) fournit le graphe de programme (c). Le constructeur Parbegin/ParEnd
est plus structuré que le constructeur Fork&Join. En effet, dans l'exemple de code (b) qui
produit le graphe (d), il est possible d'imbriquer les Forks et les Joins sans les faire
correspondre exactement deux à deux comme les ParBegin/ParEnd. L'étiquette g: marque un
point de jointure venant de deux forks différents. Cependant, le Fork&Join permet de réaliser
des graphes parallèles plus puissants que le ParBegin/ParEnd, mais il est très difficile à
maîtriser à-la-main et doit être limité à un emploi dans les couches d'implémentation de bas
niveau.
7. L'Instruction Doall

L'instruction Doall applique de manière itérative le principe ParBegin/ParEnd à un bloc


d'instructions. Il permet au programmeur de spécifier un seul processus qui au lieu d'être itéré
comme dans le mode séquentiel est répliqué automatiquement pour produire autant de
processus. L'instruction Doall est bien adaptée aux traitements en parallèles sur les tableaux et
les applications qui utilisent une représentation sous forme de tableaux.

Par exemple, pour effectuer les calculs de prévision météorologique (Weather Code), on
décompose l'espace en une grille à trois dimensions (a), selon l'altitude, la latitude et la
longitude. Le pas de discrétisation de l'espace produit une représentation de complexité
polynômiale (L x N x M) qui peut être stockée sous forme d'un tableau à trois indices. Dans la
version séquentielle (b), on utilise trois boucles imbriquées. Dans la version parallèle (c) une
seule instruction Doall génère tous les processus : un pour chaque point i, j, k de l'espace.

• Code Séquentiel (b) :

For i = 1 to L
For j= 1 to M
For k = 1 to N
u*[i,j,k] = n* pi[i,j] * u[i,j,k]
v*[i,j,k] = m* pi[i,j] * v[i,j,k]
s[i,j,k] = pi[i,j] * sigma[i,j]
End
End
End

• Code Parallèle (c) :

Doall longitude, latitude, altitude


u*[i,j,k] = n* pi[i,j] * u[i,j,k]
v*[i,j,k] = m* pi[i,j] * v[i,j,k]
s[i,j,k] = pi[i,j] * sigma[i,j]
End
8. Les Processus

Comme Pratt, on peut voir les processus comme le résultat de la relâche de certaines
restrictions sur les blocs de code. Ce qui différencie les subroutines du code expansé inline
c'est le fait que la macroexpansion ne peut pas être récursive. Ce qui différencie les coroutines
des subroutines c'est que les coroutines ne sont pas obligés de se terminer quand on les
appelle. Ce qui différencie les processus des coroutines c'est que les processus peuvent
s'exécuter en parallèle alors que les coroutines sont toujours concurrentes (elles partagent le
CPU unique).

Lorsqu'on crée des processus parallèles ou tâches, on peut le faire de manière statique comme
en Concurrent-Pascal ou Modula ou bien de manière dynamique comme en PL/1 ou en Ada.
Si l'allocation est statique (a), on peut réserver en mémoire partagée au début de l'exécution de
l'application. Cependant les tâches occupent leurs ressources même si elles ne travaillent pas.
Si l'allocation est dynamique (b), on peut mieux organiser l'allocation des ressources mais cela
prend du temps supplémentaire, en cours d'exécution, pour effectuer cette gestion. De plus, la
gestion dynamique des tâches est plus difficile à maîtriser.

9. Le Besoin de Synchronisation dans les Communications

Pour calculer les n itérations de la formule (a) on crée deux processus asynchrones : P calcule
les sommes ai + bi et Q calcule les produits résultat-de-P * ci. Les deux processus partagent
donc les résultats de Q. Le processus P se comporte comme un producteur de données et Q
comme un consommateur des données produites par P. On peut modéliser le comportement
des processus P et Q par le schéma (b).
On voit que, parce que les processus s'exécutent de manière indépendante, leurs indices
varient indépendamment selon la vitesse d'exécution propre de chaque processus. Cette
différence peut provoquer des inconvénients graves : par exemple, si P est plus rapide que Q
(cas de cette formule) alors il produit plus de résultats intermédiaires que Q ne peut en
consommer et il y a une accumulation (c) qui peut conduire à une perte d'informations. Au
contraire, si Q est plus rapide que P (+ et * sont inversés), alors il relit des résultats issus de P
qu'il a déjà traités et il produit des résultats faux (d).

10.Instruction Test&Set et les Sémaphores de Dijkstra

Le mécanisme le plus simple de synchronisation entre des processus (Ping, Pong) qui
partagent une même variable x est l'instruction Test&Set dérivée des Operating Systems
multitâches. La variable partagée x est alors composée de deux champs (a) : un champ valeur
et un champ sémaphore d'un bit qui permet d'implémenter un certain nombre de mécanismes
de communication entre les processus Ping et Pong via la variable x. L'instruction Test&Set
réalise de manière non interruptible l'opération mémoire décrite en (b). L'instruction Test&Set
permet d'implémenter de nombreux mécanismes de contrôle d'accès. Parmi ceux-ci, les
Sémaphores de Dijkstra sont très populaires : il s'agit d'associer à la ressource dont on veut
contrôler l'accès indivisible une variable partagée de type booléen appelée sémaphore.
Code des processus Ping et Pong :

Process Ping
-- attente active sur le sémaphore :
While Test&Set (x.sem) = 0 do nothing;
F (x.value) ; -- travail sur x...
x.sem := 1

Process Pong :
-- attente active sur le sémaphore :
While Test&Set (x.sem) = 0 do nothing ;
G (x.value) ; -- travail sur x...
x.sem := 1

Lorsqu'un des deux processus veut accéder à la variable x il commence par tester si le
sémaphore est ouvert (x.sem = 1).

 Si ce n'est pas le cas, l'instruction Test&Set renvoie 0 et ne modifie pas le sémaphore ;


le processus réitère alors ad libitum sa consultation du sémaphore, bloquant ainsi le
processeur sur lequel il travaille : c'est l'attente active. L'attente active n'est pas
envisageable dans un système multitâches monoprocesseur car cela conduit à un
deadlock (le processeur ne peut travailler et ne veut pas lâcher pour autant la seule
ressource de calcul de la machine : le monoprocesseur) mais elle peut être envisagée
sur une machine multiprocesseurs car les autres processeurs continuent à travailler et
peuvent accéder à la variable partagée.
 Par contre, si le sémaphore est ouvert, alors l'instruction Test&Set renvoie 1 et elle
ferme le sémaphore (x.sem ¨ 0) ; le processus sort de l'attente active et peut lire et
écrire dans de champ x.value de la varible x autant qu'il le souhaite ; pendant ce temps,
les autres processus trouvent le sémaphore fermé et restent bloqués en attente active ;
quand le processus a fini son travail sur le champ x.value de la variable x, il doit
rouvrir le sémaphore par une simple instruction x.sem ¨ 1 qui a pour conséquence
d'autoriser tout autre processus à accéder à la variable x.
11.Barrières de Synchronisation

Les Barrières de Synchronisation sont nécessaires lorsqu'on travaille en mode asynchrone


(MIMD) avec des structures de données de type ParBegin/ParEnd ou bien Doall.
Normalement, la structure ParBegin/ParEnd contient implicitement une barrière de
synchronisation (a) : les processus P, Q et R attendent que le dernier ait fini pour franchir la
barrière. Dans le cas d'un Doall sur lequel on boucle comme c'est le cas dans le code pour la
météorologie (b), on obtient des cycles q, q+1, q+2, ... Il ne faut pas mélanger les calculs du
corps de Doall du cycle q et du cycle q+1. C'est pourquoi, on associe au Doall une instruction
de barrière EndSync qui oblige les L x N x M processus à attendre que tous aient terminé pour
attaquer un nouveau cycle.

12.L'instruction Fetch&Add
Fetch-and-Add Généralisation Test&Set
F&A (V, e) F&ƒ (V, e) T&S (V)
Lire V ; Lire V ; Lire V ;
V := V + e V := ƒ (V , e) V := 1

Le New-York Ultra Computer (NYU) utilise un mode de synchronisation très original fondé
sur une instruction d'accès mémoire : Fetch-and-Add (V, e) où V est l'adresse d'une variable
scalaire de synchronisation située en mémoire partagée et e est une expression entière. Cette
instruction exécute de manière insécable (a) : la variable V est lue et incrémentée de la valeur
entière e. Une forme plus générale de cette instruction (b) permet de définir d'autres fonctions
(en général booléennes) que l'addition d'un entier. On a montré qu'une grande classe de
mécanismes de synchronisation pouvaient être implémentés au moyen de la simple instruction
Fetch&Add.

Le fonctionnement de l'instruction Fetch&Add est très proche de celui de l'instruction


Test&Set (c) mais il y a une différence essentielle : le Fetch&Add est non bloquant ; cela
revient à dire que si N Processus exécutent le Fetch&Add en même temps ils recevront tous
une réponse (sous forme d'un entier). L'ordre des réponses n'est pas prévisible par le
programmeur : il est non-spécifié. Le Test&Set est bloquant car un seul processus peut faire
cette instruction à la fois. Le défaut de l'instruction Fetch&Add est qu'elle nécessite une
implémentation matérielle en association avec un réseau d'interconnexion pour combiner les
demandes puis dispatcher les réponses. Ce réseau contient plus de matériel et est aussi
beaucoup plus lent qu'un réseau d'interconnexion normal : les estimations de Gottlieb
indiquent un facteur de 5 à 25 fois plus lent.

Programme principal ; Pout tout Processus Pi = 1, N


* ;
*
* Begin
* -- X est un générateur centralisé qui
fournit les indices
X:= 0 ; Y := N ;
I := F&A (X, 1) ;
Lancer N processus Pi = 1, N ;
-- Attente active sur le passage de *
la Variable Y à 0 *
While F&A (Y,0) 0 do * -- Travail sur T [I]
nothing ; *
* *
* *
* F&A (Y, -1)
* End
*
suite du Prog. Princ.

Exemple : on désire assigner N processus à N cases d'un tableau T pour y faire un certain
travail, puis attendre que tout le monde ait fini et continuer l'exécution. On utilise deux
variables de synchronisation : Y qui compte combien il reste de processus encore actifs et X
qui fournit aux processus lancés un bon indice pour aller chercher la case du tableau T qui les
concerne. Le programme principal (d) affecte la variable Y à N et lance en parallèle les N
processus. Chaque processus lancé (e) se met en attente active sur la variable générateur
d'indice X ; dès qu'il a une réponse (X est alors insécablement incrémenté) le processus
effectue son travail sur l'élément associé à cet indice et décrémente Y pour dire qu'il a fini.
Pendant ce temps, le programme principal se met en attente du passage de Y à 0 pour
continuer.

13.La Gestion de la Mémoire Partagée dans les Supercalculateurs

Dans les Supercalculateurs, la mémoire centrale est partagée logiquement : cela veut dire
qu'elle est vue par chaque processeur dans sa totalité au niveau du mécanisme d'adressage. En
pratique, elle peut être découpée en bancs mémoire ; il arrive même que les bancs soient
placés dans les processeurs ! (RP3), mais il s'agit quand même d'une mémoire commune. La
hiérarchisation dans les machines M MIMD complique encore l'organisation mémoire qui
peut être vue comme globale à un certain niveau et locale à un autre niveau (HEP).
Pour relier physiquement les bancs mémoire aux processeurs on utilise généralement un
réseau d'interconnexion. Un réseau d'interconnexion se distingue d'un réseau
d'intercommunication en cela qu'il relie des organes internes d'une machine (ici le processeur
et sa mémoire) alors que les réseaux d'intercommunication relient des machines entre elles (de
processeur à processeur). La conséquence est que les bus d'interconnexion ont généralement
un très grand débit d'information à passer et que la performance d'instructions aussi
importantes que Load et Store dépendent directement de ce débit : ils doivent avoir un temps
de service extrêmement court et une bande passante très grande. Cela peut être réalisé au
moyen d'un bus mémoire (Alliant) ou d'un bus hiérarchisé si la machine est hiérarchisée
(Cm*). Si le nombre de points à relier devient grand (NYU, RP3) on préfère alors des réseaux
multi-étages. Par contre, pour un petit nombre de processeurs (Cray) on relie tous les
processeurs à tous les bancs (Réseau Crossbar).

Pour éviter la contention mémoire dans le cas d'une mémoire commune (avec ou sans
découpage en bancs) on adjoint très souvent une mémoire cache à chaque processeur. Cette
mémoire locale rapide a deux avantages : elle permet d'accélérer les accès par effet-cache et
elle évite aux processeurs d'accéder à la mémoire commune, diminuant ainsi la contention.
Cependant, on sait que la gestion d'un cache est assez complexe dans une machine
séquentielle classique. Dans un environnement multiprocesseurs, cette gestion est encore plus
difficile et différentes politiques ont été proposées.

14.La Mémoire Partagée

Dans une machine monoprocesseur classique la mémoire centrale idéale est une mémoire de
taille infinie et de temps d'attente nul pour le processeur. Dans un Multiprocesseur à Mémoire
Partagée, la mémoire idéale doit posséder une propriété supplémentaire : elle doit être
multiports. Une mémoire multiports (a) est capable de servir les N PEs de manière
indépendante, c'est-à-dire est capable d'effectuer N requêtes émanant de N PES
simultanément dès lors que les requêtes portent sur des mots mémoire d'adresse différente (en
effet, en cas d'accès à une variable partagée, il est indispensable de sérialiser les demandes)
A l'heure actuelle, on sait à peine réaliser des mémoires bi-ports. La plupart du temps, on a
affaire à une mémoire monoport classique (b) où les demandes sont systématiquement
sérialisées par un arbitre : ceci crée un goulet de contention très grave.

Pour éviter ce goulet, on a recours à deux techniques complémentaires que l'on peut combiner
:

 Les Caches (c) : on place entre le PE et la mémoire partagée une petite mémoire
locale très rapide qui contient des blocs de mémoire centrale. L'accès au cache se fait
de manière classique en terme de mots. Si lors d'un accès au cache le mot demandé par
le PE n'y est pas, alors on va chercher le bloc qui contient ce mot en mémoire
centrale : l'accès à la mémoire centrale se fait donc en terme de blocs et ne se produit
qu'en cas de défaut dans un des caches ; il est beaucoup moins fréquent que l'accès au
cache ce qui diminue le phénomène de contention dû au fait que la mémoire centrale
n'a qu'un seul port. De plus les caches permettent d'avoir des temps d'attente nuls pour
le PE.
 Les Bancs (d) : on découpe la mémoire centrale en bancs mémoire dont le nombre est
généralement (mais pas forcément) égal au nombre de PEs. Dans ce cas, les PEs et les
bancs mémoire doivent être reliés par un réseau d'interconnexion (généralement de
type dynamique multi-étages). Si les N PEs envoient N requêtes dont les adresses sont
situées dans des bancs mémoire différents, alors tout se passe comme si on avait une
mémoire N-ports ; cependant pour qu'à chaque ensemble de requêtes les adresses
soient bien réparties sur les bancs mémoire, il faut que l'organisation des données dans
l'application se prête à une telle distribution !
15.Principe de Fonctionnement d'un Cache

Dans une machine séquentielle classique, le cache n'est utilisé que pour accélérer les accès
mémoire. Le processeur fournit au contrôleur de cache l'adresse de la variable qu'il souhaite
accéder en lecture et celui-ci par diverses techniques (dispositifs associatifs, fonctions câblées
de Hash coding ...) peut dire si la donnée est dans le cache (cache hit) ou non (défaut de
cache) ; si elle n'y est pas, il la lit en mémoire centrale et la place dans le cache en vue d'un
prochain accès.

On possède donc deux copies de la valeur de la même variable. Pour maintenir la cohérence
entre ces deux copies, on utilise deux techniques :

 Le Write Through (a) : on effectue la mise-à-jour de la copie en mémoire centrale


"au plus tôt". Chaque fois qu'on modifie la valeur d'une donnée dans le cache, le
contrôleur de cache donne aussi un ordre de modification de la valeur en mémoire
centrale ; cet ordre étant asynchrone par rapport aux accès du processeur sur le cache,
le Write Through n'introduit pas de délai si les écritures ne sont pas trop rapprochées.
Il travaille généralement sur des mots.
 Le Write Back (b) : on effectue la mise-à-jour de la copie en mémoire centrale "au
plus tard". Chaque fois qu'on modifie la valeur d'une donnée dans le cache le
contrôleur de cache donne aussi un ordre d'invalidation du bloc du cache qui contient
cette donnée par positionnement d'un bit de bloc dans une table matérielle. Lors d'une
lecture, si la donnée n'est pas dans le cache, le défaut de cache qui s'ensuit provoque le
transfert entier du bloc qui la contient de la mémoire centrale vers le cache ; il faut
faire attention à ce que le bloc qu'on écrase dans le cache ne soit pas invalidé ; si c'est
le cas, il faut le recopier en mémoire centrale avant de pouvoir utiliser sa place pour
faire venir le nouveau bloc ! Le Write Back essaye de reculer le plus longtemps
possible les mises-à-jour en mémoire centrale. Il travaille essentiellement au niveau
des blocs (de taille 16 à 256 mots). Le Write Through est beaucoup plus simple à
implémenter que le Write back mais on a montré que le Write Back apporte de
meilleurs résultats.
16.Maintien de la Cohérence des Données dans les Caches

Dans un Multiprocesseur à Mémoire Partagée qui utilise un cache associé à chacun de ses PEs
(a), si une variable est partagée par plusieurs processus, il arrive alors qu'on ait plusieurs
copies dans plusieurs caches de la valeur de la variable qui est en mémoire partagée. Si une
des copies est modifiée par un PE, le Write-Through n'est plus suffisant pour maintenir la
cohérence. Il faut aussi effectuer des mises-à-jour dans tous les autres caches où il existe une
copie de cette variable.

On distingue trois grandes techniques pour résoudre le problème de la cohérence des données
dans les caches :

1. Mécanismes Matériels :

 Machines à Bus : Les principaux mécanismes matériels de maintien de la cohérence


des données dans les caches d'un Multiprocesseur organisé en Bus sont inspirés des
techniques classiques séquentielles :
- Mode Multiple Write Through,
- Mode Multiple Write Back.
 Machines à Réseau d'interconnexion : dans ces machines on utilise généralement des
mécanismes matériels à Répertoire centralisé.

1. Algorithmes Logiciels : les variables partagées sont détectées par le compilateur qui
les marque pour qu'elles ne puissent pas être montées dans les caches.
2. Cache partagé (b) : le cache est décomposé en bancs et le réseau d'interconnexion est
placé entre les bancs du cache et les processeurs. La mémoire commune apparaît alors
(à un degré de vitesse près) comme une mémoire secondaire.
17.Multiple Write Back

En 1983, Goodman a proposé un algorithme de de type Write back adapté aux


Multiprocesseurs organisés en Bus : il s'agit de l'algorithme Write Once qui est basé sur le
principe des espions de bus (snoopy) : les blocs contenant la variable X sont copiés dans les
caches (a) ; l'écriture par un PE dans un de ces blocs est reportée sur la copie en mémoire
(Write Once) et invalide les autres blocs (b).

Lorsqu'un PE lit ou écrit dans son cache, tous les espions de bus associés aux autres caches
consultent le répertoire de leur cache pour faire passer le bloc contenant le mot accédé (s'il y
est) dans un nouvel état selon le diagramme (c).

Chaque répertoire de cache est accédé par le PE et l'espion de bus. Plusieurs variantes de cet
algorithme ont été proposées : Synapse, SPUR de Berkeley, Illinois...
Flèches en gras : Transitions dues aux PEs
Flèches en maigre : Transitions dues aux espions de bus.

Etats d'un Bloc :

Invalide : Le bloc est libre.

Valide : Le bloc n'a jamais été modifié.

Réservé : Le bloc a été modifié par une seule écriture reportée sur le bus ;

la mémoire a été mise à jour et les autres blocs sont invalidés.

Modifié : Le bloc a été modifié par plus d'une écriture : c'est la seule copie à jour.

Fonctionnement de l'algorithme "Write Once" :

1. Défaut de lecture (Read Miss)

 Etat Modifié : le cache qui possède la bonne copie la fournit et met à jour la mémoire.
Ces deux blocs deviennent alors Valides.
 Autres états : La mémoire fournit le bloc.

2. Ecriture avec présence (Write Hit)

 Etat Modifié : écriture locale


 Etat Réservé : écriture suivie du passage à l'état Modifié
 Etat Valide : écriture simultanée dans le bloc et en mémoire suivie du passage à l'état
Réservé (Write Once).

3. Défaut d'écriture (Write Miss)

 Etat Modifié : le cache qui possède la bonne copie la fournit et met à jour la mémoire.
Le bloc écrit passe à l'état Réservé et les autres blocs dans les caches à l'état Invalide.
 Autres états : La mémoire fournit le bloc. Le bloc écrit passe à l'état Réservé et les
autres blocs dans les caches à l'état Invalide.
18.Multiple Write Through

Dans les algorithmes basés sur le principe du Write Through (a), en lecture, on autorise la
copie multiple dans les caches de blocs issus de la mémoire centrale partagée ; en écriture, il
faut effectuer autant d'écritures qu'il y a de copies, y compris en mémoire (b)
La station de travail Firefly de DEC utilise un algorithme de maintien de la cohérence des
caches basé sur des espions de bus, de type Write Through. Dans cette architecture, on
dispose d'un bus spécial pour effectuer les mises à jour. Le diagramme des transitions d'état
est donné en (c) ; les états notés miss-P et hit-P dans le diagramme (c) indiquent que la ligne
Bloc-Partagé est active.

Archibald et Baer à qui on doit une classification des méthodes de gestion des caches et les
diagrammes de transition d'états ont effectué des comparaisons entre les méthodes Write
Through et Write Back :

 si on a peu de données partagées, les algorithmes sont très proches les uns des autres,
 si on a beaucoup de données partagées, alors la mise à jour immédiate (Write
Through) donne de meilleurs résultats que l'invalidation (Write Back) surtout si le le
nombre de blocs partagés est faible ; si le taux de partage diminue ou si le nombre de
partage augmente alors l'écart de performances entre les deux techniques diminue.

Flèches en gras : Transitions dues aux PEs


Flèches en maigre : Transitions dues aux espions de bus.

Etats d'un Bloc :

Invalide : Le bloc est libre.

Valide-exclusif : Le bloc est non modifié ; il n'existe pas de copies dans un autre cache.

Partagé : Le bloc est diffusé dans plusieurs caches.

Modifié : Le bloc a été modifié, c'est la seule copie à jour.


Dans le diagramme (c) l'état Invalide n'est pas présent car il n'y a pas d'opération
d'invalidation explicite.

19.Utilisation de Répertoires Centralisés

Dans les Multiprocesseurs à Mémoire Centrale Partagée dont le réseau d'interconnexion n'est
pas un Bus, il n'est pas possible d'utiliser des algorithmes de type espion de bus car il n'est
plus possible de façon simple et efficace de connaître en permanence la situation exacte des
blocs au niveau de chaque cache. On utilise alors un Répertoire Centralisé qui contient l'état
de chaque bloc et sur lequel on peut appliquer les techniques Write Through ou Write Back.

L'algorithme le plus ancien de Répertoire Centralisé est celui que Tang a proposé en 1976.
Son principe est que les blocs sont diffusés en lecture mais qu'un seul bloc peut être modifié
en écriture. Son fonctionnement est le suivant :

1. Défaut de Lecture : On consulte le Répertoire Central pour savoir où est la copie à


jour. Si elle est en mémoire, elle est diffusée sinon si elle est dans un cache on la
ramène d'abord en mémoire et on la diffuse.
2. Ecriture avec présence dans un cache : Si l'état local du bloc accédé est Modifié alors
on peut écrire sans délai sinon on passe l'état local à Modifié et on prévient le
Répertoire Central qui invalide les copies présentes dans les autres caches.
3. Défaut d'écriture : Si le bloc est diffusé en écriture, il est d'abord ramené en mémoire
centrale, puis fourni avec l'état local Modifié au cache demandeur qui écrit dedans.
Dans le cas où le bloc est diffusé en lecture dans d'autres caches, ces copies sont
invalidées et le bloc est fourni avec l'état local Modifié au cache demandeur qui écrit
dedans.

En 1978, Censier et Feautrier ont proposé un algorithme semblable à celui de Tang, mais dont
l'organisation du Répertoire Central est différente (a). On associe à chaque bloc un vecteur de
présence qui contient un bit par PE et un bit supplémentaire de Lecture/Ecriture :

 Le bit Lecture/écriture = 0 : Le bloc est diffusé en Lecture. On place un bit à 1 dans le


vecteur de présence pour chaque PE dont le cache contient le bloc.
 Le bit Lecture/écriture = 1 : Le bloc est diffusé en Ecriture. On place un bit à 1 dans le
vecteur de présence dans la case correspondant au seul PE qui a le droit d'écrire (et
aussi de lire) dans le bloc.
Cette solution fournit les mêmes informations que le Répertoire Centralisé classique mais
l'accès au profil d'un bloc est plus rapide à mettre à jour. Elle a l'inconvénient majeur
d'occuper une place mémoire proportionnelle au nombre de blocs et au nombre de PEs ; par
exemple pour une mémoire de 16 M octets, organisée en blocs de 16 octets, et possédant 15
PEs il faut un Répertoire Centralisé de 2 M octets.

20.Algorithmes Logiciels de Gestion des Caches

Les algorithme logiciels visent à supprimer ou à diminuer les mécanismes matériels


nécessaires à la maintenance de la cohérence des informations dans les caches des
Multiprocesseurs. La solution la plus simple et la plus ancienne consiste à détecter à la
compilation les variables partagées et à les placer dans des blocs qui sont lockés en mémoire
centrale : ainsi, toutes les données dans les caches sont locales à un processus et
n'apparaissent que dans le cache où tourne ce processus. Cette solution est assez simpliste et
c'est pourquoi on a cherché à la raffiner en effectuant à la compilation une analyse plus
poussée des programmes pour essayer de placer sans trop de risque d'incohérence des données
partagées dans les caches : c'est le marquage des données.

Le marquage des données consiste à découper le graphe d'exécution du programme en


intervalles de temps caractéristiques. On utilise généralement les points de synchronisation
(explicites ou implicites) pour délimiter les intervalles. Pendant un intervalle de temps
caractéristique, une variable peut être dans un des quatre états définis en (a). A chaque fin
d'intervalle, l'état des données partagées doit être répercuté sur les autres caches et en
mémoire centrale. On dispose de trois algorithmes principaux pour le marquage des données
qui utilisent tous les trois la méthode du Write Through :

1. Algorithme Global : Cette méthode proposée par Cheong et Veidenbaum en 1986,


traite les données partagées de façon globale. Dans un intervalle de temps
caractéristique, toutes les données sont cacheables ou non. On utilise le Write Through
ce qui fait qu'à la fin de chaque intervalle de temps caractéristique les blocs en
mémoire sont toujours bons.
2. Algorithme d'invalidation sélective rapide : Cette méthode proposée par Cheong et
Veidenbaum en 1988, est une amélioration de l'algorithme précédent. Elle nécessite
l'ajout d'un mécanisme matériel au cache qui associe un bit de changement à chaque
bloc dans le cache. L'idée est de réaliser un test de cohérence au moment de l'accès
individuel à la donnée ; ainsi, on est sûr de fournir la dernière copie à jour, soit à partir
du cache soit à partir de la mémoire.
3. Algorithme à numéro de version : Cette méthode proposée par Cheong et
Veidenbaum en 1989, est une amélioration de l'algorithme précédent. Elle nécessite
l'ajout d'un mécanisme matériel sous forme d'un tableau de compteurs adressables et
d'un tableau de registres associés aux blocs dans le cache. A chaque variable du
programme on associe un compteur matériel dans le premier tableau ; chaque fois
qu'on passe un point de synchronisation, on incrémente ce compteur. Par ailleurs,
quand un bloc est chargé dans un cache, son registre est estampillé avec la valeur
courante du (ou du max des) compteur associé à la donnée partagée qui est dans ce
bloc. Lorsqu'on fait une référence au bloc, si la valeur du registre est supérieure à la
valeur du compteur alors la bonne copie est dans le cache sinon on va la chercher en
mémoire centrale.

Les résultats en simulation on fait apparaître des résultats très contrastés selon les types de
programmes parallèles utilisés (91% de présence dans un cas, 3% dans un autre !). Cependant
l'algorithme à numéro de version est celui qui présente le moins de contrastes d'un programme
à l'autre : c'est lui qui exploite le mieux la localité de l'information.

21.Principe des Mémoires Multiports

Depuis longtemps on a cherché à réaliser des mémoires Multiports pour les Multiprocesseurs
à Mémoire Partagée car les mémoires Multiports ne nécessitent pas de réseau
d'intercommunication. Une mémoire Multiports (a) est une mémoire centrale qui peut servir
simultanément plusieurs demandes (lectures et/ou écritures) à la seule condition que les mots
accédés par les demandeurs soient d'adresses différentes.

Ainsi, les seules attentes dues à la sérialisation des demandes ne se produisent que dans le cas
d'accès à une variable partagée.

 Une première façon de réaliser une mémoire Multiports est de la découper en bancs.
Chaque banc fournit un port qui permet de lire et d'écrire indépendamment à condition
que les mots accédés par les demandeurs soient sur des bancs différents : ceci est une
contrainte très importante dans l'organisation des accès mémoire.
 Des réalisations de mémoire bi-ports existent, en particulier dans les Multiprocesseurs
à bus. Dans l'exemple (b) on a un système à deux bus et deux PEs qui se partagent une
mémoire RAM à double accès. Il ne s'agit pas vraiment d'une mémoire bi-port car un
arbitre est placé sur la carte mémoire qui sérialise les accès ; en pratique, on est quand
même très près des performances d'une telle mémoire.
 On peut construire une vraie mémoire bi-port en dupliquant la mémoire (c) : en mode
lecture, on peut lire sur chaque port (associé à chaque banc) en un seul cycle. En
écriture, il faut deux cycles : lors du premier cycle on écrit dans le banc associé au port
et lors du deuxième cycle on écrit dans l'autre banc (on croise les écritures). Cette
méthode ne peut être généralisée à N ports, même si on dispose de N copies de la
mémoire !
22.Mémoire Multiports de Tanaka

Une Architecture de mémoire multiports complète a été proposée par Y. Tanaka en 1988. Elle
utilise la mémorisation des informations sous forme de parité. Considérons le bit i du mot j de
la mémoire :

 Pour lire : on calcule sa parité sur les bancs composant une ligne. Dans l'exemple ci-
dessus, si on lit le le bit i du mot j on trouve 1 comme parité : le bit i du mot j vaut
donc 1. Chaque ligne de bancs correspond à un port de lecture.
 Pour écrire : on lit le banc associé au port d'écriture et on inverse le bit s'il est différent
du bit à écrire (cette opération est effectuée simultanément sur tous les bancs d'une
même colonne) ; le fait d'inverser un seul des bits i sur une ligne change la parité de la
ligne de bits i, donc le résultat de la prochaine lecture du bit i. Dans l'exemple ci-
dessus, si le port 2 veut écrire un 0 dans le bit i du mot j, il faut lire d'abord la parité et
comme elle est différente de 0 il écrit un 1 dans tous les bit i du mot j des bancs de la
colonne 2. Ainsi à la prochaine lecture du bit i du mot j sa parité vaudra 0.
L'inconvénient de cette architecture est qu'au lieu d'avoir un seul banc mémoire on doit en
utiliser N2 pour réaliser une mémoire à N ports de lecture et N ports d'écriture ; de plus, au
temps d'accès mémoire s'ajoute un temps en log N pour le calcul de la parité.

23.La Machine à Mémoire Multiports Série : M3S

La machine M3S est développée à l'Université de Toulouse par D. Litaize. Ce


Multiprocesseur à Mémoire Partagée utilise une mémoire multiports série. Les bancs mémoire
sont composés d'une RAM classique qui peut être lue dans des registres à décalage. Ces
registres à décalage sont reliés via un Crossbar local à des drivers série. De l'autre côté, les
modules processeurs sont composés d'un processeur standard du commerce (par exemple le
Motorola 88 000) et de son cache.

La machine est organisée autour d'un réseau d'interconnexion de type point à point qui relie
les drivers des bancs mémoire aux caches des modules processeurs. On utilise des liens série à
très haut débit réalisés en technologie AsGa, seule compatible avec les débits nécessités par
les demandes émanant des caches. Par exemple, pour une machine M3S ayant 64 PEs et 64
bancs mémoire et avec un débit sur les liaisons série de 1 Giga bit/s, la bande passante du
réseau est de 8000 Mega Octets/s (contre quelques centaines de Mega Octets/s pour les
systèmes actuels à base de bus).

Un dispositif de cohérence des données dans les caches de type répertoire est utilisé. Le
répertoire est centralisé au niveau de la mémoire mais distribué sur les modules : chaque
module mémoire ne gère que les problèmes de cohérence des blocs qui le concernent. Les
messages sont émis via une liaison série de cohérence qui autorise la diffusion des messages.
La gestion distribuée de la cohérence des caches n'est pas génératrice de goulet d'étranglement
et préserve la structure modulaire de M3S.
24.Le Phénomène des Hotspots

Le hotspot est un phénomène qui se superpose au phénomène de la contention mémoire. Il se


produit lorsqu'on utilise des variables de synchronisation qui sont beaucoup plus utilisées que
la moyenne des autres variables de l'application. Il se produit alors un congestion des accès
sur le banc mémoire contenant la variable paratagée qui diminue notablement les
performances du réseau d'interconnexion.

En phase de fonctionnement normal, c'est-à-dire en cas de trafic mémoire équiréparti, la


courbe de réponse du réseau (taux de contention mémoire exprimé en nombre de cycles du
réseau) est fonction du taux de demandes d'accès mémoire faites aux réseau (taux de
demandes par cycle du réseau). Ce cas de figure correspond à 0 % de hotspot. On voit que
tout se passe bien si la charge reste inférieure à 80 % de la capacité de transfert théorique du
réseau.

Si dans une application, une variable partagée a un taux d'utilisation de 1% des accès alors la
courbe de réponse globale du réseau se dégrade. Si cette variable a un taux d'utilisation de 4%
des accès mémoire, alors la courbe de réponse globale du réseau devient vite inopérante !

Encore plus grave, si on utilise un réseau de type Multi-étages bloquant (par exemple un
Oméga) alors la contention qui se produit sur le port mémoire bloque aussi les ports amont du
switch associé à ce port ; à son tour ces points chauds bloquent les switches en amont ... ainsi,
peu à peu une grande partie du réseau est bloquée, ce qui altère des requêtes qui n'ont pourtant
rien à voir avec le point chaud initial : c'est le phénomène de saturation en arbre. Dans le
NYU, la gestion factorisée des Fetch&Add au niveau des switches permet de diminuer les
accès effectifs aux données de synchronisation et de limiter très notablement l'effet de
hotspot.

25.Implémentation du Fetch & Add dans le Réseau d'interconnexion de


NYU

Le New York Ultracomputer (NYU) a été développé par A. Gottlieb au Courant Institute de
New York University. C'est un Multiprocesseur MIMD possédant jusqu'à 4096 bancs
mémoire et jusqu'à 4096 processeurs organisés autour d'un réseau d'interconnexion de type
Multi-étages Oméga (a).

Les bancs mémoire possèdent chacun un contrôleur (b) qui outre l'arbitrage des accès au banc
réalise l'instruction Fetch&Add (V, e). Dans ce Multiprocesseur, l'instruction Fetch&Add est
aussi implémentée au niveau des switches du réseau d'interconnexion. Les switches sont
intelligents : ils peuvent concentrer les Fetch&Add portant sur une même variable de
synchronisation. Par exemple (c), si un switch du réseau reçoit en même temps un
Fetch&Add(V, e0) et un Fetch&Add(V, e2) il mémorise e0 dans une petite mémoire locale et
fait suivre un Fetch&Add(V, e0 + e2) qui concentre les deux demandes. Dès qu'il reçoit la
réponse old-V, il éclate correctement les deux réponses : old-V et old-V+e0 vers ses deux
demandeurs. Cette concentration des demandes pour les variables de synchronisation diminue
énormément les conflits mémoire.
26.La Gestion des Processus dans les Supercalculateurs

La classe des Supercalculateurs est définie par les Multiprocesseurs MIMD et les machines
Vectorielles. Ces deux types de machines ont une mémoire centrale commune. Dans les
Supercalculateurs, il arrive très souvent que le modèle de base (MIMD ou SIMD) soit enrichi
d'un niveau hiérarchique supplémentaire pour augmenter le parallélisme ; généralement ce
niveau supérieur est de type MIMD : cela donne lieu à des machines M MIMD ou bien M
SIMD dont l'architecture est très complexe.

 Pour qu'une machine exécute des tâches en parallèle, il faut d'abord extraire ces tâches
pour les porter à la connaissance de la machine : c'est le Partitionnement. Cela peut se
faire de manière manuelle (c'est l'utilisateur qui écrit dans le code de l'application où
commencent et où finissent les tâches) ou bien de manière automatique grâce à un
outil logiciel (compilateurs spéciaux ...) ou directement dans le matériel (modèles
Data/Demand driven...). Dans les Supercalculateurs de type MIMD on laisse
généralement le travail de Partitionnement au programmeur ; dans la plupart des cas le
langage de description des tâches est généralement un Fortran étendu à des
déclarations de tâches. Dans les Supercalculateurs de type SIMD le langage de
description des tâches est généralement un Fortran qui possède des outils spéciaux de
vectorisation, capables d'extraire automatiquement le parallélisme pipe-line.
 Tous le Supercalculateurs ayant au moins un étage MIMD ont des problèmes de
synchronisation. Ces problèmes se produisent quand plusieurs processus ont besoin de
travailler sur une même variable en mémoire commune. Plusieurs types de
mécanismes ont été introduits pour gérer la synchronisation. Ces mécanismes sont
hérités des mécanismes déjà définis pour les Operating Systems multitâches
fonctionnant sur des machines séquentielles classiques.
 Quand on a partitionné une application en N processus, on dispose généralement de M
processeurs pour l'exécuter, tel que M < N. Cela pose alors le problème du scheduling,
c'est-à-dire de l'affectation temporaire de processus à des processeurs : à tout moment,
il faut qu'il y ait un maximum de processeurs au travail, mais en même temps, il ne
peut y avoir qu'un seul processus actif par processeur. De multiples algorithmes
d'allocation ont été étudiés pour les Supercalculateurs. Certains sont statiques : c'est le
compilateur qui effectue l'allocation Processus/Processeur une fois pour toute ;
d'autres sont dynamiques : on mesure la charge des processeurs au moment de
l'exécution pour décider de l'allocation Processus/Processeur.
27.Le Partitionnement dans HEP

Le Heterogeneous Element Processor (HEP) de la firme Denelcor Inc. a été construit en 1982
mais n'a pas été commercialisé. Cette machine parallèle possède cependant des
caractéristiques intéressantes au niveau du traitement du parallélisme. Il s'agit d'une machine
de la classe MIMD hiérarchisée : les applications (ou Jobs) sont décomposées par le
programmeur en tâches qui sont elles-mêmes décomposées par le programmeur en processus.
Les tâches et les processus sont exécutés en parallèle. Les instructions des processus sont
exécutées en mode séquentiel. Le grain de parallélisme est donc assez fort. Une tâche est
affectée dans son entier à un processeur. Un processeur peut supporter plusieurs tâches, mais
il est limité à un nombre maximal de 50 processus. Avec ses 16 processeurs, HEP peut donc
supporter des applications parallèles ayant jusqu'à 800 processus.

1. Mémoire de programmes distribuée :


La première originalité de la machine HEP est que la mémoire centrale n'est partagée que
pour les données. Les programmes sont placés dans les processeurs. Ainsi, on diminue
grandement la contention due au Fetch des instructions. La machine est organisée autour d'un
réseau à commutation de paquets Multi-étages qui interconnecte 128 bancs mémoire à 16
processeurs.

2.Scheduling tournant :

La deuxième originalité de la machine HEP est que pour réduire les problèmes de blocage dûs
à la contention sur les accès à la mémoire de données, on déschédule systématiquement le
processus pendant son accès à la mémoire de données. Pour cela, on dispose de plusieurs files
d'attentes : file des accès à la mémoire, file des tâches et pour chaque tâche sa file des
processus. En faisant "tourner" les processus dans ces files, on arrive à alimenter un certain
nombre d'unités fonctionnelles qui travaillent en mode Pipe-line.

28.Auto Scheduling dans HEP

Dans la machine HEP, on dispose d'un mécanisme d'équilibrage dynamique de la charge qui
utilise des variables de synchronisation. Pour les variables déclarées "variables de
synchronisation" (elles sont annotées par un $ dans le programme ; exemple : $toto) est
associé un bit noté F/E qui indique si le mot est plein (F/E =1) ou vide (F/E =0). Les lectures
et les écritures de ces variables testent au préalable le bit F/E : en cas de lecture, il faut que le
bit F/E soit égal à 1 pour que la lecture réussisse, sinon le processus qui lit est bloqué ; en cas
d'écriture, il faut que le bit F/E soit égal à 0 pour que l'écriture réussisse, sinon le processus
qui écrit est bloqué. On dispose aussi de Get ($toto) qui consulte la valeur de la variable de
synchronisation $toto de manière non destructive (F/E non modifié) et non bloquante
(indépendamment de l'état du bit F/E de $toto).

Exemple :

For i := 1 to 100 do a(i) := b(i) + c(i) ;

Les tableaux a, b, c qui ont 100 éléments sont des variables globales. On veut générer 10
processus qui vont se partager le travail des 100 itérations. Pour cela, on crée 10 processus
identiques qui se synchronisent sur une variable $n qui génère les indices : l'instruction i:= $n
n'est exécutée que si le F/E de $n est = 1 ; alors le F/E <--0 ce qui bloque tout accès à $n par
les autres processus ; l'instruction $n := i+1 incrémente le générateur $n et son F/E passe en
même temps à 1 ce qui autorise les autres processus à accéder au nouveau $n.

Main Synchro ($n);


Begin
purge $n; -- F/E de $n <-- 0
$n := 1; -- ssi = Empty
For i := 1 to 10 do Launch Pi;
-- Attente active sur $n pour la suite
While get ($n) < 100 do;
...
End

Process Pi
Local(i); Global(a, b, c); Synchro($n) ;
Begin
i := $n; -- ssi = Full
$n := i + 1; -- ssi = Empty
If i < 100 Then a(i) := b(i) + c(i)
...
End
29.Architecture de HEP
Le Multiprocesseur HEP a une mémoire partagée organisée en 128 bancs. Il comporte 16
processeurs pipe-line qui sont interconnectés aux bancs mémoire via un réseau multi-étages.
Dans la machine HEP, les processus sont déschédulés lorsqu'ils effectuent des accès mémoire.
La commutation de contexte est extrêmement rapide. Ceci permet d'alimenter les Unités
Fonctionnelles Pipe-line flottantes avec des instructions provenant de plusieurs processus.
Ainsi les instructions sont indépendantes les unes des autres et ne limitent pas le pipe-line.
Pour s'y retrouver, chaque instruction possède un champ TAG qui contient le numéro de son
processus : il permet d'aiguiller les résultats correctement.

30.C.mmp

Le C.mmp (Computer with multiple mini-processors) est un Multiprocesseur à Mémoire


Partagée développé à l'Université de Carnegie Mellon en 1971. Il est composé de 16 PEs à
base de PDP-11 (modèles 20 et 40) connectés par un réseau dynamique de type Crossbar à 16
bancs mémoire de 2 Mo chacun. Chaque PE dispose d'une mémoire locale de 8 ko, d'un cache
et d'un dispositif d'E/S via un Unibus.

Le temps de cycle mémoire d'un PDP-11/40 est de 2,5 ms. Le temps de traversée du Crossbar
(translation d'adresse, traversée des switches et des câbles) est de 1 ms seulement ce qui est
largement compatible avec le cycle du PDP-11.

Avec le Crossbar on peut effectuer 16 accès mémoire simultanément à condition que les
adresses soient différentes. Evidemment, le nombre de PEs est faible et cette architecture n'est
pas généralisable en raison de la montée quadratique du nombre de switches nécessaires à la
construction des Crossbars.

Le C.mmp est un des premiers Multiprocesseurs pour lequel on a développé un Operating


System parallèle spécifique : Hydra. Des applications ont été écrites en Hydra et des mesures
ont montré que certaines d'entre-elles avaient un gain proche du facteur linéaire

(soit 16 fois plus rapide). Ceci est surtout dû à l'utilisation du Crossbar qui n'introduit pas de
contention et qui est extrêmement rapide à traverser.
Le système Hydra permet de détecter les pannes sur les PEs grâce à un bus général qui sert
aux interruptions externes et qui a permis aussi d'implémenter un mécanisme de Watchdog :
chaque PE doit positionner un bit au moins toutes les quatre secondes sinon il est suspecté de
mal fonctionner ; cependant cette technique ne permet pas de détecter les pannes transitoires.

Les essais sur le C.mmp et sur Hydra ont montré que la machine était utilisable mais on lui a
fait plusieurs reproches quant à la facilité d'utilisation :

 Les PDP-11 n'offrent qu'un très petit espace d'adressage (64 ko) et la gestion de
l'espace en mémoire partagée est difficile.
 Le plus grave problème a été le debug et la trace des processus en cours d'exécution.
Le manque d'outils s'est fait cruellement sentir

Le projet C.mmp a été suivi par le projet Cm*, un Multiprocesseur à bus hiérarchisés, décrit
dans le chapitre sur les réseaux d'interconnexion.

31.L'Architecture Butterfly de BBN

L'originalité de l'Architecture Butterfly de BBN est qu'elle peut être vue soit comme une
machine à mémoire distribuée soit comme une machine à mémoire partagée :

 En tant que machine à mémoire distribuée, elle comprend jusqu'a 256 PEs qui sont
composés d'un processeur Motorola 68 000 (ou 68 020 avec coprocesseur 68 881),
d'une mémoire locale (de 1 à 4 Mo) et d'un Processeur Contrôleur de Noeud (PCN)
qui utilise le réseau dynamique de type Oméga (appelé Butterfly par BBN) pour
effectuer la messagerie.
 En tant que machine à mémoire partagée, elle est composée de 256 PEs et de 256
Bancs mémoire reliés par un réseau d'interconnexion dynamique de type Oméga
construit à base de switches 4 ¥ 4.
Le PCN utilise un MMU (Memory Management Unit) pour traduire l'adresse virtuelle du
68000 en adresse réelle, de manière à ce que les bancs représentent un espace global contigu.
Le temps d'accès mémoire en mode local est de 2 ms et en mode global via le réseau Oméga il
est de 6 ms (en l'absence de contention) ce qui est très rapide. Le réseau Oméga de la
Butterfly de BBN n'est pas combinant comme celui du NYU ou du RP3 et si plusieurs
demandes entrent en collision sur un switch, toutes sont "tuées" sauf une.

Le réseau est composé de 4 étages maximum de switches 4 x 4 réalisés sous forme d'un VLSI
spécifique. Avec 8 de ces VLSI sur une carte on peut réaliser une machine 16 PEs x 16 BMs
(a). Le débit maximal sur un lien du réseau est de 32 Mbit/s. Une configuration à 256 PEs
travaillant sur un produit de matrices a produit 115 MIPS soit un gain de 230 (efficacité de
91%) par rapport à un seul PE (0,5 MIPS).

32.La Machine RP3 d'IBM

Le Research Parallel Processor Project (RP3) de G.S. Almasi a été mis en place au centre de
recherches Watson d'IBM, en collaboration avec le projet NYU, pour étudier le parallélisme
massif. L'architecture de RP3 est donc très proche de celle de NYU : il s'agit d'un
Multiprocesseur de type M MIMD organisé physiquement en 8 Armoires (a) contenant 64
PEs et 16 Processeurs d'E/S (b). Les PEs du RP3 (c) disposent chacun d'un cache de données
géré par un MMU. Le CPU est réalisé à base du Microprocesseur Risc d'IBM, le ROMP qui
équipe les PC-RT. Il est associé à un coprocesseur flottant via un bus interne.
La puissance théorique maximum du RP3 est de 1300 MIPS et de 800 Mflops. La principale
originalité du RP3 réside dans son réseau d'interconnexion qui est double :

 Le premier réseau est réalisé dans une technologie extrêmement rapide : en ECL (avec
des boîtiers TMC de refroidissement comme dans l'IBM 3090). Ce réseau est utilisé
uniquement pour faire des lectures et des écritures en mémoire. C'est un réseau de type
Multi-étages Oméga organisé en 4 étages de switches 4 x 4 pour une configuration de
512 PEs. Son horloge de base a un Tc = 20 ns ; un message prend deux cycles pour
traverser un switch ; le temps de traversée du réseau est donc de 2 x 20 ns x (étages +
remplissage du pipe-line message) ; par exemple, pour un message de 8 octets, il ne
faut que 320 ns pour traverser ce réseau ! C'est 6 fois plus rapide que dans le réseau
Oméga du Butterfly de BBN.
 Le deuxième réseau est réalisé en technologie Cmos, beaucoup moins rapide que
l'ECL, mais il effectue les opérations de combinaisons des requêtes multiples et le
Fetch&Add comme dans le NYU. Les concepteurs auraient préféré un seul réseau
combinant en ECL, mais les contraintes technologiques ne le permettaient pas en
1987. Ce réseau est aussi de type Multi-étages Oméga à 64 ports. Il est composé de 6
étages de switches 2 x 2 ; un étage supplémentaire multiplexe les ports vers les 512
PEs. Son chemin de données est plus large que celui du premier réseau ce qui
compense la lenteur de la technologie mais en cas de contention les opérations de
combinaison des requêtes ralentissent quand même sérieusement le temps de
traversée.
33.Gestion Mémoire dans la Machine RP3

La gestion mémoire du RP3 a été conçue pour offrir une très grande flexibilité d'approche
entre les architectures à mémoire partagée et les architectures à mémoire distribuée. On peut
choisir entre un mode d'adressage purement local et un mode d'adressage où l'espace
d'adressage est distribué sur les PES. Le partitionnement de la mémoire entre zones locales et
globales est dynamique : il est effectué à l'exécution par un mécanisme sophistiqué de
transformation d'adresses qui permet de faire varier graduellement la part de mémoire locale
et la part de mémoire globale. Ainsi, le RP3 peut fonctionner en Multiprocesseur à Mémoire
Partagée ou bien en Machine à Passage de Messages, mais aussi en mode mixte.

Le RP3 possède 4 Giga octets de mémoire physique (32 bits d'adresse physique) répartis en
512 modules de 2 Mega Octets. Son espace d'adressage logique est donc égal à son espace
d'adressage physique. La transformation d'adresse logique en adresse physique absolue ne
porte que sur le caractère local/global des pages. Le RP3 offre en plus un mécanisme
d'entrelaçage matériel des adresses sur les PEs qui peut varier entre 0 (pas d'entrelaçage ) et 9
bits (pour une configuration maximale de 512 PEs). Le degré d'entrelaçage étant fixé pour
chaque page, les mots consécutifs sont répartis automatiquement sur les PEs pour que les
accès parallèles à cette page utilisent bien la décomposition en modules.

Le RP3 utilise des caches dont la cohérence est gérée en logiciel avec une assistance
matérielle. Les données cacheables pendant un intervalle de temps caractéristique sont
marquées par le compilateur. Les mécanismes matériels permettent de gérer les marques et les
franchissement de point de synchronisation.

Les applications qui ont été testées sur le RP3 sont essentiellement de type SPMD (Single
Program Multiple Data). Il s'agit d'un mode de fonctionnement intermédiaire entre le mode
MIMD et le mode SIMD où on exécute le même code de processus sur des PEs différents,
avec des données différentes et de manière asynchrone.

Retour

Вам также может понравиться