Vous êtes sur la page 1sur 97

Multicoeurs, Hyperthreading, paralllisme : qu'estce que c'est ?

Par Guy Grave (Mewtow)

www.siteduzero.com

Licence Creative Commons BY-NC-ND 2.0 Dernire mise jour le 29/09/2012

2/97

Sommaire
Sommaire ........................................................................................................................................... 2 Lire aussi ............................................................................................................................................ 2 Multicoeurs, Hyperthreading, paralllisme : qu'est-ce que c'est ? ...................................................... 4 Partie 1 : Le paralllisme : un peu de thorie ...................................................................................... 5
Les diffrentes architectures parallles ............................................................................................................................ 5
Les diffrents types de paralllisme ............................................................................................................................................................................ 5 Paralllisme de Threads .............................................................................................................................................................................................. 5 Paralllisme d'instruction ............................................................................................................................................................................................. 5 Paralllisme de donnes ............................................................................................................................................................................................. 6 Taxonomie de Flynn .................................................................................................................................................................................................... 6 SISD ............................................................................................................................................................................................................................ 6 SIMD ........................................................................................................................................................................................................................... 6 MISD ........................................................................................................................................................................................................................... 7 MIMD ........................................................................................................................................................................................................................... 8 Rsum ....................................................................................................................................................................................................................... 8 Partage de la mmoire ................................................................................................................................................................................................ 8 SASM .......................................................................................................................................................................................................................... 8 DADM .......................................................................................................................................................................................................................... 9 SADM ........................................................................................................................................................................................................................ 10

Les limites thoriques ...................................................................................................................................................... 11


Loi d'Amdhal .............................................................................................................................................................................................................. 12 Hypothses de base .................................................................................................................................................................................................. 12 Gain ........................................................................................................................................................................................................................... 13 Drivation de la Loi d'Amdhal .................................................................................................................................................................................... 13 Ce que nous dit la loi d'Amdhal ................................................................................................................................................................................. 14 Paralllisons mieux ! ................................................................................................................................................................................................. 14 Nombre de processeurs ............................................................................................................................................................................................ 15 Code srie ................................................................................................................................................................................................................. 16 Loi de Gustafson ....................................................................................................................................................................................................... 16 Paralllisme de donnes ........................................................................................................................................................................................... 16 Hypothses de base .................................................................................................................................................................................................. 17 Gain ........................................................................................................................................................................................................................... 17 Que nous apprend Gustafson ? ................................................................................................................................................................................ 18

Partie 2 : Multi-processeurs, Multicoeurs et Hyperthreading ............................................................. 18


Hyperthreading et compagnie ......................................................................................................................................... 19
Du paralllisme avec un seul processeur ................................................................................................................................................................. 19 Le pipeline fait des bulles ! ........................................................................................................................................................................................ 19 Types de super-threading matriel explicite .............................................................................................................................................................. 20 Processeurs superscalaires ...................................................................................................................................................................................... 25 Et dans le processeur ? ............................................................................................................................................................................................ 26 Cycle Fetch / Excution ............................................................................................................................................................................................. 26 Niveau circuits ........................................................................................................................................................................................................... 27

Processeurs multicoeurs ................................................................................................................................................. 30


Processeurs multicurs ........................................................................................................................................................................................... 31 Le multicurs, c'est quoi ? ....................................................................................................................................................................................... 31 Multicurs asymtrique ............................................................................................................................................................................................ 31 Cluster Multithreding ................................................................................................................................................................................................. 32 Le partage des caches .............................................................................................................................................................................................. 33 Caches ddis versus caches partags ................................................................................................................................................................... 33 La ralit .................................................................................................................................................................................................................... 35 Communication inter-processeurs ............................................................................................................................................................................ 36 L'exemple du x86 ...................................................................................................................................................................................................... 37

Partie 3 : Le partage de la mmoire .................................................................................................. 38


Cohrence mmoire ........................................................................................................................................................ 39
La cohrence : c'est quoi ? ....................................................................................................................................................................................... 39 Mcanismes de base ................................................................................................................................................................................................ 40 Politique d'criture du cache ..................................................................................................................................................................................... 40 Caches partags ....................................................................................................................................................................................................... 43 Direct Memory Acces ................................................................................................................................................................................................ 44 Conclusion ................................................................................................................................................................................................................ 44 Protocoles de cohrence des caches ....................................................................................................................................................................... 44 Protocole MSI ............................................................................................................................................................................................................ 44 Protocole MESI ......................................................................................................................................................................................................... 46 Protocole MOESI ....................................................................................................................................................................................................... 46 Implmentation .......................................................................................................................................................................................................... 47 Directory Protocol ...................................................................................................................................................................................................... 47 Snooping protocols ................................................................................................................................................................................................... 47 Write Invalidation et Write Broadcasting ................................................................................................................................................................... 48

Consistance mmoire ..................................................................................................................................................... 50


Problmes ................................................................................................................................................................................................................. Accs simultans ...................................................................................................................................................................................................... Memory Ordering ...................................................................................................................................................................................................... Modles de consistance ............................................................................................................................................................................................ Squential Consistency ............................................................................................................................................................................................. 50 50 51 51 51

www.siteduzero.com

Sommaire

3/97

Relaxed Consistency ................................................................................................................................................................................................. 51 Exemples .................................................................................................................................................................................................................. 52 Fences et Memory Barrier ......................................................................................................................................................................................... 53 Full Fence .................................................................................................................................................................................................................. 53 Load/Store Fences .................................................................................................................................................................................................... 54 Et pour le programmeur ? ......................................................................................................................................................................................... 54

Synchronisation entre Threads ....................................................................................................................................... 56


Sections Critiques ..................................................................................................................................................................................................... 56 Exclusion mutuelle .................................................................................................................................................................................................... 56 Instructions atomiques .............................................................................................................................................................................................. 57 Problmes avec les verrous d'exclusion mutuelle .................................................................................................................................................... 58 Mmoire Transactionelle Matrielle ........................................................................................................................................................................... 58 Instructions LL/SC ..................................................................................................................................................................................................... 58 Versions volues ..................................................................................................................................................................................................... 59 Speculative Lock Elision ........................................................................................................................................................................................... 59 L'exemple avec le x86 ............................................................................................................................................................................................... 60

Partage des ressources .................................................................................................................................................. 62


Bus mmoire ............................................................................................................................................................................................................. 62 Bus Contention .......................................................................................................................................................................................................... 62 Prefetcher Contention ............................................................................................................................................................................................... 62 Mmoire cache .......................................................................................................................................................................................................... 63 Cache Contention ...................................................................................................................................................................................................... 63 False Cache Sharing ................................................................................................................................................................................................. 64 Solutions ................................................................................................................................................................................................................... 64

Partie 4 : Architectures NUMA et distribues .................................................................................... 65


Architectures Distribues ................................................................................................................................................ 65
C'est quoi ? ............................................................................................................................................................................................................... Diffrents types ......................................................................................................................................................................................................... Des besoins en terme de suret ............................................................................................................................................................................... Communication inter-processeur .............................................................................................................................................................................. Message Passing ...................................................................................................................................................................................................... Les rseaux inter-processeurs .................................................................................................................................................................................. C'est quoi ? ............................................................................................................................................................................................................... De quoi NUMA est-il ne nom ? .................................................................................................................................................................................. Noeuds ...................................................................................................................................................................................................................... Cohrence des caches, le retour ! ............................................................................................................................................................................ Directory Protocol ...................................................................................................................................................................................................... Organisation de base ................................................................................................................................................................................................ Directory .................................................................................................................................................................................................................... 65 66 66 67 67 67 70 70 70 71 71 71 72

NUMA .............................................................................................................................................................................. 69

Partie 5 : Le paralllisme de donnes ............................................................................................... 74


Instructions SIMD ............................................................................................................................................................ 74
Instructions SIMD ...................................................................................................................................................................................................... Arithmtique sature ................................................................................................................................................................................................. Exemples : MMX, SSE et AVX .................................................................................................................................................................................. MMX .......................................................................................................................................................................................................................... SSE ........................................................................................................................................................................................................................... AVX ........................................................................................................................................................................................................................... 74 75 76 76 78 80

Les processeurs vectoriels .............................................................................................................................................. 80


Architectures ............................................................................................................................................................................................................. 81 Processeur vectoriels Memory-Memory .................................................................................................................................................................... 81 Processeurs vectoriels Load-Store ........................................................................................................................................................................... 81 Pipelining ................................................................................................................................................................................................................... 82 Startup et Dead Time ................................................................................................................................................................................................ 83 Solutions ................................................................................................................................................................................................................... 84 Chaining .................................................................................................................................................................................................................... 85 Multiple Lanes ........................................................................................................................................................................................................... 85 Accs mmoires ........................................................................................................................................................................................................ 85 Pipelining des accs mmoires ................................................................................................................................................................................. 86 Patterns d'accs mmoire ......................................................................................................................................................................................... 86 Quelques registres utiles ........................................................................................................................................................................................... 88 Vector Length Register .............................................................................................................................................................................................. 88 Vector Mask Register ................................................................................................................................................................................................ 89

GPGPU et Streams Processors ...................................................................................................................................... 89


Architecture d'un GPU ............................................................................................................................................................................................... Vieux GPUs ............................................................................................................................................................................................................... Stream Processors .................................................................................................................................................................................................... Hirarchie mmoire ................................................................................................................................................................................................... Pas de cache de donnes ......................................................................................................................................................................................... Register Files ............................................................................................................................................................................................................ Du cot de la RAM .................................................................................................................................................................................................... Une histoire de latence ............................................................................................................................................................................................. Jeu d'instruction ........................................................................................................................................................................................................ Plusieurs ALU ............................................................................................................................................................................................................ Prdication ................................................................................................................................................................................................................ 90 90 90 91 92 92 93 94 94 95 95

www.siteduzero.com

Sommaire

4/97

Multicoeurs, Hyperthreading, paralllisme : qu'est-ce que c'est ?


Par Guy Grave (Mewtow)

Mise jour : 26/09/2012 Difficult : Facile Dure d'tude : 1 mois, 3 jours, 3 heures, 7 minutes 2 020 visites depuis 7 jours, class 69/792 De nos jours, avec lavnement des architectures multicurs, de nombreux programmeurs cherchent utiliser au mieux la puissance fournie par les processeurs modernes. En effet, il n'est pas rare de possder des processeurs contenant plusieurs curs. Cela fait quelques annes que de tels processeurs ont ts mis sur le march et sont accessibles tout un chacun moyennant une certaine somme d'argent (trs souvent trop assez consquente). L'utilit de tels processeurs est trs simple : la performance ! De tels processeurs permettent de faire excuter des instructions indpendantes dans des processeurs spars. Cette solution qui consiste rpartir des calculs sur plusieurs processeurs s'appelle le paralllisme. Mais les processeurs multicurs ne sont pas les seuls processeurs permettant de faire ceci : de nombreux autres processeurs et architectures permettent dexcuter plusieurs calculs simultanment. Entre les ordinateurs embarquant plusieurs processeurs, les architectures dataflow , les processeurs vectoriels et autres, il y a de quoi tre perdu assez rapidement. Mais rassurez-vous : ce tutoriel est l ! Grce ce tutoriel, vous aurez un aperu des diffrentes architectures parallles et vous verrez leurs forces et leurs faiblesses. Nous parlerons aussi bien du fonctionnement de ces processeurs et ordinateurs que des interactions entre matriel et logiciel : prparez-vous, le monde du paralllisme vous attend.

www.siteduzero.com

Multicoeurs, Hyperthreading, paralllisme : qu'est-ce que c'est ?

5/97

Partie 1 : Le paralllisme : un peu de thorie


Commenons par les bases et expliquons ce qu'est le paralllisme, pour commencer. Dans ce chapitre, nous verrons un peu quel est le but des diffrentes architectures que nous verrons dans ce tutoriel.

Les diffrentes architectures parallles


Il existe diffrents types d'ordinateurs parallles, tous plus bizarres que les autres. L'imagination des chercheurs en architectures de ordinateurs a en effet t mise rude preuve devant le dfi que nous imposait la cration des architectures parallles et de nombreuses architectures ont ts inventes pour rpondre ce besoin. Si on devait se lancer sans chercher organiser le tout, on serait rapidement perdu. Pour se faciliter la tche, diverses classifications ont t inventes pour mettre un peu d'ordre dans tout ce foutoir.

Les diffrents types de paralllisme


Il faut savoir qu'il existe diffrentes formes de paralllisme, qui ne cherchent pas parallliser la mme chose.

Paralllisme de Threads
La premire solution, la plus vidente, consiste simplement excuter des calculs indpendants en parallle. Pour cela, il suffit de dcouper notre programme en plusieurs sous-programmes indpendants qu'on peut faire xecuter en parallle. Ces sousprogrammes indpendants sont ce qu'on appelle des Threads. Attention : j'utilise le terme thread non dans le sens thread logiciel, mais dans un sens plus gnral, largement utilis dans la littrature sur l'architecture des ordinateurs parallles. Quand je parlerai de threads, je parlerai de threads matriels . Je prfre prvenir tout de suite, vu que dans la suite du tutoriel, j'utiliserai le terme thread de cette faon, et particulirement dans les chapitres sur les processeurs multithreads un peu spciaux. Pour le moment, considrez qu'un thread est simplement un morceau de programme, constitu d'une suite d'instructions excuter en srie, et qui devra utiliser le processeur.

Il suffit de faire excuter chaque Thread sur un processeur spar pour pouvoir parallliser le tout. Les architectures permettant dexcuter des threads en parallle sont donc des architectures multiprocesseurs ou multicurs, ainsi que quelques autres processeurs un peu spciaux. Avec ce genre de paralllisme, le dcoupage d'un programme en threads est avant tout un problme logiciel. Ce dcoupage est donc dans ce cas du ressort du compilateur ou du programmeur : c'est eux de modifier le programme pour le parallliser. Nos langages de programmation disposent souvent de mcanismes permettant de dcouper notre programmes en threads logiciels, excutables en parallle si le matriel le permet. Dans certains cas, le compilateur peut s'en charger tout seul, bien que cela soit plus rare. Et enfin, plus tonnant, certains processeurs sont capables de dcouper un programme lexcution, ventuellement grce des indications fournies par le programme lui-mme ! On peut ainsi, partir d'un programme unique non-dcoup en threads, utiliser plusieurs curs ou processeurs sans problmes ! Le dcoupage en thread se fait alors lexcution, et ce de faon optimale en fonction du nombre de processeurs. Mais cela est tout de mme assez rare, mme si cela a dj t tent : on reviendra dessus quand je parlerai des architectures EDGE et du spculative multithreading dans ce tutoriel.

Paralllisme d'instruction
Mais certains chercheurs se sont dit que penser hors du cadre ne faisait pas de mal : ceux-ci ont considr que parallliser un programme pouvait tre un problme matriel, et que crer des architectures rellement conue pour excuter des instructions en parallle serait une grande avance. Crer des architectures spciales serait donc un gros plus qui permettrait non pas de dcouper des programmes entiers en morceaux qu'on pourrait excuter en parallle, mais permettrait de parallliser directement un programme au niveau de ses instructions ! Nos chercheurs ont cherch (quoi de plus normal...) un moyen de crer des ordinateurs de ce genre, sans trop d'aide venant du logiciel. Les architectures dataflow furent une de ces rponses. Mais nous ne parlerons pas de ces architectures spciales dans ce tutoriel. Les techniques apprises sur les ordinateurs dataflow ont malgr tout t reprises dans les processeurs modernes, qui incorporent des techniques comme l'Out Of Order et autres renommages de registres, qui sont des descendants directs de nos architectures dataflow . Ces techniques permettent un processeur seul dexcuter plusieurs instructions simultanment, la condition que ces instructions appartiennent un seul et unique programme. Les concepteurs de processeurs on en effet invent des tas de techniques permettant notre processeur de ne pas excuter des instructions dans l'ordre prvu par le programmeur : le pipeline, l'Out Of Order, la cration de processeurs superscalaires, etc. Sachez juste que cette forme de paralllisme n'est pas au programme de ce tutoriel : le tutoriel Fonctionnement d'un ordinateur depuis zro se charge dj d'expliquer certaines de ces techniques. Allez donc lire la partie 7 si vous vous en sentez le courage et que vous en avez les

www.siteduzero.com

Partie 1 : Le paralllisme : un peu de thorie


comptences !

6/97

Paralllisme de donnes
Autre solution, excuter le mme programme sur des donnes diffrentes et indpendantes. Cela permet donc de traiter N donnes sur N processeurs en mme temps. Pour rsumer : tous les processeurs excutent un seul et unique programme ou une suite d'instructions, mais chacun de ces processeurs va travailler sur une donne diffrente. Cette solution est celle qui est la moins limite, comme on le verra plus tard : il n'y a pas vraiment de limitations thoriques ce genre de paralllisme. Les processeurs pouvant faire ce genre de chose ne sont pas rares, bien au contraire : la quasi-totalit des processeurs est aujourd'hui de ce type. Plus prcisment, tous les processeurs Intel et AMD actuels, ainsi que leurs confrres de chez ARM, MIPS et VIA en sont capables. Le paralllisme de donne est aussi massivement utilis dans les cartes graphiques, qui sont des composants devant excuter les mmes instructions sur un grand nombre de donnes : chaque calcul sur un pixel est plus ou moins indpendant des transformations qu'on effectue sur ses voisins.

Taxonomie de Flynn
Maintenant qu'on connait un peu les diffrentes formes de paralllisme, on va maintenant voir les diffrents types d'architectures parallles. Dans les annes 1966, un scientifique amricain assez connu dans le milieu du hardware qui se nomme Flynn a class ces architectures en 4 grandes catgories : SISD, SIMD, MISD, et MIMD. Cette classification a remarquablement tenue le coup au fil du temps : on a beau eu inventer des tas d'architectures plus bizarres les unes que les autres, cette classification n'en est pas moins reste consistante et redoutablement fiable. Elle n'est pas parfaite, et certaines architectures ne rentrent pas vraiment dans les catgorie de la classification de Flynn, mais ce n'est qu'un dtail que je me permets de passer sous silence. Aussi, je me permets de vous parler de cette classification qui, bien que simpliste, est d'une grande aide au quotidien (ou presque).

SISD
Le premier type d'ordinateur correspond aux processeurs purement squentiels et incapables de toute forme de paralllisme. Ceux-ci vont excuter une instruction sur un seul ensemble de donnes, et rien d'autre. Ce sont les ordinateurs SISD (Single Instruction Single Data ).

SIMD
Vient ensuite le tour des architectures SIMD (Single Instruction Multiple Data ), qui sont des architectures permettant d'exploiter le paralllisme de donnes. Celles-ci peuvent excuter une instruction sur plusieurs donnes la fois.

www.siteduzero.com

Partie 1 : Le paralllisme : un peu de thorie

7/97

On verra ces types d'architectures en dtail dans notre tutoriel. V ous verrez par exemple que nos processeurs un tant soit peu rcents possdent des instructions machines capables d'effectuer des calculs sur plusieurs donnes diffrentes la fois, ce qui les classe d'office dans la catgorie SIMD. C'est aussi le cas de pas mal de cartes graphique, comme on le verra plus tard. Nous verrons aussi des architectures composes de plusieurs processeurs, sur lesquelles chaque processeur excute la mme instruction que ses collgues, mais sur des donnes diffrentes.

MISD
Vient ensuite le tour des ordinateurs MISD (Multiple Instruction Single Data ). Ceux-ci peuvent excuter des instructions diffrentes en parallle sur une donne identique.

Autant prvenir tout de suite : on ne verra aucun exemple de ce type dans le tutoriel. Cette catgorie darchitectures est vraiment trs rare. On peut citer comme exemples d'architectures MISD les architectures systoliques et cellulaires.

www.siteduzero.com

Partie 1 : Le paralllisme : un peu de thorie

8/97

MIMD
Et enfin, voici la classe la plus importante : les architectures MIMD (Multiple Instruction Multiple Data ). Celles-ci peuvent xecuter des instructions diffrentes sur des donnes diffrentes.

Nos processeurs multicurs et multiprocesseurs font partie de la catgorie MIMD. On peut prciser que cette catgorie MIMD peut tre dcoupe en deux sous-catgories. La premire est le Single Program Multiple Data , aussi appel SPMD : cela consiste excuter un seul programme sur plusieurs donnes la fois. Dit comme cela, on pourrait croire qu'il s'agit de SIMD. Mais il y a une diffrence : avec le SPMD, on peut parfaitement excuter des morceaux de programmes diffrents sur des donnes diffrents. Le SIMD force excuter la mme instruction sur plusieurs donnes. Vient ensuite le Multiple Program Multiple Data , qui consiste excuter des programmes en parallle sur des donnes diffrentes.

Rsum
Cette classification peut sembler simple, mais est tout de mme redoutablement efficace. Elle marche pour la grosse majorit des architectures parallles que nous allons voir dans ce tutoriel, les cas spciaux mritant un article eux tout seuls.

Peut traiter : Une instruction la fois Plusieurs instructions diffrentes la fois

Un seul ensemble de donnes la fois Plusieurs ensembles de donnes la fois SISD MISD SIMD MIMD

Partage de la mmoire
La capacit traiter des donnes ou des instructions diffrentes simultanment n'est pas la seule diffrence entre les architectures parallles : la faon dont les processeurs doivent se partager la mmoire est aussi trs importante. Suivant la faon dont est partage la mmoire, de nombreux problmes peuvent apparaitre. Aussi, il est important de savoir comment est partage la mmoire. V oyons un peu les diffrentes possibilits de partage de la mmoire.

SASM
Dans le premier cas, on se retrouve avec une seule et unique mmoire partage entre tous les processeurs. Le temps mit pour accder la mmoire est identique pour tous les processeurs : on parle alors d'Unifed Memory Access ou encore d'UMA.

www.siteduzero.com

Partie 1 : Le paralllisme : un peu de thorie

9/97

Avec ce genre d'architecture, rien nempche plusieurs processeurs de vouloir accder la mmoire en mme temps. Seul problme : la mmoire ne peut lire ou crire qu'une donne la fois (enfin sur des mmoires normales : on passe sous le tapis le cas des mmoires multiports). Il va donc falloir trouver des moyens pour arbitrer les accs la mmoire entre les processeurs pour viter les problmes. Par exemple, imaginez qu'un processeur aille modifier une donne qui est en cours de traitement par un autre processeur : on peut aller rapidement vers une belle catastrophe et il est facile de se retrouver avec des donnes crases, mises jour trop tt, ou pire. Pour viter ce genre de situations fcheuses, le matriel se charge alors de fournir quelques instructions pour faciliter la communication ou la synchronisation entre les diffrents morceaux de programmes (interruptions inter-processeurs, instructions machines atomiques permettant d'implmenter des Mutex, etc). La consquence, c'est que les couts de synchronisation et de communication entre les diffrents morceaux de programmes peut tre assez consquent et peut rduire les performances si on s'y prend assez mal : ce partage de la mmoire est assez embtant. Sans compter certaines contraintes concernant la hirarchie mmoire, qui jouent souvent de mauvais tours.

Et avec des caches ?


Il n'est pas rare que l'on amliore l'architecture SASM en rajoutant ce qu'on appelle des mmoires caches . Il s'agit simplement de mmoires intercales entre la RAM de l'ordinateur et le processeur. Ces mmoires caches sont plus petites, mais aussi plus rapides et permettent ainsi daccder plus rapidement la mmoire RAM de l'ordinateur. Il faut dire que la mmoire RAM est vraiment plus lente que le processeur, qui passe beaucoup de temps attendre la mmoire. C'est pour limiter la casse que l'on a invent ces fameux caches.

Cela permet dacclrer l'accs la mmoire partage, et permet ainsi des gains assez consquents. Malheureusement, ajouter un ou plusieurs caches sur des architectures SASM entraine l'apparition de quelques petits problmes lorsque deux processeurs doivent crire au mme endroit en mmoire. Imaginons que deux processeurs manipulent une donne : ceux-ci ont une copie de la donne dans leur cache qu'ils manipulent et modifient loisir. Si un processeur modifie cette copie de la donne et que celle-ci est enregistre dans son cache ou en mmoire, elle sera alors diffrente de celle prsente dans le cache de l'autre processeur. Ce qui fait qu'un processeur peut continuer manipuler une donne prime qui vient d'tre mise jour par l'autre processeur. Et a peut poser quelques problmes ! Un processeur doit toujours viter de se retrouver avec une donne prime et doit toujours avoir la valeur correcte dans ses caches : cela s'appelle la cohrence des caches . De manire gnrale, les mmoires caches posent de sacrs problmes sur pas mal d'architectures parallles. Autant prvenir tout de suite : je parlerai beaucoup des mmoires caches et des problmes qu'elles peuvent poser dans ce tutoriel. Mais on abordera le sujet en dtail plus tard.

DADM
www.siteduzero.com

Partie 1 : Le paralllisme : un peu de thorie

10/97

Viennent ensuite les architectures DADM, aussi appeles les architectures distribues. Avec elles, chaque processeur possde sa propre mmoire, sans aucune mmoire partage entre les processeurs. Tous les processeurs sont relis entre eux via un rseau local, qui leur sert changer des donnes ou des ordres un peu particuliers.

Les processeurs peuvent ainsi accder la mmoire d'un autre processeur via le rseau local : il leur suffit de faire une demande au processeur qui dtient la donne. Cette demande va traverser le rseau local et arriver son destinataire : la donne demande est alors envoye via le rseau local et est copie dans la mmoire locale de lordinateur demandeur. Il va de soit que les communications entre les diffrents processeurs peuvent prendre un temps relativement long, et que ceux-ci sont loin d'tre ngligeables. Avec une organisation de ce genre, la qualit et les performances du rseau reliant les ordinateurs est trs important pour les performances.

Encore des caches


Bien sr, rien nempche de mettre des mmoires caches entre la mmoire d'un processeur et celui-ci.

Cette fois, placer des mmoires caches ne pose strictement aucun problme : on n'a pas besoin de garantir la cohrence des caches avec ce genre de systme.

SADM
Enfin, il reste une dernire classe d'architectures : les architectures SADM. Avec elles, les processeurs possdent une mmoire locale, qui leur est rserve et dans laquelle ils peuvent faire ce que bon leur semble. Mais ils peuvent aussi accder aux autres mmoires : cela leur prend un peu plus de temps. V oici un exemple d'architecture NUMA possible.

www.siteduzero.com

Partie 1 : Le paralllisme : un peu de thorie

11/97

Avec cette mthode, chaque processeur voit toute les mmoires virtuellement rassembles dans une seule grosse mmoire unique. Accder une portion de cette mmoire unique correspondant sa mmoire locale est rapide, mais accder une portion de la grosse mmoire unique correspondant la mmoire d'un autre processeur est redoutablement plus lent. Bien grer les temps d'accs aux diffrentes mmoires est donc un plus bien agrable.

Des caches, encore et toujours !


On peut encore une fois utiliser des mmoires caches sur ce genre de machines, mais on retombe sur un problme : la cohrence des caches n'est pas assure et on doit se dbrouiller pour qu'elle le soit, comme pour les architectures SASM.

www.siteduzero.com

Partie 1 : Le paralllisme : un peu de thorie

12/97

Les limites thoriques


On a vu dans le chapitre prcdent que l'on pouvait donc utiliser plusieurs processeurs pour traiter des taches ou des donnes. Mais existe-t-il des limites l'utilisation de plusieurs processeurs ?

Dans ce chapitre, on va voir que suivant la manire utilise pour exploiter plusieurs processeurs, les rsultats changent du tout au tout. On y verra que dans certains cas, ajouter des processeurs ne sert pas grand chose, voire rien. Dans d'autres, il n'y a pas vraiment de limites thoriques aux gains de performances que l'on peut obtenir en ajoutant des processeurs.

Loi d'Amdhal
Imaginez que vous ayez cod un programme, spcialement conu pour exploiter plusieurs processeurs. V otre programme est conu de faon rpartir ses calculs sur autant de processeurs que possible. Seul problme, vous ne savez pas trop quel sera le gain que vous obtiendrez en utilisant plusieurs processeurs avec un programme pareil, et vous voudriez savoir quel est le gain thorique maximal que vous pourriez obtenir. Et bien pour connatre ce gain maximal, vous pouvez utiliser la loi d'Amdhal . Cette loi est base sur une approche simple : on prend un programme et on regarde ce qui se passe en fonction du nombre de processeurs.

Hypothses de base
Pour dmontrer cette loi, on va supposer : que notre ordinateur contient un nombre de processeurs, qu'une portion plus ou moins importante de notre programme peut utiliser plusieurs processeurs , cette utilisation est la plus efficace possible quel que soit le nombre de processeurs, on se moque des cots induits par la gestion du code sexcutant sur plusieurs processeurs, le systme dexploitation, la faon dont est conu le matriel, et on passe tous ces dtails sous le tapis. Expliquons un peu plus en dtail la deuxime hypothse. Une portion de notre programme peut exploiter plusieurs CPU : on l'appelle le code parallle. Mettre plusieurs CPU n'acclrera que cette portion du programme et pas le reste. Intuitivement, on se doute que plus la quantit de ce code parallle est importante, plus notre programme profitera de la prsence de plusieurs processeurs. On va appeler le reste du programme, incapable d'exploiter plusieurs CPU, le code srie. L encore, on devine facilement que plus le programme contient de code srie, moins notre programme gagnera en performances si l'on augmente le nombre de processeurs. Pour calculer le gain maximal que l'on peut obtenir sur processeurs, on suppose que ce code parallle peut tout aussi bien exploiter la puissance d'un seul processeur, que de 2 , 20 , voir 10 000 000 processeurs. Bref, ce code est quasiment parfait et on s'interdit les situations du style : on observe un gain norme avec 2 ou 4 processeurs, mais pas au-del. De plus, ce programme est excut sur la mme quantit de donnes : on ne rajoute pas de donnes en mme temps qu'on ajoute des processeurs, et ces donnes ont toujours la mme taille, histoire de comparer ce qui est comparable.

Un seul processeur
Pour commencer, il va nous falloir une rfrence pour pouvoir comparer les gains d l'ajout de processeurs. La meilleure rfrence, c'est le cas o l'on utilise un seul processeur. Le temps mis par notre programme pour sexcuter sur un seul processeur, qu'on notera du temps dexcution du code srie, not , et du temps dexcution du code parallle sur un seul processeur, not . , est donc la somme :

Plusieurs processeurs
Maintenant, on va prendre plusieurs processeurs et comparer.

www.siteduzero.com

Partie 1 : Le paralllisme : un peu de thorie


Mais combien va-t-on prendre de processeurs ?

13/97

processeurs, pas un de plus, pas un de moins ! V oyons ce qui se passe : le code parallle va faire excuter des calculs simultanment sur ces N processeurs, au lieu de les excuter les uns aprs les autres. Le temps pass excuter ce code parallle va diminuer. Mais que vaut le temps dexcution du code parallle sur N processeurs ?

Pour l'valuer, on n'a pas vraiment le choix : on doit utiliser une des hypothses de dpart qui dit que notre code va rpartir au mieux ses instructions sur les processeurs. Le temps dexcution du code parallle sera alors divis par le nombre de processeurs . Bien sr, le code srie n'est pas affect par le nombre de processeurs : celui-ci est excut sur un seul processeur et met donc toujours le mme temps sexcuter : ne change pas ! En notant le temps mis par notre code sexcuter sur N processeurs, on obtient donc :

On rappelle que

est le temps mis pour excuter le code parallle sur un processeur, pas

Gain
Mais que faire pour comparer le temps dexcution du programme sur la machine avec un processeur et celui mesur sur une machine avec plusieurs processeurs ? Pour cela, on va devoir calculer un truc que l'on appelle le gain. Ce gain se calcule en inversant la fraction vue au-dessus, ce qui nous donne :

En clair, il suffit de diviser le temps dexcution obtenu avec un seul processeur par le temps dexcution sur N processeurs. Ce gain a une signification simple : si le gain vaut X, alors l'application est X fois plus rapide que si on lexcutait sur un seul processeur. On verra ainsi si le programme sexcute 2, 3, voir 50 fois rapidement. Bien videmment, plus ce gain est lev, plus notre programme aura gagn en performance compar la version avec un seul processeur.

Exemple
Si le temps de calcul avec, mettons 5 processeurs (donc N = 5) est la moiti du temps de calcul obtenu avec un seul processeur, cela veut dire que

Dans ce cas, on peut aussi dire que le programme va deux fois plus vite. Vu que le temps de calcul avec 5 processeurs est gal la moiti du temps de calcul avec un seul processeur, on peut aussi dire que le temps de calcul avec un processeur est en effet 2 fois plus long que le temps de calcul avec 5. Cela se vrifie en calculant simplement notre gain :

Drivation de la Loi d'Amdhal


www.siteduzero.com

Partie 1 : Le paralllisme : un peu de thorie


Partons de notre fraction

14/97

Remplaons T(N) par sa valeur explicite plus haut savoir

. On trouve donc le rapport entre et qui vaut

Cette quation peut se "simplifier" et donne alors l'quation suivante

Et l, il faut remarquer deux choses trs simples : n'est rien d'autre qu'un pourcentage : c'est le pourcentage de temps mis excuter le code srie. De mme, est le pourcentage de temps mis excuter le code parallle.

En posant

et

, on peut alors simplifier notre equation en

On peut alors calculer le gain en inversant notre fraction et on trouve

On peut aussi remarquer que

et ainsi obtenir

Cette quation s'appelle la loi d'Amdhal et nous donne la gain maximal thorique que l'on peut obtenir avec un code passant pourcents de son temps dexcution dans un code parallle, avec processeurs.

Ce que nous dit la loi d'Amdhal


Cette loi nous donne donc une estimation du gain en temps dexcution d'une application excute sur plusieurs processeurs. Mais que peut-on en dduire d'utile ? Peut-on trouver des moyens de gagner en performance efficacement grce cette loi ? Oui, et c'est ce qu'on va voir.

www.siteduzero.com

Partie 1 : Le paralllisme : un peu de thorie

15/97

Paralllisons mieux !
Tout d'abord remarquons une chose : quand on fait tendre le nombre de processeurs vers l'infini, le gain atteint un maximum qui vaut

Qu'on peut simplifier en

Cela signifie une chose trs simple : quand tend vers l'infini, le code paralllis est excut en un temps qui tend vers . Seul reste le code srie qui ne peut pas tre acclr par plusieurs processeurs. Le temps dexcution de ce code restant le mme, le temps dexcution du programme ne peut pas descendre en-dessous du temps dexcution du code srie. C'est donc la premire limite que nous impose la loi d'Amdhal. La solution la plus simple est donc de parallliser le plus possible le code de notre programme, histoire de faire diminuer et augmenter le plus possible. C'est cela qui est le plus recherch l'heure actuelle. Seul problme : tous les programmes ne se laissent pas parallliser aussi facilement. Certains programmes se paralllisent facilement parce que les calculs qu'ils ont faire sont assez indpendants. Mais d'autres n'ont pas cette particularit et sont trs trs difficilement paralllisables, voire pas du tout.

Nombre de processeurs
L'autre solution est d'augmenter le nombre de processeurs, afin de rduire le plus possible le terme . Mais cette solution a une

efficacit assez limite : il faut que la part de code paralllisable soit suffisante pour que cela ait un impact suffisant. Imaginons un exemple simple : 20% du temps dexcution de notre programme (quand il est excut sur un seul processeur) est pass excuter du code parallle. Avec N processeurs, le gain calculable par la loi d'Amdhal nous donne un gain maximal de

Si on calcule le gain en fonction du nombre de processeurs, on obtient alors la tableau suivant. Nombre de processeurs Gain maximal 2 3 4 5 6 7 8 ... 16 ... 11.11% 15.38% 17.64% 19.04% 20% 20.6% 21.21% ... 23% ... 25%

www.siteduzero.com

Partie 1 : Le paralllisme : un peu de thorie

16/97

On voit bien qu'au del de 5 ou 6 processeurs, augmenter le nombre de processeurs ne sert pas vraiment grand chose : doubler leur nombre revient souvent augmenter les performances d'un misrable pourcent. Cette solution devient donc trs limite quand on augmente le nombre de processeurs. En clair : au-del d'un certain nombre de processeurs, a ne marche plus ! Au-del de 10 processeurs avec un code passant 20% de son temps excuter du code parallle, le gain est ngligeable. Pour prendre un autre exemple, au-del de 8 processeurs, un code passant 50% de son temps excuter du code parallle ne sera pas vraiment excut plus vite. 8 processeurs, cela correspond une quadri-core incorporant la technologie SMT comme on en trouve chez intel. Remarquons que les programmes qui passent la moiti de leur temps excuter du code parallle sont rares chez les programmes grand-public.

Dans la ralit
Enfin, une dernire remarque : la loi d'Amdhal est optimiste : elle a t dmontre en postulant que le code parallle peut tre rparti sur autant de processeurs qu'on veut et peut profiter d'un grand nombre de processeurs. Dans la ralit, rares sont les programmes de ce genre : certains programmes peuvent la rigueur exploiter efficacement 2, 4 , voir 8 processeurs mais pas audel. Elle ne tient pas compte des nombreux problmes techniques, aussi bien logiciels que matriels qui limitent les performances des programmes conus pour exploiter plusieurs processeurs. La loi d'Amdhal donne une borne thorique maximale au gain apport par la prsence de plusieurs processeurs, mais le gain rel sera quasiment toujours infrieur au gain calcul par la loi d'Amdhal.

Code srie
Une autre solution, plus simple est encore de diminuer le temps dexcution du code srie en optimisant le code, sans forcment chercher le parallliser. C'est la seule solution viable pour un programme contenant peu de code paralllisable. Pour donner un exemple, on va faire un petit tableau contenant le gain obtenu avec un programme avec un P de 10% en fonction du nombre de processeurs. Nombre de processeurs Gain maximal 2 4 8 5% 8% 10% 11.11%

Si notre programme ne peut pas voir P augmenter, au-del de 4-8 processeurs, il vaudra mieux diminuer le temps dexcution de celui-ci plutt que de chercher rajouter beaucoup de processeurs inutilement.

Loi de Gustafson Paralllisme de donnes


La loi d'Amdhal est base sur une approche simple : on prend un programme qui fait ce qu'il y a faire, et on regarde ce qui se passe en augmentant le nombre de processeurs. Mais n'y aurait-il pas une autre faon de faire, qui permettrait d'utiliser plusieurs processeurs diffremment ?

Et bien si ! Avec la loi d'Amdhal, on a pris un programme qui travaille toujours sur des donnes de mme taille et on n'a pas augment le nombre de donnes traiter avec le passage de 1 N processeurs. Au lieu de toujours utiliser la mme quantit de donnes, pourquoi ne pas simplement utiliser les processeurs supplmentaires pour travailler sur un nombre de donnes plus grand ? Prenons l'exemple d'une application de traitement d'image : au lieu de travailler sur une image de 2048*2048, pourquoi ne pas utiliser nos processeurs pour travailler sur plusieurs images de mme taille ou sur une image de taille quadruple, histoire de rentabiliser ?

www.siteduzero.com

Partie 1 : Le paralllisme : un peu de thorie

17/97

Cela s'appelle le paralllisme de donnes , qui consiste excuter le mme programme sur des donnes diffrentes et indpendantes. Cela permet donc de traiter N donnes sur N processeurs en mme temps. Pour rsumer : tous les processeurs excutent un seul et unique programme ou une suite d'instructions, mais chacun de ces processeurs va travailler sur une donne diffrente. Et l encore, on peut trouver une loi similaire la loi d'Amdhal, mais beaucoup moins restrictive : la loi de Gustafson !

Hypothses de base
Prenons un programme sexcutant sur un seul processeur. Celui-ci prend un temps excuter son code srie et un temps pour excuter son code parallle. Ce code parallle sexcute sur une donne (image, fichier son, ou autre). Ce code parallle sera excut simultanment sur plusieurs donnes : ainsi, pour N donnes, On pourra prendre N processeurs et excuter sur chacun d'eux le code parallle sur une des N donnes. En prenant 1 ou N processeurs, la partie srie restera la mme et sera execute sur un seul processeur durant un temps .

Une seule donne


Avec une seule donne, le temps dexcution du code parallle. est gal , avec le temps dexcution du code srie et celui

Plusieurs donnes
Par contre, durant le temps , on pourra demander chacun des processeurs de traiter une donne en un temps qui fait qu'en un temps , on peut demander notre processeur dexcuter un programme sur donnes. Dans ce cas, le temps vaut donc . donnes unes par une, ce . Ce

Si ce calcul fait sur ces donnes avait t fait sur un seul processeur, on aurait d calculer ces qui aurait pris un temps gal

Gain
En calcule donc le gain

Cette quation se factorise en

A ce stade, une petite remarque s'impose :

est gal au pourcentage de temps .

pass excuter le code srie et

celui du code parallle (sur N processeurs), qu'on note

On trouve la loi de Gustafson qui nous donne le gain en fonction du nombre de processeurs :

En remarquant que

, on trouve alors la loi de Gustafson

www.siteduzero.com

Partie 1 : Le paralllisme : un peu de thorie

18/97

Que nous apprend Gustafson ?


La loi de Gusatfson rpond un besoin certain : comment faire pour exploiter au mieux le paralllisme lorsque l'on souhaite travailler sur une grande quantit de donnes. Cela correspond au paralllisme de donnes : excuter simultanment un mme programme sur des donnes indpendantes. Plus le nombre de donnes traites en parallles est grand, plus ce genre de paralllisme est efficace. En effet, sur un seul processeur, si on augmente le nombre de donnes, et que ces donnes doivent tre traites par la partie paralllise du programme, cela prendrait un temps gal Mais surtout, il n'y a pas de limites thoriques : on peut mettre autant de donnes que l'on veut, avec processeurs, celles-ci sont toutes traites par un processeur simultanment et le temps mis pour traiter donnes sur processeurs sera identique au temps mit pour traiter une donne sur un seul processeur. Aucune limite n'existe concernant la quantit de donnes traitables simultanment, et donc au gain que l'on peut obtenir. Bien sr, il faut se rappeler que la loi de Gustafson s'applique sur une dure dtermine : elle ne rend pas les calculs plus rapides : si une donne met un temps tre traite, alors on ne gagne rien en terme de temps de calcul. V oici donc quelques conclusions que l'on peut tirer avec ce que l'on vient de voir. Parallliser un programme qui excute de nombreux calculs en parallle sur le mme ensemble de donnes est vou montrer "rapidement ses limites". Ce paralllisme est en effet soumi la loi d'Amdhal. Par contre, le paralllisme de donnes, consistant effectuer un mme programme/sousprogramme sur un ensemble de donnes diffrentes donne de trs bons rsultats. Il existe nanmoins d'autres formules ou lois permettant de dduire plus ou moins prcisment l'efficacit thorique de la paralllisation d'un programme quelconque. On peut citer par exemple la mtrique de KarpFlatt. Mais dans la ralit, aucune de ces formules n'est utilisable directement : de nombreux autres paramtres interviennent, qui dpendent de l'architecture des processeurs utiliss, du langage de programmation utilis et de la manire dont a t programm le programme en question. N'oublions pas que ces formules sont thoriques, et ne servent qu'a donner des indictions qualitatives.

www.siteduzero.com

Partie 2 : Multi-processeurs, Multicoeurs et Hyperthreading

19/97

Partie 2 : Multi-processeurs, Multicoeurs et Hyperthreading


Aprs avoir vu l'organisation globale des architectures parallles et les fameuses loi d'Amdhal et de Gustafson, nous allons maintenant nous intresser au fonctionnement des ordinateurs plusieurs processeurs et des architectures multicurs. Aprs tout, il a bien fallu adapter le matriel aux diffrentes formes de paralllisme, et pour s'en rendre compte, rien de tel qu'une petite vire au centre de nos processeurs actuels.

Hyperthreading et compagnie
Dans ce qui prcde, on a vu des ordinateurs possdant plusieurs processeurs. V ous devez donc fatalement penser qu'il faut obligatoirement plusieurs processeurs pour xecuter plusieurs programmes en parallle, mais sachez que c'est faux ! Il est parfois possible pour un processeur seul dexcuter plusieurs programmes en mme temps. Pour cela, il faut utiliser des processeurs spciaux, qui utilisent des ruses de sioux.

Du paralllisme avec un seul processeur


A l'intrieur d'un processeur, on trouve un petit circuit qui est capable d'effectuer des calculs. Celui-ci s'appelle l'unit de calcul . Ce circuit n'est pas le seul prsent dans notre processeur, loin de l, mais c'est celui qui va nous intresser dans ce chapitre.

Le pipeline fait des bulles !


Il arrive souvent que l'unit de calcul d'un processeur ne fasse rien. Mine de rien, un paquet de phnomnes bizarres aux noms coucher dehors (dpendances de donnes, accs la mmoire, mauvaise prdiction de branchement, etc) peuvent aboutir ce genre de choses. Par exemple, dans certains cas, le fait qu'une instruction doive attendre le rsultat d'une autre peut rendre l'unit de calcul inutilise durant un moment.

De nombreuses techniques ont ts inventes pour faire en sorte que notre unit de calcul soit utilise au plus possible : utilisation de caches non-bloquants, forwarding, Out Of Order , excution superscalaire, et autre techniques aux noms qui font peur aux enfants sont de la partie. Mais il arrive toujours que notre unit de calcul soit inutilise durant quelques cycles. Par quelques cycles, on veut dire que quand on accde la mmoire RAM, on peut faire patienter l'unit de calcul durant une bonne centaine de cycles d'horloge. Autant dire que niveau efficacit, on peut mieux faire ! Un jour, quelqu'un a eu une ide fabuleuse : puisqu'on arrive pas trouver de quoi donner du travail notre unit de calcul avec un seul programme, pourquoi ne pas essayer avec plusieurs programmes ? Au lieu de faire en sorte de ne rserver notre unit de calcul qu'aux instructions d'un seul programme, on pourrait prendre des instructions en provenance de plusieurs programmes et

www.siteduzero.com

Partie 2 : Multi-processeurs, Multicoeurs et Hyperthreading

20/97

les rpartir sur notre unit de calcul suivant les besoins : si un programme n'utilise pas notre ALU, elle sera disponible pour un autre.

Types de super-threading matriel explicite


Il existe diffrentes manires pour remplir notre unit de calcul avec des instructions en provenance de plusieurs programmes. Suivant la mthode utilise, on peut se retrouver avec des gains plus ou moins intressants et une unit de calcul plus ou moins utilise. Dans ce qui va suivre, on va dtailler ces diffrentes faons.

Fine Grained Multithreading


La premire mthode consiste switcher entre les programmes : on excute une instruction d'un programme, puis on passe au second programme, et ainsi de suite. C'est le processeur qui se charge de passer automatiquement d'un programme un autre, sans temps d'attente.

www.siteduzero.com

Partie 2 : Multi-processeurs, Multicoeurs et Hyperthreading

21/97

Cette technique a de nombreux avantages : on est certain que lorsque une instruction sexcute, elle n'a pas attendre le rsultat de l'instruction excute juste avant elle. Ces deux instructions n'appartenant pas au mme programme, il est impossible qu'une instruction doive manipuler le rsultat d'une autre. Cela permet de grandement simplifier le processeur vu que l'on a pas besoin de grer le cas o une instruction doit attendre le rsultat d'une autre : a fait des circuits en moins, et l'unit de calcul est mieux utilise. Mais ces processeurs ont un dfaut : pour obtenir de bonnes performances, on est oblig d'avoir beaucoup de programmes en cours dexcution. Sans cela, diverses raisons techniques vont faire que l'unit de calcul sera inutilise durant pas mal de temps.

www.siteduzero.com

Partie 2 : Multi-processeurs, Multicoeurs et Hyperthreading

22/97

Pour ceux qui ont lu mon tutoriel fonctionnement d'un ordinateur depuis zro, je peux aller plus loin dans les prcisions : il faut autant des programmes en cours dexcution que d'tages dans notre pipeline

Pour limiter la casse, certains processeurs peuvent dcider de lancer plusieurs instructions d'un mme programme la suite sur l'unit de calcul, au lieu de changer de programme constamment. videmment, cela ncessite des conditions particulires. Gnralement, chaque instruction va contenir quelques bits qui permettront de dire au processeur : tu peux lancer 1, 2, 3; etc : instructions la suite sans problme, il n'y aura pas de dpendances entre ces instructions. Ainsi, le processeur peut dcider si besoin d'alimenter l'unit de calcul avec ces instruction du mme programme. Cette technique s'appelle la dependance lookahead technique.

www.siteduzero.com

Partie 2 : Multi-processeurs, Multicoeurs et Hyperthreading

23/97

La technique du Fine Grained Multithreading pose tout de mme quelques problmes : on n'a rarement suffisamment de programmes pour alimenter l'unit de calcul convenablement, et les techniques de dependance lookahead n'arrivent pas forcment limiter la casse. Aussi cette technique de paralllisme matriel a assez peu d'efficacit en dehors de quelques cas particuliers.

Coarse Grained Multitrheading


Autre forme de paralllisme matriel : le Coarse Grained Multitrheading . Avec celui-ci, le processeur dcide toujours de changer de programme, mais il fait nettement moins souvent. Le changement de programme se fait lors de certains vnements particuliers qui prennent du temps.

www.siteduzero.com

Partie 2 : Multi-processeurs, Multicoeurs et Hyperthreading

24/97

L'ide est dexcuter les instructions d'un programme normalement, jusqu' ce qu'un vnement rendant l'unit de calcul inutle durant un certain temps arrive : accs la mmoire (cache miss), branchement mal prdit, etc. Au lieu de passer tout ce temps ne rien faire, autant changer de programme pour remplir les vides avec des instructions d'un autre programme. Suivant le processeur, les vnements faisant changer de programme ne sont pas forcment les mmes. Sur certains processeurs, on change de programme lorsque certaines instructions sont excutes : accs la mmoire, branchements, etc. Sur certains processeurs, on utilise une instruction de changement de programme, fournie par le jeu d'instruction du processeur : elle seule peut faire changer le programme excut. Et enfin, nettement plus intressant : certains processeur dcident tout seuls quand changer de programme ! Gnralement, ces processeurs changent de programme xecuter lorsqu'on doit accder la mmoire (lors d'un cache miss). Il faut dire que l'accs la mmoire est quelque chose de trs lent, aussi changer de programme et xecuter des instructions pour recouvrir l'accs la mmoire est une bonne chose. Il faut tout de mme remarquer une chose : pour tre efficace, ce genre de processeurs a besoin de pouvoir accder la mmoire cache pendant qu'il attend des donnes en provenance de la mmoire. En termes techniques, on dit que celui-ci a besoin de caches non-bloquants. Et oui, il n'est pas rare que les instructions du programme fraichement dmarr doivent accder la mmoire cache pour faire leurs calculs : autant dire que si le cache n'est pas utilisable parce qu'un autre programme est interrompu pour accder la mmoire, a la fout mal !

Simultaneous Multithreading
Les techniques vues au-dessus imposent pas mal de contraintes et surtout peuvent laisser l'unit de calcul inutilise durant assez longtemps. Pour viter ce genre de choses, il existe une dernire technique qui permet de mieux exploiter l'unit de calcul dans pas mal de situations. Il s'agit du Simultaneous Multi-Threading ou SMT. Cette technique consiste faire pareil que ses prdcesseurs, une diffrence prt : aucun programme n'a la priorit sur l'autre. Pas besoin de changer de programme tout les cycles ou lors d'un vnement particulier : les deux programmes sexcutent en mme temps, et chaque programme va utiliser l'unit de calcul ds que les autres la laissent libre.

www.siteduzero.com

Partie 2 : Multi-processeurs, Multicoeurs et Hyperthreading

25/97

Ce fameux Simultaneous Multi-Threading porte un autre nom : Intel lui a en effet donn le nom d'Hyperthreading , pour de sombres raisons marketing . Aprs tout, Hyperthreading est quand mme un nom un peu plus vendeur que Simultaneous Multi-Threading . Le premier processeur Intel qui a intgr cette technologie tait le processeur Intel Pentium 4. Il faut dire que ce processeur possdait des caractristiques techniques (un pipeline long, trop long) qui faisaient qu'il utilisait assez peu son unit de calcul et qu'il avait normment de vides remplir. L'Hyperthreading tait limit deux programmes/ threads logiciels diffrents et ne pouvait faire plus.

Processeurs superscalaires
Nos ordinateurs modernes implmentent souvent non pas une, mais plusieurs units de calcul. C'est ainsi, au lieu d'utiliser une seule unit de calcul, ceux-ci peuvent rpartir les instructions d'un programme sur ces diffrentes units de calcul histoire d'aller plus vite. Ainsi, nos processeurs peuvent contenir entre 2, 4, 8, voire encore plus units de calcul, sans problmes (ou presque). Et il faut savoir que les techniques vues au-dessus se marient trs bien avec la prsence de plusieurs units de calcul. On a beaucoup plus d'opportunits pour xecuter des instructions de programmes diffrents !

www.siteduzero.com

Partie 2 : Multi-processeurs, Multicoeurs et Hyperthreading

26/97

Et dans le processeur ?
Reste savoir comment notre processeur est capable de russir ce genre de prouesses. Et on va le voir, ce n'est pas si difficile que a ! Mais pour commencer, quelques rappels sont de rigueur !

Cycle Fetch / Excution


Nos processeurs sont censs xecuter des instructions machines. Pour ce faire, ceux-ci comportent donc une unit de calcul, qui est capable dexcuter notre instruction. Mais notre unit de calcul n'est pas le seul circuit ncessaire pour xecuter notre instruction : notre instruction n'arrive pas de nulle part ! Chaque instruction est reprsente dans notre ordinateur sous la forme d'une suite de bits, stocke dans la mmoire RAM de notre ordinateur un endroit bien prcis. Celles-ci sont places les unes la suite des autres dans l'ordre o elles doivent tre excutes. Par exemple : Adresse 0 1 2 3 4 ... 5464 Instruction Charger le contenu de l'adresse 0F05 Charger le contenu de l'adresse 0555 Additionner ces deux nombres Charger le contenu de l'adresse 0555 Faire en XOR avec le rsultat antrieur ... Instruction d'arrt

Pour se souvenir d'o il en est, le processeur contient une petite mmoire qui contient la position (l'adresse mmoire) de notre instruction dans la RAM de l'ordinateur. Vu que nos instructions sont places les unes aprs les autres en mmoire, ce registre permet de localiser la prochaine instruction xecuter : il suffit d'augmenter le contenu de ce registre de faon le faire pointer sur l'instruction suivante chaque excution d'une instruction. Ce fameux registre s'appelle le Program Counter. Il existe quand mme un moyen pour modifier l'ordre dexcution des instructions : certaines instructions permettent de modifier le contenu du Program Counter. Ces instructions permettent de "sauter" directement une instruction voulue dans le programme et poursuivre l'excution partir de celle-ci. Ce sont les instructions de branchements .

www.siteduzero.com

Partie 2 : Multi-processeurs, Multicoeurs et Hyperthreading

27/97

Cette instruction, il va falloir la charger depuis la mmoire, et l'amener de la mmoire notre unit de calcul. C'est le rle d'un ou de plusieurs circuits prsents dans notre processeur, qui contient notamment notre fameux Program Counter. Ce chargement est toutefois un peu plus compliqu : un processeur ne fonctionne pas aussi simplement, et notre circuit charg de rcuprer une instruction depuis la mmoire est un peu plus complexe : il est dcoup en plusieurs circuits qui effectuent des taches diverses comme " dcoder l'instruction " ou " renommer des registres ". Cela consiste interpreter la suite de bits qui reprsente l'instruction et en dduire comment configurer lunit de calcul et les autres circuits du processeur. Mais cela ne nous intresse pas ici. On peut donc considrer que notre processeur est dcoup en deux grands circuits : une unit de calcul (aussi appele ALU) qui se charge de faire les calculs, et un circuit qui se charge de rcuprer l'instruction depuis la mmoire.

Notre processeur contient aussi de petites mmoires ultra-rapides qui servent stocker temporairement des donnes : on les appelle des registres . Pour information, le Program Counter est un de ces registres. Gnralement, une instruction qui doit manipuler une donne contenue dans un registre devra indiquer qu'elle veut manipuler ce registre dans sa suite de bits. Un programme pourra manipuler le contenu des registres, pour plus de simplicit et de performances. Pour identifier chacun des registres, on donne ceux-ci des noms, qui ne sont rien d'autres que des numros.

Niveau circuits
Autrefois, on fusionnait les circuits chargs du chargement de notre instruction (et de tas d'autres taches annexes), et l'unit de calcul. Mais aujourdhui, ce n'est pas le cas : ces deux circuits sont spars. Cela a un avantage certain : l'unit de chargement (aussi appele l'unit de Fetch ) peut ainsi xecuter une instruction sur l'unit de calcul et commencer charger l'instruction suivante.

Multiple Fetch
Les processeurs permettant dexcuter plusieurs programmes utilisent cette technique fond. En effet, ceux-ci doivent remplir les vides de notre unit de calcul avec des instructions en provenance de plusieurs programmes. En clair, ceux-ci doivent charger l'avance plusieurs instructions : au minimum une par programme ! Il restera ensuite les rpartir sur notre unit de calcul suivant les besoins. Pour cela, on va devoir trouver un moyen pour charger plusieurs instructions, en provenance de programmes diffrents. Mine de rien, la solution est assez simple : il suffit d'utiliser plusieurs units de chargement, chacune avec son Program Counter. V oil qui est simple.

www.siteduzero.com

Partie 2 : Multi-processeurs, Multicoeurs et Hyperthreading

28/97

Toutefois, cela n'est pas la seule solution : on peut trs bien rassembler ces plusieurs units de chargement en une seule grosse unit, avec plusieurs Program Counter (toujours un par programme). Mais cela est un dtail. Quoiqu'il en soit, beaucoup de circuits sont dupliqus dans un processeur utilisant le SMT.

Decoupled architectures
Il faut bien stocker ces instructions prcharges quelque part. Et pour cela, on va intercaler une petite mmoire entre l'unit de chargement/dcodage et l'unit de calcul. Cette petite mmoire s'appelle l'instruction buffer . Nos instructions seront donc mises en attente dans cette mmoire tampon, avant d'tre rparties sur notre ALU au besoin. Pour cela, un circuit spcialis, le dispatcher se chargera de rpartir les diffrentes instructions sur les units de calcul. Ainsi, on peut rpartir nos instructions en provenance de divers programmes sur notre ALU. Il suffit de prcharger les instructions en provenance de nos programmes dans cet Instruction Buffer, et laisser le dispatcher faire son travail. Sur les processeurs utilisant le SMT, on trouve non pas un, mais plusieurs Instruction Buffers. Il y en a un par programme.

www.siteduzero.com

Partie 2 : Multi-processeurs, Multicoeurs et Hyperthreading

29/97

Jeu de registres
Il nous reste enfin un lger petit problme rgler : chaque programme manipule des registres et il semble difficile de partager un registre pour le laisser stocker plusieurs donnes en provenance de plusieurs programmes. Il faudra donc dupliquer les registres histoire que chaque programme puisse avoir son ensemble de registres rien qu' lui. Cela vite que nos processeurs se marchent sur les pieds. Il faudra aussi faire quelques modifications : les registres tant identifis par des noms, dupliquer des registres ne suffira pas. Si deux programmes utilisent le mme nom de registre, ils utiliseront le mme registre. Pour viter les ennuis, on va donc dcider de placer chaque registre ayant un nom en plusieurs exemplaires : plusieurs registres auront le mme nom, et chacun d'entre eux se verra attribuer un programme diffrent. Dans les faits, cela est fait en dupliquant les registres, et aussi en ajoutant des circuits qui permettront de savoir quel programme est attribu chaque registre. Et voil, on obtient enfin notre processeur, qui utilise du SMT.

www.siteduzero.com

Partie 2 : Multi-processeurs, Multicoeurs et Hyperthreading

30/97

www.siteduzero.com

Partie 2 : Multi-processeurs, Multicoeurs et Hyperthreading

31/97

Processeurs multicoeurs
Dans les chapitres prcdents, on a vu que le paralllisme de tache pouvait saccommoder d'un seul processeur. Nanmoins, ces techniques ne sont pas des plus efficaces, et rien ne vaut l'utilisation de plusieurs processeurs pour rellement tirer partie du paralllisme de taches. Les premires tentatives pour implmenter le paralllisme au niveau matriel ont ts des plus simples : il suffisait simplement de mettre plusieurs ordinateurs ensemble et de les relier via un rseau local. Puis, on a ensuite utilis une autre mthode : placer plusieurs processeurs dans la mme machine. Ainsi, certains ordinateurs contenaient une cart mre sur laquelle on pouvait mettre plusieurs processeurs, gnralement 2 ou 4. Mais utiliser plusieurs processeurs n'est pas la seule solution pour xecuter plusieurs programmes en parallles.

Processeurs multicurs
V ous avez tous surement entendus parler des processeurs multicurs, et si a se trouve, vous avez la chance d'en possder un. Ces processeurs permettent aussi d'xecuter plusieurs programmes la fois.

Le multicurs, c'est quoi ?


Avant toute chose, il pourrait paraitre bizarre qu'un seul processeur puisse xecuter plusieurs programmes simultanment. Logiquement, un processeur n'est cens xecuter qu'un programme la fois (sauf si celui-ci utilise le Simultaneous Multithreading ). On vous a surement dit que ce processeur contenait plusieurs curs, capables dexcuter des programmes en parallle, mais cela ne vous a pas vraiment clair ? Et bien je vous le donne en mille : en fait, un processeur multicurs n'est rien d'autre qu'une puce de silicium sur laquelle on a plac plusieurs processeurs ! Et oui, ce n'est rien de plus que cela. Chacun de ces processeurs intgr dans ce circuit sappellera un coeur. Suivant le nombre de curs prsents dans notre processeur, celui-ci sera appel un processeur dual-core (deux curs), quadcore (4 curs), octo-core (8 curs), etc. On pourrait croire que placer deux processeurs sur la mme puce est un peu du gchis : pourquoi ne pas simplement utiliser deux processeurs spars, chacun dans son coin. Mais sachez que ce genre d'intuitions est toujours trompeuse ! Intgrer deux curs sur une mme puce a au contraire de nombreux avantages en terme de performances. Ces processeurs multicurs sont apparus ces dernires annes. Et ce n'est pas un hasard : pour crer ce genre de processeurs, il a fallu faire de nombreux progrs dans les technologies de fabrications de nos processeurs, et attendre que le besoin s'en fasse sentir.

Multicurs asymtrique
Dans la grosse majorit des cas, les curs d'un processeur multicurs sont tous identiques. Mais ce n'est certainement pas une obligation : on peut trs bien mettre plusieurs processeurs assez diffrents sur la mme puce, sans que cela ne pose problme. On peut trs bien utiliser un cur principal avec des curs plus spcialiss autour, par exemple. Cela s'appelle du multicurs asymtrique. Ce terme est opposer au multicurs symtrique, dans lequel on place des processeurs identiques sur la mme puce de silicium.

L'architecture CELL
Le processeur CELL est un des exemples les plus rcent de processeur multicurs asymtrique. V ous connaissez surement ce fameux processeur, et vous en possdez peut-tre un chez vous. videmment, il ne faut pas chercher dans l'unit centrale de votre PC de bureau, non. Il faut chercher dans votre console de jeux : et oui, votre PS3 contient un processeur CELL. Pour simplifier, notre processeur CELL peut tre vu comme intgrant un cur principal POWER PC version 5, qu'on retrouvait autrefois dans les Mac, et environ 8 processeurs auxiliaires.

www.siteduzero.com

Partie 2 : Multi-processeurs, Multicoeurs et Hyperthreading

32/97

Sur le schma du dessus, le processeur principal est appel le PPE, et les processeur auxiliaires sont les SPE. Comme on le voit sur ce schma, l'organisation des caches et des diffrentes mmoires intgres au processeur CELL est assez droutante. Notre processeur principal intgr un cache L1, ainsi qu'un cache L2, tout deux spcialement ddi celui-ci. On remarque que nos processeurs, PPE et SPE, sont relis via la mmoire et aux diffrents bus de notre ordinateur pas un bus intercal, qui relier tous les processeurs, la mmoire, et les autres bus entre eux. Nos SPE sont relis une mmoire locale, le Local Store , qui communique avec le processeur principal via un bus spcial. Pour information, cette mmoire fait dans les 256 kibioctets : c'est trs peu, mais suffisant pour ce que ces processeurs doivent faire. Chaque SPE peut ainsi aller charger ou enregistrer des donnes dans cette mmoire locale. Par contre, il leur est interdit d'aller manipuler la mmoire centrale de l'ordinateur directement. Pour cela, ces processeurs doivent passer par un intermdiaire : des contrleurs DMA. Ceux-ci sont capables de faire transiter des blocs de mmoire entre la mmoire centrale et le local store du SPE. C'est un peu l'exact oppos de ce qui se passe pour le processeur principal : celui-ci peut aller trifouiller la mmoire RAM de l'ordinateur sans problme, et sans devoir passer par un intermdiaire. Pour rentrer dans les dtails scabreux, il faut savoir que les SPE possdent des instructions permettant de commander leur contrleur DMA et que c'est le seul moyen qu'ils ont pour rcuprer des informations depuis la mmoire. Et c'est au programmeur de grer tout a ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent xecuter. On peut considrer que notre processeur principal va dlguer des calculs effectuer aux SPE. Pour cela, notre processeur principal va simplement crire dans le local store du SPE, et va lui envoyer une demande lui ordonnant de commencer lexcution du programme qu'il vient d'crire.

Cluster Multithreding
Plus haut, j'avais dit qu'un cur n'tait rien d'autre qu'un processeur. Ce n'est pas tout fait faux, mais ce n'est pas tout fait vrai non plus : certaines architectures outrepassent lgrement cette rgle. Sur les processeurs prcdents, on avait rellement plusieurs curs spars, chacun avec se propres circuits rien qu' lui. Mais sur certains processeurs multicurs, ce n'est pas vraiment le cas : certains circuits sont communs et partags entre plusieurs curs. Cette technique consistant ne pas dupliquer certains circuits et en partager certains s'appelle le cluster multithreading . Cette technique est notamment utilise sur les processeurs FX-8150 et FX-8120 d'AMD, et quelques autres processeurs de la mme gamme. Ceux-ci sont bass sur l'architecture Bulldozer. Avec ces processeurs, tous les curs se partagent l'unit de calcul sur les nombres flottants (les nombres virgule).

www.siteduzero.com

Partie 2 : Multi-processeurs, Multicoeurs et Hyperthreading

33/97

Ce partage des circuits a une utilit : cela permet d'viter de dupliquer trop de circuits. Il est en effet vident qu'un seul circuit partag entre tous les curs prendra moins de place et utilisera moins de composants lctroniques que plusieurs circuits (un par cur). Le seul problmes, c'est que ce partage peut parfois se faire avec des pertes de performances, sur certains processeurs. Mais c'est un dtail : en choisissant bien les circuits partager, cela peut tre assez indolore.

Le partage des caches


C'est bien beau de mettre plusieurs processeurs sur une mme puce, mais il ne faut pas oublier ce qui arrive LA pice maitresse de tout processeur actuel : sa mmoire cache ! Et oui, encore une fois, nous allons parler de ce bon vieux cache. Parce que mine de rien, l'organisation des diffrents caches d'un processeur multicurs est lgrement plus complexe que prvu.

Caches ddis versus caches partags


Tout d'abord, dans le cas le plus simple, chaque cur possde sa propre mmoire cache rien que pour lui.

Sur d'autres microprocesseurs multicurs, la mmoire cache est partage entre les curs, qui peuvent tous les deux accder une seule et unique mmoire cache.

www.siteduzero.com

Partie 2 : Multi-processeurs, Multicoeurs et Hyperthreading

34/97

Ces deux mthodes ont des inconvnients et des avantages. Mais avant toute chose, il faut savoir que la quantit de mmoire cache que l'on peut placer sur une surface donne est limite : on ne peut pas mettre autant de cache que l'on veut dans un processeur. Et le cache prend une trs grande place dans notre processeur : environ la moiti, voire 60% des circuits de notre processeur servent intgrer la mmoire cache ! Si vous ne me croyez pas, voici un exemple avec un processeur dual-core Inrel core 2 duo. J'ai entour le cache en rouge.

Autant vous dire que le cache est une ressource prcieuse. Et cela pose un problme pour les architectures qui utilisent des caches spars pour chaque cur : ceux-ci seront individuellement assez petits. Alors qu'est-ce qui est le mieux : pleins de caches plus petits, ou un unique cache aussi gros que la somme de tous les caches spars ? Le principal dfaut des architectures cache ddis vient de l. Si on excute un programme prvu pour n'utiliser qu'un seul cur, celui-ci n'aura accs

www.siteduzero.com

Partie 2 : Multi-processeurs, Multicoeurs et Hyperthreading


qu' un seul cache : celui ddi au cur sur lequel il va sexcuter. Avec un cache partag, il aura accs une mmoire cache unique beaucoup plus grosse : on a moins de cache miss, et donc des performances qui s'envolent (vraiment !).

35/97

Et cela peut aussi se gnraliser avec plusieurs programmes : si un programme a besoin de beaucoup de cache et l'autre trs peu, on peut alors partager le cache de faon ce que chacun aie sa juste part, le programme gourmand pouvant utiliser autant de mmoire cache qu'il le peut. Avec des caches ddis, le cache utilisable par ce programme aurait t plus petit, et le programme aurait pu manquer de mmoire cache. Qui plus est, si une donne est utilise par deux curs et est prsente dans le cache, les processeurs caches partag ont un gros avantage. Sur un processeur cache spar, si une donne prsente dans plusieurs caches est modifie, la mise jour des autre version de la donne dans le cache des autres processeurs se fera en passant par la mmoire RAM comme intermdiaire. Vu la lenteur de la mmoire RAM, on se doute que les performances seront catastrophiques ! Avec les processeurs cache partag, il y a juste mettre jour la donne dans le cache unique, et puis c'est tout : pas de mise jour des donnes dans les caches des autres curs, ni rien d'autre. Par contre, les architectures cache partag ont aussi leurs problmes : plusieurs programmes utilisent le mme cache et peuvent se marcher sur les pieds. Rien nempche un programme d'utiliser une portion du cache dj utilise par un autre : l'autre programme devant alors recharger la donne stocke dedans en mmoire. Mais ce dfaut est assez rare en pratique, et on obtient souvent de meilleurs performances avec un cache partag qu'avec des caches ddis.

La ralit
Dans la ralit, il faut nuancer un tout petit peu les choses : un processeur multicurs ne contient pas qu'un seul cache, et on se retrouve avec une organisation assez hybride, dans laquelle certains caches sont partags et pas d'autres.

Gnralement, on trouve deux trois caches dans un processeur (multicurs ou non) : le L1, le L2, et le L3. Le L2 et le L3 sont souvent partags, tandis que le L1 n'est jamais partag ! Heu...pourquoi le cache L1 a droit ce genre de traitement de faveur ?

Trs simple : il est trop moche pour mriter d'tre partag. Oui, j'avoue, c'tait nul comme blague. La vraie raison tient dans le fait que ce cache doit avoir une latence trs faible, et que partager un cache n'est jamais vraiment innocent en terme de temps d'accs. Partager le cache L1 serait parfaitement possible, mais rendrait celui-ci tellement lent qu'on aurait l'impression que nos programmes tourneraient au ralenti. Par contre, rien nempche de partager les autres caches, comme le L2 ou le L3 sans trop pourrir leur temps d'accs.

www.siteduzero.com

Partie 2 : Multi-processeurs, Multicoeurs et Hyperthreading

36/97

On peut dcider de partager un cache entre tous les curs, voire limiter ce partage quelques curs particuliers pour des raisons de performances. Ainsi, rien nempche pour un processeur quad-core d'avoir deux caches L2, chacun partags avec deux curs, et le cache L3 partag entre tous les curs.

Communication inter-processeurs
Bon, c'est bien beau d'avoir plusieurs processeurs ou plusieurs coeurs, mais comment on fait pour les utiliser ?

Et oui, mine de rien, il faut bien trouver comment assigner un programme un processeur ou un cur en particulier ! Je vous dit tout de suite, cela est gr en partie par le systme d'exploitation de votre ordinateur, mme si le matriel a son mot dire. Pour expliquer comment notre systme d'exploitation se dbrouille pour lancer un programme sur un autre processeur ou un autre coeur, il va falloir expliquer une notion : celle d'interruption. Hol, c'est quoi une interruption ?

C'est une fonctionnalit de notre processeur qui va permettre darrter temporairement lexcution d'un programme pour en excuter un autre. Ces interruptions ont pour but d'interrompre lexcution d'un programme afin de ragir un vnement extrieur (matriel, erreur fatale dexcution d'un programme, demande faite par l'OS ou un programme...) et de la traiter en temps voulu, avant de rendre la main au programme interrompu. Notre interruption va donc devoir effectuer un petit traitement (ici, lancer un programme sur un processeur). Ce petit traitement est fait par un petit programme au auquel on a donn un nom technique : routine d'interruption. Lorsqu'un processeur doit excuter une interruption, celui-ci : arrte l'excution du programme en cours d'excution, excute la routine d'interruption, reprend l'excution du programme suspendu l ou il en tait.

www.siteduzero.com

Partie 2 : Multi-processeurs, Multicoeurs et Hyperthreading

37/97

Notre OS et nos pilotes fournissent toutes les routines d'interruptions de bases pour que notre matriel fonctionne : la majorit des programmes systmes sont des interruptions. V oici comment nos programmes applicatifs peuvent exploiter le matriel sans se fatiguer : en excutant l'interruption qui va bien. V ous voulez crire une donne sur le disque dur, un programme systme excutant des interruptions est fourni par votre OS. Dans notre cas, la gestion des programmes excuter en parallle se fera grand coup d'interruptions inter-processeurs . Ces interruptions inter-processeurs ne sont rien d'autre que des interruptions dclenches par un processeur ou un cur et envoyes vers un autre. Pour dmarrer un programme sur un autre processeur, il suffira d'envoyer une interruption vers cet autre processeur afin de le rveiller et faire en sorte que notre interruption initialise celui-ci correctement pour lancer le programme voulu. Et pour cela, il suffit juste d'crire une routine qui soit programme pour. C'est donc le systme d'exploitation qui fournira cette routine. Dans notre cas, ces interruptions seront dclenches par le programme paralllis : celui-ci dclenchera une interruption pour dmarrer un thread , au besoin.

L'exemple du x86
Gnralement, notre processeur ou notre carte mre incorpore un circuit qui s'occupe de grer les interruptions dclenches par le matriel et qu'on appelle le contrleur d'interruptions . Pour gnrer des interruptions inter-processeur, ce circuit doit tre adapt pour pouvoir rediriger des interruptions dclenches par un processeur vers un autre. Par exemple, nos anciens PC incorporaient sur leur cart mre un contrleur d'interruption cre par Intel qui se nomme le 8259A. Mais celui-ci ne grait pas les interruptions inter-processeurs. Pour grer cette situation, les carte mres multiprocesseurs devaient incorporer un contrleur spcial en complment. Celui-ci a t remplac par plusieurs autres contrleurs, des contrleurs APIC, plus volus et capables de grer les architectures multiprocesseur et multicurs. Pour simplifier, chaque processeur possde un local APIC , qui s'occupe de grer les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC , qui s'occupe de grer les interruptions en provenance des priphriques et de les redistribuer vers les local APIC . Ce IO-APIC s'occupe aussi de grer les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre.

www.siteduzero.com

Partie 2 : Multi-processeurs, Multicoeurs et Hyperthreading

38/97

Tous les local APIC et l'IO-APIC sont relis ensembles par un bus APIC spcialis, par lequel ils vont pouvoir communiquer et s'changer des demandes d'interruptions. On peut prciser quel est le processeur de destination en configurant certains registres du IO-APIC, afin que celui-ci redirige la demande d'interruption d'un local APIC vers celui slectionn. En gros, cela se fait avec l'aide d'un registre de 64 bits nomm l'Interrupt Command Register . Pour simplifier le mcanisme complet, chaque processeur se voit attribuer un Id au dmarrage qui permet de l'identifier (en fait, cet Id est attribu au local APIC de chaque processeur). Certains bits de ce registre permettent de prciser quel est le type de transfert : doit-on envoyer l'interruption au processeur metteur, tous les autres processeurs, un processeur particulier. Dans le dernier cas, certains bits du registre permettent de prciser l'Id du processeur qui va devoir recevoir l'interruption. A charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre.

www.siteduzero.com

Partie 3 : Le partage de la mmoire

39/97

Partie 3 : Le partage de la mmoire


Dans les chapitre prcdents, je n'ai pas arrt de dire que le partage de la mmoire posait de nombreux problmes qu'il fallait rsoudre d'une faon ou d'une autre. Mais j'ai volontairement pass sous le tapis ces problmes. Dans cette partie, nous allons voir en dtail quel genre de dsastres peuvent tre dus au partage de la mmoire.

Cohrence mmoire
Comme je l'ai dit dans ce tutoriel, partager la mmoire RAM entre plusieurs processeurs n'est pas une chose facile et pose de nombreux problmes. Et c'est maintenant que l'on va rentrer dans les vif du sujet et parler de ces fameux problmes en dtail. Lorsque ces threads doivent manipuler une mme donne, les ennuis commencent. Il faut savoir que dans de telles situations, la gestion des caches pose de sacrs problmes, capables de faire planter un programme en un rien de temps. Pour illustrer ces problmes, je vais commencer par introduire la situation par un petit exemple assez simple.

La cohrence : c'est quoi ?


Imaginons que deux processeurs manipulent une donne : ceux-ci ont une copie de la donne dans leur cache qu'ils manipulent et modifient loisir. Si un processeur modifie cette copie de la donne et que celle-ci est enregistre dans son cache ou en mmoire, elle sera alors diffrente de celle prsente dans le cache de l'autre processeur.

Ce qui fait qu'un processeur peut continuer manipuler une donne prime qui vient d'tre mise jour par l'autre processeur. C'est pas bon ! Il faut corriger a. D'autres situations peuvent faire en sorte que le contenu du cache devienne prim, et les critures dans une mmoire cache ne sont pas les seules poser problme. Derrire ce problme, se cache deux problmatiques assez complexes : pour commencer, il faut faire en sorte que notre processeur ne puisse pas lire une donne prime et faire en sorte d'avoir les bonnes valeurs dans la mmoire cache et/ou sa mmoire : on parle de cohrence mmoire ; et ensuite, il faut faire en sorte que nos critures ou lectures en mmoire (ou dans les caches) soient prises en compte dans le bon ordre, et s'occuper de faire les mises jour au bon moment : on parle de consistance mmoire. Pour simplifier, la cohrence porte sur la valeur de la donne, et se proccupe du contenu de la donne. Elle s'occupe du "quoi" mettre jour, et avec quelle valeur. La consistance s'occupe du "quand" mettre jour. Chacun de ces deux problmes impose des contraintes techniques distinctes. Nous allons commencer par la cohrence mmoire, qui est plus simple aborder. Un processeur doit toujours viter de se retrouver avec une donne prime et doit toujours avoir la valeur correcte dans ses

www.siteduzero.com

Partie 3 : Le partage de la mmoire

40/97

caches : il doit maintenir la cohrence des caches . Pour cela, la seule solution est d'utiliser des mcanismes permettant de faire en sorte que ce genre de cas n'arrivent pas.

Mcanismes de base
Lire des donnes est rarement un problme en terme de cohrence mmoire : ce sont surtout les critures qui vont tre la cause des divers problmes de cohrence mmoire. Ce sont les critures en mmoire RAM, ainsi que les critures dans la mmoire cache qui vont poser problme. On va commencer par regarder ce qui se passe lors d'une criture dans la mmoire cache.

Politique d'criture du cache


Si on crit dans la mmoire cache, il se peut que le contenu de la mmoire RAM ne soit pas jour. Cela peut poser quelques problmes : un processeur qui veut lire une donne depuis la mmoire n'aura pas la dernire version de la donne, vu que la mmoire n'a pas encore t mise jour.

C'est ce qui se passe avec les caches Write Back . Avec eux, le contenu de la mmoire n'est pas cohrent avec le contenu du cache. Avec les caches Write Back , le processeur crit dans le cache contenant la donne sans crire dans la mmoire RAM et dans les niveaux de caches infrieurs (par exemple, le L2 et le L3 sont des niveaux de caches infrieurs au L1, plus "proche" du

www.siteduzero.com

Partie 3 : Le partage de la mmoire

41/97

processeur) s'ils existent. On doit attendre que la donne soit efface du cache pour l'enregistrer en mmoire ou dans les niveaux de caches infrieurs (s'ils existent). Cela vite de nombreuses critures mmoires inutiles. les caches Write Back rendent plus difficile l'implantation de mcanismes de gestion de la cohrence des caches, et ne maintiennent pas la cohrence de la mmoire. C'est pourquoi il existe un autre type de cache : le cache Write Trought ; mieux adapt ce genre de situations.

Write Through
Avec les caches Write Through , toute donne crite dans le cache est crite en mme temps dans la mmoire RAM et dans les niveaux de caches infrieurs s'ils existent. Avec ces caches, l'implmentation des mcanismes permettant d'assurer la cohrence mmoire est plus facile : on est certain que la cohrence mmoire est assure dans le cas o une donne n'est prsente que dans le ou les caches attribus un seul processeur. Avant

Aprs

www.siteduzero.com

Partie 3 : Le partage de la mmoire

42/97

Le contenu de la mmoire est donc toujours le bon. Mais ce n'est pas forcment le cas pour les donnes stockes dans les mmoires caches des autres processeurs. La cohrence des caches n'est pas maintenue. Avant

Aprs

www.siteduzero.com

Partie 3 : Le partage de la mmoire

43/97

Ces caches ont aussi de nombreux dfauts en terme de performances. Ils ont notamment tendance commettre beaucoup d'critures dans la mmoire RAM, ce qui peut saturer le bus reliant le processeur la mmoire. Les performances peuvent s'en ressentir.

Caches partags
Les caches partags ne posent aucun problme de cohrence. Avec eux, une donne n'est pas duplique en plusieurs exemplaires, mais n'est prsente qu'une seule fois dans tout le cache. Ainsi, si on crit celle-ci, on peut tre sur que les autres processeurs liront la donne mise jour et non une ancienne version.

V ous remarquerez que sur le schma, la mmoire RAM contient encore une autre version de la donne (du moins, si on utilise un cache Write Back ). Mais cela ne pose pas de problme : les processeurs ne pourront pas accder la donne en RAM, et iront toujours chercher la donne dans le cache. Le cache est conu pour. Au final, notre processeur aura donc toujours accs la

www.siteduzero.com

Partie 3 : Le partage de la mmoire


dernire valeur crite par les autres processeurs.

44/97

Direct Memory Acces


Malheureusement, les mmoires caches ne sont pas vraiment les seules fautives dans ce genre de situation. D'autres phnomnes peuvent mettre le bazar. Le processeur n'est pas toujours le seul composant dans notre ordinateur capable d'aller crire dans la mmoire : certains priphriques peuvent ainsi aller directement lire ou crire dans la mmoire RAM. Pour ce faire, ces priphriques utilisent un circuit intermdiaire : le contrleur DMA. Pour les curieux, celui-ci est dtaill dans mon tutoriel sur le fonctionnement d'un ordinateur depuis zro, dans ce chapitre : Communication avec les entres-sorties. Pour simplifier, on va simplement dire qu'il s'agit d'un intermdiaire, qui permet aux priphriques d'aller lire ou crire en RAM. Quoiqu'il en soit, si un contrleur mmoire va crire en mmoire RAM, les modifications faites en mmoire ne seront pas rpercutes dans les mmoires caches. Et utiliser des caches Write Through n'y changera rien !

Pour rsoudre ce problme, on interdit de charger dans le cache des donnes stockes dans les zones de la mmoire qui peuvent tre modifies par des priphriques ou des contrleurs DMA. Toute lecture ou criture dans ces zones de mmoire ira donc directement dans la mmoire RAM, sans passer par la ou les mmoires caches.

Conclusion
Comme on l'a vu, on dispose de certaines solutions de base, comme partager les caches, utiliser des caches Write Trough , ne pas copier certaines donnes dans le cache, etc; qui peuvent fortement limiter la casse. Mais ces techniques de base ne rsolvent pas tout ! Les caches Write Trough permettent de maintenir la cohrence entre des niveaux de cache diffrents (L1-L2 ou cachemmoire, par exemple), mais ne peuvent rien pour les autres situations. On peut les utiliser pour maintenir la cohrence entre le cache et la mmoire, ou entre un cache L1 et un cache L2 associs un processeur. Mais ils ne servent rien si une donne est copie dans les caches L1 de deux processeurs diffrents. De mme, on ne peut pas partager tous les caches : partager le L1 n'est pas possible sans grosses contreparties que n'importe quel concepteur de processeur voudrait viter tout prix. Ces solutions sont donc limites.

Protocoles de cohrence des caches


Pour limiter la casse, on a invent d'autres mcanismes qui se chargent de dterminer quelles sont les donnes primes et de les remplacer par des donnes valides. Ces mcanismes mettent jour les donnes dans les diffrents caches en copiant les donnes mises jour d'un cache vers un autre, ce qui se fait parfois en se servant de la mmoire comme intermdiaire. Ces mcanismes sont implments dans diffrents circuits prsents dans nos processeurs ou sur nos cartes mres. Ces circuits vont fonctionner suivant un certain protocole, qui dcrira quand considrer qu'une donne est prime, comment changer les donnes entre caches, etc. Ce protocole est ce qu'on appelle un protocole de cohrence des caches . Il existe beaucoup de protocoles de cohrence des caches, comme vous pouvez en juger par cette liste non-exhaustive : MSI protocol ; MESI, aussi appel Illinois protocol ; MOSI ; MOESI ; MERSI ; MESIF ; write-once ; Synapse ; Berkeley ; Firefly and Dragon protocol ; etc.

Protocole MSI
Le plus simple de ces protocoles s'appelle le protocole MSI. Ce protocole se contente simplement de donner chaque donne

www.siteduzero.com

Partie 3 : Le partage de la mmoire

45/97

stocke dans le cache un tat, qui permet de savoir si cette donne est prsente dans les autres caches, si elle a t modifie, etc. Le protocole MSI dfinit trois tats : Modified ; Shared ; Invalid . V ous remarquerez que les initiales de ces tats ne sont rien d'autre que le nom du protocole. L'tat Shared correspond une donne jour : cet exemplaire de la donne est prsent dans tous les caches du processeur, et aussi en mmoire. L'tat Modified correspond une donne qu'on a modifie dans le cache. Cette donne est donc jour, mais les autres copies prsentes dans les autres caches du processeur ou en mmoire sont primes. Enfin, l'tat Invalid correspond au cas o la donne prsente dans le cache est prime : un autre processeur a mit jour l'exemplaire prsent dans son cache, et seul cet autre processeur a une donne valide (qui est dans l'tat Modified ). Quand on veut lire une donne devenue invalide, le processeur va lire la bonne donne en mmoire RAM ou dans un autre cache qui possde la bonne version de la donne. Une fois les donnes mises jour, la donne dans l'tat Modified (dans le cache du processeur qui a modifi la donne) et Invalid (dans les autres caches) repassent dans l'tat Shared . C'est seulement quand on veut lire une donne prime que la mise jour a lieu, pas avant. Retenez bien ce dtail pour la suite du tutoriel.

Fonctionnement

Quand un processeur veut aller lire dans le cache, il se retrouve face deux solutions : soit la donne dans le cache est dans l'tat Modified ou Shared , et le processeur peut l'utiliser sans problme ; soit elle est dans l'tat Invalid , et il devra aller rcuprer la bonne version en mmoire RAM ou dans un autre cache.

www.siteduzero.com

Partie 3 : Le partage de la mmoire


Pour l'criture, c'est autre chose. Quand le processeur crit dans une donne, on se retrouve face plusieurs cas : si la donne est dans l'tat Modified , rien ne change, elle reste dans cet tat ; si la donne est dans l'tat Shared , et elle passe donc dans l'tat Modified .

46/97

Protocole MESI
Ce protocole MSI n'est pas parfait. Par exemple, un tel protocole ne permet pas de savoir si une donne prsente dans un cache est prsente dans les autres caches. Ainsi, si on veut modifier cette donne, on doit prvenir tous les autres caches (ou un circuit qui est charg de grer la cohrence mmoire) qu'on vient de modifier cette donne. videmment, si aucun autre cache ne possde cette donne, on aura prvenu pour rien, et gaspill des communications entre caches ou entre cache et mmoire pour rien. Ces communications entre caches ne sont pas gratuites et en faire trop peut ralentir fortement notre ordinateur. Pour rgler ce problme, on a invent le protocole MESI. Ce protocole est assez connu, et pour cause : le premier processeur l'avoir utilis n'est autre que le processeur Pentium premier du nom ! Ce protocole a t utilis sur de nombreux processeurs, jusquaux processeurs Core d'Intel, et leurs concurrents d'AMD. Ce protocole MESI rajoute un tat supplmentaire : Exclusive. Exclusive est une sorte d'amlioration de Shared . Avec le Shared du protocole MSI, on ne sait pas si les autres caches possdent eux aussi une copie de la donne, ou si seul un seul cache utilise cette donne. Mais dans le protocole MESI, l'tat Shared du protocole MSI est scind en deux tats pour faire cette distinction : une donne sera dans l'tat Exclusive si les autres processeurs ne possdent pas de copie de la donne ; et sera dans l'tat Shared sinon. Avec cette distinction, on peut viter l'envoi de messages aux autres caches (ou aux circuits chargs de grer la cohrence des caches) si on crit dans une donne marque Exclusive : on sait que les autres caches ne possdent pas de copie de la donne, alors il ne sert rien de prvenir inutilement.

Fonctionnement
Cette fois-ci, je ne vais pas vous donner un schma pour illustrer le fonctionnement du protocole MESI : un tel schma serait illisible. Tout ce qu'il faut retenir, c'est que le fonctionnement du protocole MESI est identique au protocole MSI, avec quelques ajouts. Par exemple, si une donne est charge depuis la mmoire pour la premire fois dans un cache, elle passe soit en Exclusive (les autres caches ne contenaient pas la donne), soit en Shared (les autres caches en possdent une copie). Si on crit dans une donne marque Exclusive, elle passe en tat Modified . Une donne marque Exclusive peut devenir Shared si la donne est charge dans le cache d'un autre processeur. Et inversement, une donne Shared peut devenir Exclusive si tous les autres caches se dbarrassent de cette donne.

Protocole MOESI
Le protocole MESI est un protocole de cohrence des caches qui tait assez utilis dans pas mal de processeurs. Si je parle au pass, c'est simplement que d'autres protocoles sont utiliss sur les processeurs modernes. Sur les processeurs modernes, on utilise soit le protocole MOESI, soit un de ses collgues, comme le protocole MESIF. V ous devez surement vous poser une question : pourquoi ce protocole a-t-il remplac le protocole MESI ? V oyons cela par un exemple ! Imaginons qu'un processeur veuille obtenir la bonne version d'une donne. Il y a deux solutions : soit aller chercher la bonne version en mmoire, soit aller la chercher dans un autre cache. Si notre processeur ne dispose que de deux caches, la situation est trs simple : notre processeur va ainsi faire sa demande, et c'est soit la mmoire soit l'autre cache qui va rpondre. On peut ainsi optimiser nos protocoles MESI et MSI pour transfrer nos donnes entre les caches sans passer par la mmoire. Mais si on a plusieurs mmoires caches, des problmes peuvent survenir. Par exemple, si la donne est prsente dans plusieurs caches, tous les caches ayant la bonne version de la donne (la version Shared ou Modified ) vont rpondre en mme temps cette demande. Autant dire que grer une situation dans laquelle tous les caches se marchent sur les pieds est loin d'tre facile ! Seule solution : ne pas aller chercher la donne dans les autres caches et passer uniquement par la mmoire. Cela ncessite de maintenir la mmoire jour, et donc d'aller y crire souvent : chaque fois qu'une donne passe dans l'tat Modified , on doit l'crire dans la mmoire. Les mises jour de donnes Invalid se font donc via la mmoire RAM. Vu que la mmoire RAM est au minimum 20 fois plus lente que la mmoire cache, les performances s'effondrent et votre processeur devient aussi rapide qu'une antiquit du style 486DX.

www.siteduzero.com

Partie 3 : Le partage de la mmoire

47/97

Solution du MOESI
Pour viter ce gros problme, le protocole MOESI va simplement rajouter un tat supplmentaire pour nos donnes : l'tat Owned . Si un processeur crit dans son cache, il mettra sa donne en Owned . Les autres caches se mettront alors jour, et passerons leur donne en version Modified , voire Shared une fois la mmoire mise jour. Ainsi, seul un seul processeur pourra avoir une donne dans l'tat Owned la fois. Avec MOESI, seul le processeur qui possde la donne en Owned va rpondre cette demande (ceux possdant la donne Shared ne rpondant pas la demande formule par le processeur demandeur), vitant ainsi d'aller chercher la donne jour en mmoire. Cela a un autre avantage : on n'est pas oblig de maintenir la mmoire jour. Ainsi, lorsqu'une donne passe en tat Modified , il n'y a pas besoin d'aller crire en mmoire RAM la bonne version de la donne. On diminue fortement le nombre d'critures en mmoire. Les performances s'envolent !

Implmentation
Il existe deux grands types de protocoles utilises pour maintenir la cohrence des caches : les snooping protocols, dans lesquels chaque donne contient des informations pour savoir si elle est partage entre plusieurs processeurs ; et les directory protocols, qui stockent des informations sur chaque donne, pour savoir si elle est partage ou non : ces informations tant stockes dans un circuit spcialis qu'on appelle le directory.

Directory Protocol
Notre mmoire cache est, comme toutes les autres mmoires, divises en cases mmoires, qu'on peut modifier individuellement. Dans un cache, ces cases mmoires sont regroupes en blocs de taille fixe qu'on appelle des lignes de cache. Gnralement, ces blocs ont une taille assez grosse compar aux cases mmoires : cela peut varier de 64 256 octets. Avec un directory protocol , on utilise un circuit spcial qui contient des informations sur toutes les lignes de caches prsentes dans notre processeur. Ce gros circuit, le directory, sait donc quelle est la ligne de cache qui a la dernire version valide d'une donne prsente dans plusieurs caches, si telle ligne de cache contient une donne prime, si telle ligne de cache est prsente dans plusieurs caches, etc. Ces informations sur l'tat des lignes de caches sont souvent stockes en mmoire RAM : notre directory est ainsi fortement reli la mmoire RAM, voire totalement implant dedans. Ce directory est mis jour chaque fois qu'un processeur crit dans sa mmoire cache : dans ce cas, le processeur va mettre jour le directory automatiquement. Ces protocoles sont surtout utiliss sur les architectures distribues : ils sont en effet difficiles implmenter, tandis que leurs concurrents sont plus simples implanter sur les machines mmoire partage.

Snooping protocols
Ces concurrents sont ce qu'on appelle les Snooping protocols . En soit, nos Snooping protocols n'ont rien de bien compliqu : chaque ligne de cache contient des bits qui permettent de savoir si cette ligne de cache contient une donne prime ou si la ligne de cache est valide. Quand on veut lire une donne, les circuits chargs de lire dans le cache vont vrifier ces bits. si ces bits disent que la ligne de cache est valide, les circuits grant le cache vont autoriser la lecture de la ligne de cache, sans rien faire de spcial. Par contre, si la ligne de cache est prime, on va alors lire les donnes depuis la RAM, et remplacer les donnes pourries par la valeur correcte. La mise jour de ces bits se fait lorsqu'un processeur doit aller crire dans le cache. Plus prcisment, nos caches sont interconnects entre eux, afin de maintenir la cohrence. Si un cache contient une donne partage, ce cache devra prvenir tous les autres caches quand on crira dedans. A chaque criture dans une donne partage, il devra donc prvenir ses collgues qui se chargeront alors de mettre jour la donne partage s'ils en possdent une copie. Gnralement, cette communication est facilement implmentable sur les processeurs actuels. La raison est trs simple : tous les caches sont relis la mmoire via un bus, une srie de fils qui permet de transfrer des donnes entre le cache et la mmoire. Sur nos ordinateurs actuels, ce bus est partag entre tous les caches.

www.siteduzero.com

Partie 3 : Le partage de la mmoire

48/97

On peut donc faire passer les changes dinformations entre caches via ce bus sans aucun problme. On rutilise l'existant pour mettre en uvre notre protocole de cohrence des caches, sans devoir crer des circuits supplmentaires. V ous remarquerez que c'est un avantage certain qu'on les snooping protocols sur les directory protocols. A chaque fois qu'une information est transfre sur ce bus, les diffrents caches vont regarder ce qui est transmis sur ce bus, et vont mettre jour les informations sur les lignes de cache, voire les donnes de celle-ci, jour.

Cohrence entre diffrents niveaux de caches


Sur les processeurs actuels, on trouve toute une hirarchie de mmoires caches, certains tant partags, et d'autres non ; certains utilisent le Write Trough , et d'autres les Write Back , etc. Mais le plus important est que ces caches ne sont pas tous relis au bus mmoire. Par exemple, sur un processeur contenant un cache L1 et un cache L2, le cache L1 n'est pas reli la mmoire : c'est le cache L2, le plus proche de la mmoire, qui l'est. On peut donc utiliser un Snooping Protocol pour le L2, mais pas pour le L1. Une solution est de faire en sorte que les caches L1 soient de type " Write Trough " (ou presque) : toute criture dans le L1 doit tre rpercute dans le L2. Il existe bien sr d'autres moyens, mais passons ceux-ci sous silence. Et ne parlons pas des processeurs utilisant des caches L1, L2 et L3, voire L4... Bref, maintenir la cohrence entre tous ces caches est un sacr challenge.

Write Invalidation et Write Broadcasting


Pour compliquer un peu les choses, cette mise jour se fait diffremment suivant les processeurs : soit on utilise des techniques de Write Invalidation ; soit on utilise le Write Update. Ah, je crois que quelques explications s'imposent, non ? Bon, alors pour rsumer, la premire mthode, la Write Invalidation est celle qui est utilise dans les protocoles qu'on a vu prcdemment : quand on crit une donne dans un cache, toutes les autres versions de cette donne, prsentes dans les autres caches sont invalides, considres comme primes. Lorsque l'on cherche lire une donne prime, on ne peut pas accder au cache, et la bonne version de la donne doit tre transfre depuis la mmoire ou depuis d'autres caches. Avec le Write Update, toute criture dans un cache est propage, et les autres caches sont mis jour automatiquement. La diffrence, c'est que les donnes dans les autres caches sont modifies automatiquement, mme si le processeur ne cherche pas les lire. Avec la Write Invalidation , la mise jour ne commence que lorsque l'on veut lire une donne prime. Avec le Write Update, on met jour le plus tt possible, avant mme toute tentative de lecture. Comme vous pouvez le voir, crire dans des donnes partages et utilises par plusieurs Threads peut poser de lourds

www.siteduzero.com

Partie 3 : Le partage de la mmoire

49/97

problmes, qui peuvent ruiner les performances assez rapidement. La cohrence des caches est quelques chose de compliqu, qui peut parfois tre une cause majeure de ralentissements dans des applications paralllises sur plusieurs curs /processeurs. N'oubliez pas ce genre de choses quand vous programmez, messieurs !

www.siteduzero.com

Partie 3 : Le partage de la mmoire

50/97

Consistance mmoire
Excuter plusieurs threads sur plusieurs processeurs peut avoir des consquences assez fcheuses, comme je me plait vous le rpter depuis le dbut de ce tutoriel, et on a dj eu un petit aperu de ce que cela pouvait donner dans le chapitre prcdent. On y avait vu que dans certains cas, les oprations de lecture en mmoire effectues par notre processeur pouvaient renvoyer une valeur prime. Ces lectures foireuses taient dues la prsence de mmoires caches, et disparaissaient ds qu'on pouvait maintenir la cohrence de la mmoire. Mais la fte n'est pas finie : ces lectures foireuses, qui ne donnent pas le bon rsultat sont encore possibles, mme avec des mcanismes de cohrence des caches parfaits ! En effet, maintenir la cohrence de la mmoire n'est pas une solution miracle : les caches ne sont pas les seuls fautifs. En effet, il faut aussi maintenir la consistance mmoire.

Problmes
Pour viter tout problme quand on excute un programme, on doit maintenir la consistance de la mmoire. Intuitivement, la consistance mmoire signifie que chaque lecture d'une donne en mmoire doit renvoyer la dernire valeur de la donne qui a t crite. En clair, notre processeur ne doit pas lire de donne prime. H, une minute : on n'a pas dj rgl de problme dans le chapitre prcdent, en parlant des protocoles de cohrence des caches ?

Et bien non ! On a rgl les problmes dus aux mmoires caches, mais d'autres phnomnes peuvent faire en sorte que des lectures en mmoires renvoient des donnes primes. La cohrence des caches n'est qu'une facette du problme et n'est donc pas une solution miracle. Il faudra aussi rsoudre d'autres problmes plus ou moins varis. V oyons un peu lesquels.

Accs simultans
Premier dtail assez important : les accs mmoire dans les donnes utilises par plusieurs processeurs doivent tre effectus uns par uns. En principe, rien ne lempche. Il faut dire qu'une lecture ou qu'une criture en mmoire RAM peut prendre un temps assez long et parfois variable suivant l'opration. Et si on ne fait rien contre, il est parfaitement possible qu'une nouvelle opration de lecture ou d'criture dmarre avant que la lecture ou criture prcdente soit termine. V ous pouvez ainsi avoir un processeur qui lit une donne en mme temps qu'un autre tente de l'crire, ou deux processeurs qui tentent d'effectuer des critures simultanes. En clair : a fait des Chocapic ! Dans ce cas, le rsultat des lectures et critures peut fortement varier suivant l'tat et le fonctionnement du bus mmoire, de la mmoire, de son contrleur, et de l'age du capitaine. Cela peut crer des tas de situations bizarres. Par exemple, imaginez qu'on lance une criture, suivie d'une lecture, la lecture commenant avant que l'criture prcdente soit termine. Dans ce cas, rien nempche la lecture de finir avant l'criture : notre lecture peut alors renvoyer l'ancienne valeur, celle qu'on trouvait en mmoire avant l'criture. On dmarre donc une lecture aprs une criture, et tout se passe comme si l'criture avait eu lieu aprs la lecture...

Et on peut renouveler ce genre d'exprience avec des critures dmarres en srie : l'ordre des critures peut s'inverser : l'criture la plus rcente finissant avant l'autre. Dans ce genre de cas, la consistance mmoire n'est pas respecte : une lecture ne renverra pas la dernire valeur crite en mmoire, mais une ancienne valeur prime, datant d'avant une criture antrieure. Il faut absolument trouver un moyen pour que les lectures et critures dans les donnes partages soient effectues unes par unes et ne puissent pas tre interrompues. Pour cela, des spcialistes et des chercheurs ont cherchs des solutions, et on trouv quelques solutions intressantes. Il existe en effet des solutions matrielles pour faire en sorte que nos accs une donne partage se fassent les uns aprs les autres, dans l'ordre, et pas simultanment. Ces fameuses solutions matrielles sont ce qu'on appelle des instructions atomiques .

www.siteduzero.com

Partie 3 : Le partage de la mmoire

51/97

Ces instructions sont des instructions qu'on ne peut pas interrompre. Chaque instruction atomique qui doit aller lire ou crire en mmoire va ainsi rquisitionner la mmoire pour elle toute seule et empchera toute lecture ou modification de la mmoire tant qu'elle ne se sera pas termine. L'instruction sera ainsi la seule accder la mmoire et toute autre instruction voulant y accder pendant ce temps devra alors mise en attente. De telles instructions suffisent garantir que nos critures et lectures sexcutent l'une aprs l'autre. Mais elles ne garantissent pas que l'ordre correct des oprations d'accs mmoire sera respect...

Memory Ordering
Sur les architectures avec un seul processeur (on passe sous le tapis les techniques d'Hyper-Threading, Coarse Grained Multithreading , etc), on s'attend forcment ce que les lectures renvoient obligatoirement la dernire donne crite en mmoire. En effet, notre processeur excutera les instructions d'un programme une par une. Si une criture prcde une lecture, on est certain que la lecture renverra la bonne valeur vu qu'elle est cense sexcuter aprs l'criture. Du moins, c'est la thorie : les processeur modernes n'hsitent pas modifier l'ordre dexcution des instructions histoire de gagner en efficacit. Il est ainsi parfaitement possible pour notre processeur de changer l'ordre dexcution des instructions d'accs mmoire : il peut placer une lecture avant une criture, une lecture avant une lecture, une criture aprs une autre, etc. Mais notre processeur doit se dbrouiller pour que cela ne change pas le comportement du programme. Il dispose pour cela de techniques de Memory Disambiguation assez compliques. Mais avec plusieurs processeurs, cela se complique. Comme je l'ai dit plus haut, rien nempche nos processeurs de changer euxmme l'ordre dexcution des accs mmoire. Et chaque processeur fait cela chacun dans son coin, sans se proccuper de ce que peuvent faire les autres processeurs. Les oprations d'criture venant d'un processeur peuvent tre mises dans un dsordre complet, et les autres processeur qui liront ces donnes crites en mmoire auront droit des donnes mises jour dans le dsordre. Bref, on est face un lger problme. Ces lectures effectues par les autres processeur vont ainsi renvoyer de vielles donnes cause d'un mauvais ordonnancement des instructions d'accs mmoires. En clair : notre lecture ne renvoie pas le mme rsultat, mme si les mcanismes de cohrence des caches ont fait leur travail ! La cause : les divers mcanismes de Memory Disambiguation , qui fonctionnent sur un seul processeur, mais pas avec plusieurs. Bref, on doit faire quelque chose.

Modles de consistance
On a vu plus haut que le rordonnancent des instructions effectu par notre processeur peut poser de graves problmes, et que mettre les instructions dans le bon ordre est une ncessit. On doit donc rsoudre un problme : comment faire en sorte que nos accs mmoires effectus ou non dans le "dsordre" ne posent pas de problmes ? Toute la problmatique est l : une lecture doit retourner la dernire valeur crite dans l'ordre du programme. Il y a plusieurs solutions ce problme. Ces solutions sont ce qu'on appelle des modles de consistance mmoire.

Squential Consistency
Le plus simple de ces modles de consistance s'appelle la sequential consistency . Il est trs simple comprendre : aucune rorganisation de l'ordre des instructions n'est possible. Un processeur doit imprativement excuter ses instructions dans l'ordre impos par le programme. Pas de possibilit de rorganisation, rien. Tout se passe comme si les instructions de chaque programme s'effectuaient dans l'ordre impos par celui-ci, et que les lectures ou critures des diffrents processeurs taient excutes les unes aprs les autres. Les consquences sont directes : pas d'utilisation de techniques de Memory Disambiguation . Est-ce un problme ? Oui : ce modle de consistance est vraiment strict. Trop mme : il interdit de nombreux cas de rorganisation qui seraient pourtant parfaitement lgaux. Aprs tout, toutes les rorganisations d'accs mmoire ne sont pas problmatiques, mme avec plusieurs processeurs. Cela empche de nombreuses optimisations matrielles et limite grandement les possibilits d'optimisation des compilateurs. V otre processeur et votre compilateur ne peuvent plus changer l'ordre des instructions d'accs mmoire. Par exemple, il devient alors beaucoup plus difficile dexcuter des lectures en avance, pour recouvrir le temps d'accs au cache ou la mmoire par des instructions de calcul. En clair : votre programme fonctionne parfaitement bien, mais il est plus lent qu'il ne le devrait. Pour tre franc, aucun processeur actuel n'utilise ce modle de consistance. L'impossibilit d'effectuer des rorganisations et optimisations lgales sur l'ordre des accs mmoire est proprement rdhibitoire en terme de performances. Et de nos jours, il en faut de la performance pour faire tourner des programmes de plus en plus programms avec les pieds respectueux de la Loi de Wirth.

Relaxed Consistency
Pour permettre ces optimisations lgales, d'autres modles de consistances ont t invents. Ceux-ci permettent certaines

www.siteduzero.com

Partie 3 : Le partage de la mmoire

52/97

rorganisations non-dangereuses. Par exemple, rien nempche d'effectuer une lecture avant une criture si jamais ces deux instructions manipulent des endroits trs diffrents de la mmoire : les deux donnes tant indpendantes, cette rorganisation ne pose aucun problme. Pareil pour deux lectures effectues sur des donnes indpendantes : l'ordre des lectures ne changera rien. Divers modles de consistance existent donc, qui permettent d'autoriser ou d'interdire certaines rorganisation, et qui autorisent certaines instructions ne pas tre atomiques. En fait, c'est limite s'il n'existe pas un modle de consistance pour chaque jeu d'instruction existant au monde. Autant dire que tous les prsenter serait un peu long. Mais on peut grosso-modo classer ces modles de consistance en trois grandes catgories.

Total Store Ordering


Dans le cas le plus strict, une donne ne peut pas tre rutilise tant que la donne n'a pas finie d'tre crite en mmoire et/ou mise jour dans le cache des autres processeurs. C'est exactement ce qu'on attend de la sequential consistency. Mais cette ordre peut tre un peu relch. Un processeur peut ainsi rutiliser une donne qu'il vient d'crire dans son cache avant que cette donne soit enregistre en mmoire ou dans les autres caches. Tout se passe donc comme si l'criture en question n'tait pas in-interruptible. Et oui, on peut parfaitement lire la donne crite avant mme que l'criture soit termine et que toutes les versions de la donne soient mises jour. Mine de rien, c'est ce qui tait fait dans les protocoles de cohrence des caches qu'on a vus prcdemment : le statut modified nempchait pas les lectures de la donne. Je ne vous cache pas que c'est exactement ce qui se passe avec la majorit des modles de consistances plus laxistes, dont ceux implants dans nos processeurs actuels.

Partial Store Ordering


L'autre possibilit est de permettre des critures simultanes, ainsi que des rorganisations de l'ordre des critures quand ces critures manipulent des donnes diffrentes. Ainsi, on peut dmarrer une nouvelle criture avant que les critures prcdentes ne soient termines. Au lieu d'attendre que toutes les critures prcdentes se terminent, et faire comme si les critures taient ininterruptibles, on peut dmarrer une criture pendant qu'une autre est en attente ou manipule la mmoire. La seule condition pour viter les catastrophe est que ces critures manipulent des donnes diffrentes, places des endroits diffrents de la mmoire.

No Limit
Enfin, certains processeurs vont encore plus loin : ils autorisent toutes les rorganisations possibles entre lectures et critures, tant qu'elle se font sur des donnes diffrentes. Simple, rapide, et efficace. De mme, ce qui a t fait plus haut pour l'criture est alors possible pour les lectures : on peut parfaitement dmarrer une autre lecture pendant que d'autres lectures sont en attente ou en cours. Difficile de faire plus laxiste comme modle de consistance. Et bien sachez qu'on peut faire encore plus pire : rien nempche de pouvoir modifier l'ordre des lectures effectues sur une donne. Ainsi, si deux lectures se suivent et qu'il n'y a pas d'critures entre les deux, on peut effectuer ces deux lectures dans l'ordre qu'on veut.

Exemples
Aprs ces histoires de thorie pure et simple, passons maintenant la pratique. Dans cette partie, on va voir les modles de consistances utiliss sur quelques processeurs plus ou moins grand public. Nous allons donc voir l'exemple des processeurs x86, ceux qu'on retrouve dans nos PC actuels. Le modle de consistance des processeurs x86 a vari au cours de l'existence de l'architecture : un vulgaire 486DX n'a pas le mme modle de consistance qu'un Core 2 duo, par exemple. Quoiqu'il en soit, les modles de consistance des processeurs x86 ont toujours ts assez forts, avec pas mal de restrictions. Si on compare aux autres processeurs, le x86 est assez strict. Bref, le premier modle de consistance utilis sur les processeurs x86 est apparu sur les premiers processeurs x86 et est rest en place sur tous les processeurs de marque Pentium. Ce modle est assez simple : hormis une exception, tout doit se passer comme si le processeur accdait la mmoire dans l'ordre des oprations de notre programme. Cette exception concerne les lectures : dans certains cas, on peut les excuter avant certaines critures. Nanmoins, cela ncessite quelques conditions : ces critures doivent se faire dans la mmoire cache ; la lecture doit se faire dans la mmoire ; nos critures doivent aller crire des adresses diffrentes de l'adresse accde en lecture ; aucune transaction avec un priphrique ne doit tre en cours. En clair, ce modle est vraiment strict !

www.siteduzero.com

Partie 3 : Le partage de la mmoire

53/97

Sur les processeurs partir du Pentium 4, les choses changent. Le Pentium 4 est en effet le premier processeur implmenter des techniques permettant dexcuter plusieurs processus en parallle. Ce processeur est en effet le premier utiliser l'Hyperthreading. En consquence, le modle de consistance a du tre assoupli pour viter de perdre btement en performance. V oici un rsum de ce modle de consistance : une lecture ne peut pas tre dplace avant ou aprs une autre lecture ; une criture ne peut pas tre dplace avant une lecture ; part pour quelques exceptions, l'ordre des critures dans la mmoire ne change pas : une criture dans la mmoire ne peut pas tre dplace avant ou aprs une autre criture ; on ne peut pas dplacer une criture ou une lecture avant ou aprs une instruction atomique, ainsi que quelques instructions supplmentaires (celles qui accdent aux priphriques ou aux entres-sorties, notamment) ; des lectures peuvent tre dplaces avant des critures, si ces critures et la lecture ne se font pas au mme endroit, la mme adresse. Dans cette liste, j'ai mentionn le fait que les critures en mmoire peuvent changer dans certains cas exceptionnels. Ces cas exceptionnels sont les critures effectues par les instructions de gestion de tableaux et de chaines de caractres, ainsi que certaines instructions SSE. Ces instructions SSE sont les instructions qui permettent d'crire des donnes sans passer par le cache, mentionnes il y a quelques chapitres. Ce sont donc les instructions MOVNTI, MOVNTQ , MOVNTDQ , MOVNTPS, MOVNTPD. Mais ce ne sont pas les seules : le x86 possde quelques instructions permettant de travailler directement sur des chaines de caractres ou des tableaux : ce sont les instruction REPMOVSD, REPSCACB, et bien d'autres encore. Et bien sachez que les critures effectues dans ces instructions peuvent se faire dans un dsordre complet (ou presque).

Fences et Memory Barrier


Plus haut, j'ai parl des modles de consistance qui autorisent toute rorganisation sur des donnes non-partages entre processeurs. J'ai dit plus haut que les rorganisations des lectures et critures sont autorises, mme celles qui sont censes poser des problmes de cohrence mmoire. Aprs tout, le modle de consistance relax vu prcdemment n'est rien d'autre que le modle qui posait problme au dpart : on a toujours des Store Queue, des caches non-bloquants, et des tas d'autres optimisations matrielles qui posent des problmes de cohrence/consistance. Comment nos processeurs font pour garantir que les accs des donnes partages vont se drouler correctement ?

Trs simple : le processeur fourni quelques instructions de base pour viter tout problme. Ces instructions spciales sont ce qu'on appelle des Memory Barriers, aussi appeles des Fences. Il en existe diverses catgories, aussi quelques prcisions sont les bienvenues.

Full Fence
La premire catgorie de Fence s'appelle les Full Fences. Ces Full Fences sont des instructions qui vont ordonner au processeur de terminer toutes les critures et/ou lectures qui la prcde avant de passer l'instruction suivante. Avec a, il est impossible dexcuter en avance les instructions place aprs une Full Fence. De mme, les instructions places aprs la Full Fence ne peuvent pas tre excutes aprs elles. Cela supprime donc toute possibilit de rorganisation entre les instructions places avant la Full Fence et celles places aprs.

www.siteduzero.com

Partie 3 : Le partage de la mmoire

54/97

Tous les processeurs n'implmentent pas de telles Full Fence . Il s'agit en effet d'un luxe que tous ne peuvent pas s'offrir. Pour donner quelques exemples, les processeurs x86 qu'on trouve dans nos PC possdent de telles Full Fence : il s'agit de l'instruction MFENCE. Par contre, ces Full Fence n'existent pas sur les processeurs Itanium.

Load/Store Fences
Pour plus d'efficacit, certains processeurs procdent autrement : ils possdent deux Fences bien distincts. Il existe ainsi un Fences en charge des lectures, et un autre en charge des critures. On peut ainsi dcider de terminer toutes les critures places avant la Fence avec une instruction, tandis que la terminaison des lectures sera effectue par une autre Fence. Cela a ses avantages dans certaines situations. Par exemple, imaginons la situation suivante : on charge l'adresse mmoire d'une donne depuis la mmoire dans un registre, puis on cherche copier la donne de cette adresse dans un autre registre. C'est parfaitement possible : notre processeur va d'abord charger l'adresse, puis il utilisera une instruction qui ira lire l'adresse indique dans le registre. Dans un cas pareil, on doit charger l'adresse avant la donne. Mais si le processeur est autoris dplacer une lecture avant une autre, le chargement de la donne peut alors dmarrer avant qu'on connaisse l'adresse... Pour faire en sorte que ces deux lectures (adresse et donnes) se fassent dans le bon ordre, on doit utiliser une Fence spcialise dans les lectures. Petite remarque : sachez qu'il est assez rare qu'un processeur change l'ordre des lectures. Il n'existe qu'un seul exemple de processeur connu qui puisse le faire : il s'agit du processeurs DEC Alpha, un processeur particulirement innovant pour son poque, qui neut pas le succs qui lui tait du. Certains processeurs utilisent un nombre de Fences plus lev, et peuvent ainsi avoir pleins de Fences, chacune ddie des cas particuliers. Les processeurs x86 ne sont pas dans ce cas : ils utilisent trois Fences, dont la fameuse Full Fence MFENCE, et deux Fences spcialises. On trouve donc une Fence pour les lectures : LFENCE, et une autre pour les criture : SFENCE.

Et pour le programmeur ?
Mais qui se charge de placer ces Fences au bon endroit ?

Et bien il s'agit d'un subtil mlange entre programmeur et compilateur.

www.siteduzero.com

Partie 3 : Le partage de la mmoire

55/97

Gnralement, un compilateur peut placer ces Fences au bon endroit, avec un peu d'aide du programmeur. Mais les compilateurs ne le font pas forcment. En effet, un programmeur n'a gnralement aucun moyen de dire son compilateur qu'il faut placer une Fence tel ou tel endroit. Toutefois, dans certains langages de programmation, on peut dire au compilateur qu'une donne doit toujours tre lue depuis la mmoire RAM. C'est trs utile pour prciser que cette donne est potentiellement partage par plusieurs processeurs ou manipulable par des priphriques. Cela peut se faire en dclarant des variables en volatile. Dans certains cas, les compilateurs peuvent placer des Fences lors des lectures ou critures sur ces variables. Mais ce n'est pas une obligation. En changeant de compilateur, on peut se retrouver avec des Fences manquantes, voire pas de Fences du tout. Dans ces cas l, le programmeur doive manipuler explicitement des Fences. Premire solution qui vient l'esprit : utiliser l'assembleur, un langage de programmation dans lequel on utilise directement les instructions du processeur. Cela arrive assez rarement, mais a existe. Seul problme : le programme obtenu sera spcialis pour un seul jeu d'instruction, et sera difficilement portable sur d'autres processeurs. Pour limiter la casse, certains systmes d'exploitations ou compilateurs peuvent aussi fournir des Fences explicites, encapsules dans des bibliothques ou caches dans certaines fonctions. Bref, si vous tes programmeurs, renseignez-vous un peu plus sur ces histoires. Et voil, vous avez donc un aperu des grandes classes de modles de consistance existants. Mais ce n'est qu'un aperu qui cache normment de choses : on n'a pas parl des spcificits de chaque processeur. Par exemple, vous ne savez pas quelles sont les diffrences entre le modle des processeurs x86 et celui des processeurs PowerPc. Et pourtant, il y en a des diffrences entre ces deux modles. Bref, je ne peux que vous conseiller de regarder les documentations techniques de ces processeurs, histoire de vous renseigner sur le sujet. Allez sur les sites des diffrents constructeurs de processeurs, et cherchez un peu si a vous intresse. Les curieux qui veulent se documenter sur le sujet d'une faon un peu plus gnrale auront vraiment intrt lire ce tutoriel suivant, qui parle en dtail de ces mcanismes de consistance mmoire : Shared Memory Consistency Models. De plus, la lecture de ce fichier .pdf sur les Fences pourra vous tre utile : Memory Barriers : a hardware view For Software Hacker's.

www.siteduzero.com

Partie 3 : Le partage de la mmoire

56/97

Synchronisation entre Threads


Comme je l'ai dit dans les chapitres prcdents, il n'est pas rare que plusieurs instructions appartenant des threads diffrents veuillent accder une donne simultanment. On a vu dans le chapitre prcdent comment viter que deux threads accdent simultanment une donne : il suffit dexcuter nos instructions les unes aprs les autres et interdire les accs simultans. Pour cela, on utilise des instructions atomiques. cela suffit maintenir la cohrence et la consistance de notre mmoire. Mais cela ne rsout pas tout. Au niveau logiciel, d'autres contraintes peuvent apparaitre. Et les Fences et autres instructions atomiques peuvent tre d'une grande aide dans ce genre de cas.

Sections Critiques
Dans les chapitres prcdents, on a appris faire en sorte que des instructions individuelles puissent ne pas tre interrompues. Chose assez importante s'il en est. Le seul problme, c'est que notre thread ne peut pas faire tout ce qu'il veut sur une donne partage en une seule instruction. Il va devoir utiliser plusieurs instructions successives sur la donne pour pouvoir en faire ce qu'il veut. Et comme toujours, cela pose encore une fois des problmes (vous devez en avoir marre de voir cette phrase...). Prenons un exemple : on utilise une donne partage entre plusieurs threads, chaque thread sexcutant sur un processeur x86. Notre donne est un vulgaire nombre entier. Chaque thread veut l'augmenter de 1 rgulirement. Seul problme, augmenter de 1 la donne n'est pas effectue en une seule instruction sur les processeurs x86. Il faut en effet lire la donne, l'augmenter de 1, puis l'crire. Et si deux processeurs veulent augmenter simultanment cette donne, on court la catastrophe. Chaque thread peut tre interrompu n'importe quel moment par un autre processeur qui voudra modifier sa donne. Les instructions de nos threads sexcuteront en srie, comme le modle de consistance nous l'impose, mais le processeur peut parfaitement tre drang par un autre processeur entre deux instructions. Dans notre exemple, voici ce que cela donnerait.

On a donc une valeur de dpart de 5, qui est augmente de 1 deux fois, ce qui donne au final 6... Nos processeurs sont pas foutus de faire des calculs tellement basiques qu'on pourrait les laisser des enfants de 5 ans ! Bon, blague part, pour avoir le bon rsultat il y a une seule et unique solution : le premier processeur doit avoir un accs exclusif la donne partage. Sans cela, l'autre processeur ira lire une version de la donne qui n'aura pas encore t modifie par l'autre processeur. Dans notre exemple, un seul thread doit pouvoir manipuler notre compteur la fois. Et bien sr, cette rponse peut, et doit se gnraliser presque toutes les autres situations impliquant une donne partage. Chaque thread doit donc avoir un accs exclusif notre donne partage, sans qu'aucun autre thread ne puisse manipuler notre donne. On doit donc dfinir ce qu'on appelle une section critique : un morceau de temps durant lequel un thread aura un accs exclusif une donne partage : notre thread est certain qu'aucun autre thread n'ira modifier la donne qu'il manipule durant ce temps.

Exclusion mutuelle
www.siteduzero.com

Partie 3 : Le partage de la mmoire

57/97

Autant prvenir tout de suite : crer de telles sections critiques se base sur des mcanismes mlant le matriel et le logiciel. Il existe deux grandes solutions, qui peuvent tre soit codes sous la forme de programmes, soit implantes directement dans le silicium de nos processeurs. V oyons la premire de ces solutions : l'exclusion mutuelle. Avec celle-ci, on fait en sorte qu'un seul thread puisse accder notre donne partage. Un thread qui veut manipuler cette donne va donc attendre qu'elle soit libre pour la rserver afin de l'utiliser, et la librera une fois qu'il en a fini avec elle. Si la donne est occupe par un thread , tous les autres threads devront attendre leur tour. Pour mettre en uvre cette rservation/d-rservation, on va devoir ajouter quelque chose chaque donne partager. Dans le cas le plus simple, ce quelque chose sera un simple compteur, crit en mmoire cot de la donne, qui indiquera si la donne partage est libre ou si un programme se l'est dj rserve. Dans le cas le plus simple, ce compteur vaudra 0 si la donne est rserve, et 1 si elle est libre. Ainsi, un thread qui voudra rserver la donne va d'abord devoir vrifier si ce nombre est 1 avant de pouvoir rserver sa donne. Si c'est le cas, il rservera la donne en passant ce nombre 0. Si la donne est rserve par un autre thread , il devra tout simplement attendre son tour. On a alors cre ce qu'on appelle un verrou d'exclusion mutuelle.

Instructions atomiques
Seul problme : cette vrification et modification du compteur pose problme. Celle-ci ne peut pas tre interrompue, sous peine de problmes. On peut reprendre l'exemple du dessus pour l'illustrer. Si notre compteur est 0, et que deux threads veulent lire et modifier ce compteur simultanment, il est possible que les deux threads lisent le compteur en mme temps : ils liront alors un zro, et essayeront alors de se rserver la donne simultanment. Bref, retour la case dpart... Quoique non, il y a peut-tre possibilit de faire en sorte que la vrification et modification de ce compteur puisse se faire correctement. En tout cas, cette vrification et modification du compteur se fait en plusieurs tapes : une lecture du compteur, puis une criture si le compteur est la bonne valeur. Il faudrait que cette lecture et l'criture se fassent en une seule fois. Pour rgler ce problme, certains processeurs fournissent des instructions spcialises, in-interruptibles, capables d'effectuer cette modification du compteur en une seule fois. Ces instructions peuvent ainsi lire ce compteur, dcider si on peut le modifier, et crire la bonne valeur sans tre drang par un autre processeur qui viendrait s'inviter dans la mmoire sans autorisation ! Par exemple, sur les processeurs x86, la vrification/modification du compteur vue plus haut peut se faire avec l'instruction test and set. D'autres instructions atomiques similaires existent pour rsoudre ce genre de problmes. Leur rle est toujours d'implmenter des verrous d'exclusion mutuelle plus ou moins sophistiqus, en permettant d'effectuer une lecture, suivie d'une criture en une seule fois. Ces instructions permettent ainsi de crer des smaphores, des Locks, etc. Gnralement, un programmeur n'a pas devoir manipuler des instructions atomiques lui-mme, mais ne fait que manipuler des abstractions bases sur ces instructions atomiques, fournies par des bibliothques ou son langage de programmation. V oici la plupart de ces instructions atomiques les plus connues : Instruction Description Cette instruction va lire une donne en mmoire, va comparer celle-ci l'oprande de notre instruction (une donne fournie par l'instruction), et va crire un rsultat en mmoire si ces deux valeurs sont diffrentes. Ce fameux rsultat est fourni par l'instruction, ou est stock dans un registre du processeur. Cette instruction charge la valeur de notre compteur depuis la mmoire, l'incrmente, et crit sa valeur en une seule fois. Elle permet de raliser ce qu'on appelle des smaphores. Elle permet aussi d'implmenter des compteurs concurrents. Cette instruction peut changer le contenu d'un registre et d'un morceau de mmoire de faon atomique. Elle est notoirement utilise sur les processeurs x86 de nos PC, qui implmentent cette instruction.

Compare And Swap Fetch And Add XCHG

Comme je l'ai dit plus haut, ces instructions empchent tout autre processeur d'aller lire ou modifier la donne qu'elles sont en train de modifier. Ainsi, lors de lexcution de l'instruction atomique, aucun processeur ne peut aller manipuler la mmoire : notre instruction atomique va alors bloquer la mmoire et en rserver l'accs au bus mmoire rien que pour elle. Cela peut se faire en envoyant un signal sur le bus mmoire, ou pas d'autres mcanismes de synchronisation entre processeurs. Quoiqu'il en soit, le cout de ce blocage de la mmoire est assez lourd : cela peut prendre un sacr bout de temps, ce qui fait que nos instructions atomiques sont lentes. Du moins, c'est le cas si la donne est en mmoire et que le processeur est un peu stupide. En effet, sur certains processeurs, on peut optimiser le tout dans le cas o la donne est dans le cache. Dans ce cas, pas besoin de bloquer la mmoire : le processeur a juste crire dans la mmoire cache, et les mcanismes de cohrence des caches se contenteront de mettre jour la donne de

www.siteduzero.com

Partie 3 : Le partage de la mmoire


faon atomique automatiquement. Le cout des instructions atomiques est alors fortement amorti.

58/97

Problmes avec les verrous d'exclusion mutuelle


Nanmoins, cette technique des verrous d'exclusion mutuelle pose quelques problmes. Premirement, ils imposent qu'un seul thread puisse accder notre donne, en forant tous les autres se mettre en pause. Les autres thread doivent alors attendre que la donne partage soit libre pour continuer leur excution. Et c'est obligatoire, mme s'ils veulent lire la donne sans la modifier. Deuximement, si on ne fait pas trop gaffe, il est possible qu'un thread rserve la donne en oubliant la librer. Dans ce cas, tous les autres threads seront dfinitivement bloqus. Ce genre de chose est souvent synonyme de mauvaise programmation, mais c'est malgr tout un dfaut des verrous d'exclusion mutuelle. Enfin, il est possible qu'un thread n'aie jamais accs la donne partage parce que tous les autres threads passent devant : chaque fois que le thread regarde si la donne est libre, elle est occup par un de ses collgues indlicat qui sera pass avant. Deuximement : sur certains processeurs, ces instructions atomiques bloquent totalement tout accs la mmoire durant un certain moment. Si la donne partage est place en mmoire, tout accs la mmoire, mme inoffensif ou ne touchant pas la donne partage devra attendre al fin de l'instruction atomique. Si on rajoute le cout des Fences, souvent places avant ces instructions atomiques, cela commence faire beaucoup. Alors certes, on peut limiter la casse : par exemple, si la donne partage est dans la mmoire cache, pas besoin de bloquer la mmoire. On peut alors crire directement dans la mmoire cache, sans avoir bloquer quoique ce soit : les mcanismes de cohrence des caches s'occuperont alors de grer la lecture et l'criture de faon atomique. Mais quoiqu'il en soit, ces blocages peuvent tre assez couteux. Bref, nos verrous dexclusion mutuelle ne sont pas la panace.

Mmoire Transactionelle Matrielle


Pour rsoudre les diffrents problmes poss par les verrous d'exclusion mutuelle, les chercheurs en informatique ont invents diverses techniques matrielles ou logicielles pour se passer de ces verrous. Une de ses technique s'appelle la mmoire transactionnelle. Avec la mmoire transactionnelle, on n'utilise pas de verrous d'exclusion mutuelle, et plusieurs threads peuvent accder une donne sans se la rserver. La cration de nos sections critiques est alors assez diffrente de ce que l'on doit faire avec les verrous. La mmoire transactionnelle est base sur un principe simple. Avant, on disposait d'instructions atomiques. Maintenant, on peut carrment rendre atomiques des morceaux de programmes complets. Ces morceaux de programmes atomiques, ce sont ce qu'on appelle des transactions . Ces transactions ne sont rien d'autre qu'un morceau de programme, une suite d'instruction qui va sexcuter et faire ce qu'elle veut de la donne partage. Cette transaction peut ainsi lire notre donne, faire ses calculs, etc, puis l'crire. Tout ce que fait notre transaction reste plus ou moins "invisible" des autres processeurs. Pendant que notre transaction sexcute, tout se passe comme si notre transaction ne modifiait pas notre donne. Dans le cas o la donne partage n'a pas t modifie par un autre processeur durant lexcution d'une transaction, celle-ci peut alors crire son rsultat en mmoire, et le rendre visible tous les autres processeurs. Dans ce cas, tout s'est bien pass. Mais dans le cas contraire, la transaction choue et doit rependre depuis le dbut et tous les changements effectus par la transaction seront effacs, non pris en compte. Cette mmoire transactionnelle a quelques avantages sur les verrous d'exclusion mutuelle. Dj, elle est plus souple que ces verrous : il n'y pas de possibilits de blocages ou de bugs bizarres comme on en voit avec les verrous. De plus, on n'a pas besoin d'utiliser d'instructions atomiques au cas o un autre processeur voudrait aller modifier notre donne partage en mme temps. On n'a plus utiliser ces instructions couteuses en terme de performance juste au cas o. Cette mmoire transactionnelle est souvent implante dans certains langages de programmation de faon purement logicielle, en utilisant des exclusions mutuelles bien caches sous le tapis par le compilateur ou la bibliothque utilise. Ces versions logicielles de la mmoire transactionnelle sont souvent beaucoup plus lentes que ce qu'on peut obtenir avec des verrous d'exclusion mutuelle. Mais cette mmoire transactionnelle peut aussi tre implante de faon matrielle. Il existe plusieurs faons de l'implanter, et on va voir ce que cela donne pour les implmentations les plus connues.

Instructions LL/SC
La premire technique de mmoire transactionnelle matrielle est base sur deux instructions : Load-Link et Store-Conditional. Elle est assez frquente sur certains processeurs possdant peu d'instructions qu'on appelle des processeurs RISC. Elle est notamment utilise sur les processeurs POWER PC, ainsi que sur certains processeurs MIPS ou ARM, ainsi que sur le fameux processeur Alpha, le plus beau de tous. L'instruction Load-Link va lire une donne depuis la mmoire de faon atomique. Vient ensuite l'instruction Store-Conditional, qui est toujours prcde d'une instruction Load-Link. Cette instruction Store-Conditional crit une donne en mmoire condition que notre donne partage en mmoire n'aie pas t modifie depuis lexcution de l'instruction Load-Link.

www.siteduzero.com

Partie 3 : Le partage de la mmoire

59/97

Il arrive parfois que l'instruction Store-Conditionnal choue alors que notre donne partage n'a pas t modifie. Du moins, c'est le cas sur certains processeurs : sur ceux-ci, lexcution d'interruptions ou d'exceptions matrielles dans une transaction la fait chouer. Mais c'est un dfaut que certains processeurs n'ont pas. Son seul problme, c'est qu'elle ne peut manipuler qu'une seule donne partage simple. Impossible de manipuler des donnes plus grandes qu'une certaine taille. Gnralement, on ne peut pas dpasser la taille d'un registre. Notre instruction Load-Link va en effet charger la donne partage dans un registre : si on veut utiliser des donnes plus grosses que notre registre, il n'est pas possible d'utiliser Load-Link, et on doit revenir aux verrous d'exclusion mutuelle, ou faire un mlange entre mmoire transactionnelle et verrous.

Versions volues
Mais ces instructions Load-Link et Store Conditionnal ne sont pas les seul mcanismes de mmoire transactionnelle matrielle. D'autres mcanismes un peu plus volus existent. Ceux-ci se basent sur les mcanismes de cohrence des caches au chapitre prcdent. Avec ces techniques, les changements effectus dans le cache d'un processeur par une transaction ne vont pas tre propags par les mcanismes de cohrence des caches tant que celle-ci ne termine pas correctement. Tout ce passe comme si les mcanismes de cohrence des caches taient mis en pause lors du dmarrage d'une transaction. Une fois la transaction termin, le matriel vrifie que les donnes manipules par la transaction nont pas t accdes, et les modifications faites par celle-ci sont alors propages dans les caches des autres processeurs si la transaction russit. Par contre, si la transaction foire, les donnes modifie par la transaction sont marques Invalid par les mcanismes de cohrence des caches. Tout se passe comme si les mcanismes de cohrence des caches du processeur se remettent en route. De mme, les changements effectus par notre transaction sur les registres du processeur doivent tre annul si celle-ci choue. Pour cela, on peut dcider de sauvegarder une copie des registres du processeur au dmarrage de la transaction, ou dcider d'annuler les changements effectus lors de la transaction. Annuler les changements effectus lors d'une transaction n'est pas une chose simple. Nos processeurs permettent de le faire en rutilisant un circuit dj utilis dans d'autres circonstances : le Reorder Buffer. Mais celui a une taille limit a peut-prt une centaine d'instructions sur les processeurs modernes, ce qui limite la quantit dinstructions qu'on peut placer dans une transaction. La mthode concurrente, qui consiste sauvegarder les registres du processeur, n'a pas ce problme. Il arrive parfois que des transactions chouent sans qu'un autre thread n'aie fait quoique ce soit. Sur certains processeurs, lexcution de certaines instructions critiques peut ainsi faire chouer automatiquement une transaction par mesure de suret. Et parfois, cela peut arriver pour d'autres raisons. Bien sr, certains processeurs n'ont pas ce genre de problmes, mais ils sont plutt rares. C'est des techniques de ce genre qui sont utilises sur les processeurs rcents. Comme exemple, on peut citer le cas du processeur Blue gene d'IBM. Si vous ne connaissez pas ce processeur, sachez qu'il est utilis sur deux des plus grands supercaculateur au monde : le supercalculateur sequoia du Lawrence Livermore National Laboratory, et le supercalculateur Mira du Argonne National Laboratory. Sur ce processeur, chaque donne dans le cache peut tre prsente en plusieurs exemplaires. Lors du dmarrage d'une transaction, le processeur va ainsi modifier notre donne partage dans un de ses caches crant une nouvelle version de la donne. Les autres processeurs peuvent ventuellement faire de mme. Lorsque la transaction termine, les changements sont propags dans les autres caches. Si un seul processeur a manipul la donne partage, celle-ci ne sera prsente qu'en une seule version dans les caches des autres processeurs. Mais si plusieurs processeurs ont modifie cette donne dans leurs caches, ils auront chacun cre une nouvelle version de la donne, et plusieurs versions d'une mme donne diffrente sera prsente dans les caches des processeurs aprs intervention des mcanismes de cohrence des caches. Pour vrifier qu'une transaction a chou, il suffit juste de vrifier combien de versions de la donne se trouvent dans les caches : si on a plusieurs versions, la transaction a choue et doit reprendre. Pour connaitre la version de la donne, notre processeur va incorporer quelques bits supplmentaires avec notre donne, qui indiqueront son numro de version.

Speculative Lock Elision


Je suis sr que vous ne voyez strictement aucun rapport entre les instructions atomiques et la mmoire transactionnelle. Mais mfiez-vous : la mmoire transactionnelle peut aussi se trouver l o on ne l'attend pas. Il peut y avoir un rapport ! Comme je l'ai dj dit, les instructions atomiques sont lentes. Trs lentes. Sans compter que ces instructions cherchent installer des verrous qui empchent deux threads de sexcuter simultanment. Le problme, c'est que les instructions atomiques sont utilises de faon assez pessimistes : il arrive qu'on les utilise au cas o un thread irait aller modifier notre donne partage en mme temps qu'un autre manipule notre donne. Mais il arrive que dans certains cas, on rserve une donne partage alors qu'aucun autre thread ne cherche y accder : la rservation est inutile, mais on doit la faire au cas o. Aussi, pour acclrer l'excution des instructions atomiques, des chercheurs se sont penchs sur ce problme de rservations inutiles. Et ils ont trouvs une solution assez sympathique, base sur la mmoire transactionnelle. Au lieu de devoir mettre un verrous et de rserver notre donne juste au cas o, on peut agir d'une faon un peu plus optimiste.

www.siteduzero.com

Partie 3 : Le partage de la mmoire

60/97

Rien nempche de transformer nos instructions atomiques servant pour les rservations en instructions permettant de dmarrer des transactions. Bien videmment, les instructions atomiques servant librer la donne partage vont marquer la fin de notre transaction. Ce mcanisme tente donc de se passer des instructions atomiques en les transformant en transaction une premire fois, puis revient la normale en cas d'chec. Il s'appelle le Lock Elision . Ainsi, on nexcute pas l'instruction atomique permettant d'effectuer une rservation, et on la transforme en une simple instruction de lecture de la donne partage. Une fois cela fait, on commence excuter la suite du programme en faisant en sorte que les autres processeurs ne voient pas les modifications effectues sur nos donnes partages. Puis, vient le moment de librer la donne partage via une autre instruction atomique. A ce moment, si aucun autre thread n'a crit dans notre donne partage, tout ira pour le mieux : on pourra rendre permanents les changements effectus. Par contre, si jamais un autre thread se permet d'aller crire dans notre donne partage, on doit annuler les changements faits et les faire oublier. A la suite de l'chec de cette excution optimiste, cette transaction cache, le processeur reprendre son excution au dbut de notre fausse transaction, et va excuter son programme normalement : le processeur effectuera alors de vraies instructions atomiques, au lieu de les interprter comme des dbuts e transactions. Il arrive parfois que le premier essai choue lamentablement : si un autre thread a beaucoup de chance de manipuler une donne partage en mme temps qu'un autre, le premier essai a de fortes chances de planter. Pour plus efficacit, certains processeurs cherchent parfois viter ce genre de situation en estimant la probabilit que le premier essai (la transaction) choue. Pour cela, ils incorporent un circuit permettant d'valuer les chances que notre premier marche en tant que transaction : le Transaction Predictor. Une fois cette situation connue, le processeur dcide ou non dexcuter ce premier essai en tant que transaction. Ceux qui veulent se renseigner sur cette technique plus en dtail et qui veulent aller plus loin que ce misrable petit tutoriel de vulgarisation peuvent aller lire l'article original des crateurs de cette technique ici : Speculative Lock Elision. On remarquera que dans la version originale, prsente dans ce papier, on n'a pas besoin de rajouter d'instructions. Ce n'est toutefois pas une obligation, comme on va bientt le voir.

L'exemple avec le x86


Autre exemple, on peut citer les processeurs Intel rcents. Je pense notamment aux processeurs bass sur larchitecture Haswell. Au alentours de mars 2013, de nouveaux processeurs Intel sortiront sur le march : ce seront les premiers processeurs grand public qui supporteront la mmoire transactionnelle matrielle. Attardons-nous un peu sur ces processeurs, et sur l'implmentation de la mmoire transactionnelle matrielle de ces processeurs. Sur ces processeurs, deux modes sont disponibles pour la mmoire transactionnelle matrielle : le mode TSX, et le mode HLE.

TSX
Le mode TSX correspond simplement quelques instructions supplmentaires permettant de grer la mmoire transactionnelle matrielle. On trouve ainsi trois nouvelles instructions : XBEGIN, XEND et XABORT. XBEGIN sert en quelque sorte de top dpart : elle sert dmarrer une transaction. Toutes les instructions places aprs elles dans l'ordre du programme seront ainsi dans une transaction. Au cas o la transaction choue, il est intressant de laisser le programmeur quoi faire. Pour cela, l'instruction XBEGIN permet au programmeur de spcifier une adresse. Cette adresse permet de pointer sur un morceau de code permettant de grer l'chec de la transaction. En cas d'chec de la transaction, notre processeur va reprendre automatiquement son excution cette adresse. videmment, si on a de quoi marquer le dbut d'une transaction, il faut aussi indiquer sa fin. Pour cela, on utilise l'instruction XEND. XABORT quand elle, va servir stopper lexcution d'une transaction : elle sert faire planter notre transaction si jamais on saperoit d'un problme lors de lexcution de notre transaction. Lors de la fin d'une transaction, le processeur va automatiquement reprendre l'adresse indique par XBEGIN, et va remmettre le processeur dans l'tat dans lequel il tait avant le dbut de la transaction : les registres modifis par la transaction sont remis dans leur tat initial, une exception prt : EAX. Celui-ci sert stocker un code d'erreur qui indique les raisons de l'chec d'une transaction. Cela permet de donner des informations au code de gestion d'chec de transaction, qui peut alors grer la situation plus finement.

HLE
Les processeurs Haswall supportent aussi le Lock Elision . Les instructions atomiques peuvent ainsi supporter lexcution en tant que transaction une condition : qu'on leur rajoute un prfixe. Le prfixe, pour les instructions x86, correspond simplement un octet optionnel, plac au dbut de notre instruction dans la mmoire. Cet octet servira donner des indications au processeur, qui permettront de modifier le comportement de notre instruction. Par exemple, les processeurs x86 supportent pas mal d'octets de prfixe : LOCK, qui permet de rendre certaines instructions atomiques, ou REPNZE, qui permet de rpter certaines instruction tant qu'une condition est requise.

www.siteduzero.com

Partie 3 : Le partage de la mmoire

61/97

Le fait est que certains prfixes n'ont pas de signification pour certaines instructions : les placer devant ces instructions n'a alors pas de sens. Autrefois, ils taient totalement ignors, et le processeur ne tenait pas compte de ces prfixes sans signification. Pour supporter le Lock Elision , ces prfixes sans significations sont rutiliss histoire de dire au processeur : cet instruction atomique doit subir la Lock Elision et doit tre tente en tant que transaction. Deux "nouveaux" prfixes font leur apparition : XAQUIRE qui sert indiquer que notre instruction atomique doit tre tente en tant que transaction ; et XRELEASE qui dit que la transaction spculative est termine. Ainsi, un programme peut tre conu pour utiliser la Lock Elision , tout en fonctionnant sur des processeurs plus anciens, qui ne la supportent pas ! Belle tentative de garder la rtrocompatibilit.

www.siteduzero.com

Partie 3 : Le partage de la mmoire

62/97

Partage des ressources


L'utilit des architectures multicurs et multiprocesseurs est vidente : la performance ! Si les entreprises qui fabriquent des processeurs se sont lanc dans la fabrication de tels processeurs, c'est simplement que c'est le seul moyen efficace qu'ils ont trouv pour gagner de la performance sans trop faire d'efforts. Bien sur, cela nempche pas les concepteurs de processeurs d'amliorer ceux-ci pour amliorer leurs performances sur des applications squentielles, non-parallles (amlioration des Prefetchers, des politiques de remplacement des caches, des Branch Predictors, etc), mais la marge de manuvre est assez faible. Sans compter que certaines optimisations de la conception d'un processeur permettant d'amliorer lexcution de programmes parallles peuvent avoir des consquences nfastes sur les programmes squentiels. Mais passons. Sachez que lorsque l'on cre des programmes censs fonctionner sur des processeurs multicurs, certaines contraintes de performances se font plus gnantes qu'avant, et de nouvelles occasions de perdre en performances btement apparaissent. Et encore une fois, notre mmoire cache fait des siennes !

Bus mmoire
Si jamais vous crez un programme utilisant plusieurs processeurs, vous n'aurez pas toujours un gain de performance gal au nombre de processeurs. C'est mme ce qui se passe dans la plupart des cas. Gnralement, cela peut venir de la programmation du logiciel : tous les logiciels ne se paralllisent pas facilement et certains problmes ne se paralllisent pas facilement. On en avait dj parl dans le chapitre sur la Loi d'Amdhal. En tant un peu intelligent, on pourrait se dire qu'il suffit de se baser sur la loi d'Amdhal pour prdire les gains de performances qu'on obtiendra en utilisant plusieurs processeurs. Malheureusement, si vous faites cela, vous obtiendrez un cart assez impressionnant avec la ralit. On peut se demander les raisons : notre loi d'Amdhal a bien t dmontre il y a quelques chapitres, et les mathmatiques sont censes tre infaillibles. Le fait est que les hypothses poses pour dmontrer la loi d'Amdhal n'taient pas les bonnes. On a suppos que seuls les processeurs taient pertinents en terme de performance. Mais le fait est qu'un systme multiprocesseur n'est pas compos que de processeurs isols, indpendants. Ces processeurs doivent se partager divers composants matriels, diverses ressources. Ils doivent se partager le bus mmoire, le cache, un circuit appel le contrleur mmoire et divers autres circuits. Ce partage va limiter les performances. V oyons un peu pourquoi !

Bus Contention
Premires ressources matrielles partages : le bus mmoire et le contrleur mmoire. Le bus mmoire, c'est un ensemble de fils qui relie la mmoire au reste de l'ordinateur : au processeur (via les mmoires caches, intercales entre les deux), aux entres-sorties, etc. Quand un processeur ne trouve pas sa donne dans les caches, il est oblig de lire ou crire directement dans la mmoire, et il doit passer par ce bus mmoire. Seul problme : la quantit de donnes qui peuvent passer sur ce bus en une seule seconde est limite. Si on abuse un peu sur les lectures/critures en mmoire, ce bus mmoire peut saturer. Ne nous leurrons pas : la quantit maximale de donne pouvant passer par de bus en une seconde est assez leve. Cette quantit de donnes transmissibles par seconde sur le bus s'appelle le dbit binaire du bus. Oui, j'ai bien parl de dbit binaire, et pas de bande passante : si vous lisez des sites d'informatique, vous aurez surement vu ce terme : bande passante. Mais c'est un gigantesque abus de langage : n'importe quel lectronicien vous dira qu'une bande passante en octets par seconde est une sombre fumisterie que seul un softeux peut se permettre. Enfin bref. Notre mmoire a un dbit binaire assez norme : on parle de plusieurs gibioctets par seconde dans le meilleur des cas. Mais on parle l de la limite la plus haute, un dbit maximal thorique : le bus commence en ralit saturer un peu avant. En fait, tout dpend de la rpartition des donnes en mmoire : les mmoires actuelles sont en effet assez fortes pour lire et crire des donnes contigus. Cela dpend aussi de la faon dont on y accde : alterner rapidement lectures et critures sera moins rapide que n'effectuer des lectures, par exemple. Cela est du de sombres histoires de bancs mmoires, et de timings mmoires, expliques comme il se doit dans la partie sur les mmoires du tutoriel Fonctionnement d'un ordinateur depuis zro. Bref, notre bus mmoires a une rapidit limite. Il est assez rare d'atteindre de genre de limites dans la ralit. En effet, notre processeur est assez optimis pour viter daccder la mmoire : il dispose de mmoires caches, qui vitent de nombreux accs la mmoires, et vitent donc de passer par le bus. Avec un seul processeur et une application correctement programme, dans un bon langage, on ne peut pas avoir le moindre problme cause de la vitesse de ce bus. Mais tout change avec plusieurs processeurs ! En effet, ce bus mmoire sera alors partager entre plusieurs processeurs. Toutes les lectures et critures de tous les processeurs doivent alors passer par ce bus, qui peut devenir limit. Il suffit que les programmes excuts sur nos processeurs aient besoin de beaucoup accder la mmoire ( cause de caches plus efficaces ou de programmes ne tirant pas correctement parti de ces caches), et le dbit binaire du bus devient limit.

Prefetcher Contention
Petit dtail : nos processeurs effectuent pas mal d'accs mmoires cachs. Il n'y a pas que les accs mmoires prsents dans notre programme qui sont excuts. Je m'explique : afin de mieux grer nos mmoires caches, nos processeurs essayent de ruser

www.siteduzero.com

Partie 3 : Le partage de la mmoire

63/97

un petit peu et sont capables de charger des donnes dans le cache de faon anticipe. Nos processeurs peuvent en effet utiliser certaines rgularits dans les accs mmoires d'un programme et essayer d'anticiper les besoins en donnes futurs. Ils peuvent ainsi spculer sur la prochaine donne lire ou crire, son emplacement, etc. Ce faisant, ils peuvent dcider de prcharger certaines donnes avant que le processeur demande un accs mmoire. Il y a un ou plusieurs circuits inclus dans notre processeur qui grent ces prchargement anticips : ce sont les prefetchers. Ces prefetchers vont ainsi prcharger des donnes. Mais cela ncessite d'utiliser une partie du dbit binaire disponible. Si tout ce passe bien et que le prefetcher ne se trompe pas, alors on gagner fortement en performance. Mais il arrive que le prefetcher se trompe. Dans ce cas, une partie du dbit binaire de notre mmoire a t utilis rien. C'est une perte : on gche un peu de dbit binaire. Cette perte dpend de pas mal de facteurs. Un programme bien cod n'influe pas trop sur ce prefetcher , et le laisse bien fonctionner : les pertes sont rares. Mais avec plusieurs processeurs, les problmes commencent : on doit avoir assez de dbit binaire pour alimenter nos programmes, plus les prefetcher de tous les processeurs. Dans certains cas, si les prfetcher qui se trompent souvent, ils vont gaspiller beaucoup de dbit binaire rien, ce qui diminuera le dbit binaire restant pour nos programmes : la saturation du bus n'est pas loin, et les performances s'effondrent. Cette saturation arrive assez rapidement sur les processeurs qui utilisent un prefecther agressif. Par agressif, on veut dire par l qu'il prcharge beaucoup de donnes, juste au cas o. C'est ainsi : suivant le processeur utilis et l'agressivit de son prefecther , vous pouvez ou pas vous retrouver avec une saturation due au prefecther.

Mmoire cache
Autre ressource partage qui peut poser quelques problmes : les mmoires caches partages. Un cache partage n'a pas une capacit infinie, et cette capacit doit tre partage entre plusieurs threads. Globalement, les caches partags ont beaucoup d'avantages : ils simplifient les mcanismes de cohrence des caches et les rendent plus performants, et surtout, ils permettent d'augmenter la quantit de cache disponible pour un thread . Par exemple, au lieu d'avoir deux caches de 1 mbioctet par processeur, on peut utiliser un cache partag de 2 mbioctets. Si un thread a besoin d'utiliser 1.5 mbioctet et un autre 0.5 mbioctet, la solution base de cache partage est plus efficace. En gros, un cache partag est plus souple. Mais il arrive parfois que certains threads se marchent dessus l'intrieur du cache. Pour comprendre ce phnomne, il va falloir expliquer un peu plus en profondeur comment fonctionne une mmoire cache.

Cache Contention
Notre mmoire cache est une mmoire un peu particulire : elle est dcoupe en blocs de taille fixe qu'on appelle des lignes de cache. Gnralement, ces blocs ont une taille assez grosse compar aux cases mmoires : cela peut varier de 64 256 octets. Petite prcision : on est oblig de transfrer une donne du cache vers la mmoire (ou l'inverse), ligne de cache par ligne de cache. Chaque case mmoire d'une ligne de cache est numrote afin de pouvoir y accder individuellement. En effet, on peut modifier une case mmoire l'intrieur d'une ligne sans trop de problme : le regroupement en lignes de cache ne compte que pour les transferts entre mmoire RAM et cache, pas pour les transferts entre cache et registres. Lorsque notre processeur cherche accder la mmoire, les circuits qui s'occupent de grer la mmoire cache vont intercepter la demande de lecture ou d'criture, et vrifier si le cache contient une copie de la donne aller chercher en RAM. Si c'est le cas, on a un cache hit : on lit ou crit dans le cache au lieu de la mmoire. Dans le cas contraire, c'est un cache miss, et notre donne doit tre lue depuis la mmoire RAM. Si jamais on doit lire ou crire une donne de la mmoire dans un cache rempli, une des lignes de cache va tre slectionne pour tre crite en mmoire RAM : elle deviendra libre et on pourra effacer son contenu pour y charger la nouvelle donne. Pour cela, on choisit souvent la donne la plus ancienne du cache ou celle qui est la moins lue ou crite. Le seul problme, c'est qu'avec plusieurs processeurs, il arrive parfois qu'un processeur aille effacer les lignes de cache occupes par un autre thread pour y placer des donnes lui, charges depuis la mmoire. Nos threads se marchent sur les pieds ! Dans pas mal de cas, cela ne pose pas de problmes : tout dpend si la donne efface a des chances d'tre rutilise ou pas. Si cette donne n'est pas rutilise aprs avoir t vire du cache par un autre thread , il n'y a pas de problmes. Mais dans le cas contraire, le processeur qui manipulait la donne supprime va devoir aller relire sa donne depuis la mmoire. Si cela arrive trop souvent, on peut obtenir une belle baisse de performances. Pour tre franc, en choisissant bien la ligne de cache remplacer, cela ne pose pas trop de problmes sur des applications normales. Mais si ce choix n'est pas bon, alors vous pouvez avoir quelques surprises. Par exemple, nos processeurs dcident souvent de rutiliser les lignes de cache contenant les donnes les moins frquemment utilise ou les moins rcemment utilises. Or, si vous manipulez des tableaux ou des structures de donnes assez linaires (tableaux, ou structures de donnes bases dessus) que vous parcourez en itrant travers, ce choix est un des moins efficace : il faudrait mieux supprimer les donnes les plus rcemment utilises ! Il arrive aussi que l'on manipule des donnes vraiment grosses, qui ne rentrent pas trop dans le cache, ou que chaque thread aie de gros besoin en mmoire cache. Nos threads peuvent alors se marcher sur les pieds : c'est ce qu'on appelle un phnomne de Cache Contention . En clair : faites attention ne pas trop utiliser de mmoire (cache) pour viter des baisses de performances assez subtiles dues un manque de mmoire cache.

www.siteduzero.com

Partie 3 : Le partage de la mmoire

64/97

False Cache Sharing


Mais les phnomnes de cache contention ne sont pas les seuls poser problmes : il en existe d'autres. Et parmi ceux-ci, dans la famille "comment diminuer par 1000 la rapidit de son application sans m'en rendre compte", je demande le False Cache Sharing ! False Cache Sharing , le nom fait peur. Mais ce problme est assez facile comprendre. Il correspond au cas o deux threads se marchent mutuellement dessus lors de l'accs des donnes places sur une mme ligne de cache. Imaginez la scne : vous avez plac deux donnes des adresses mmoires assez proches. Lors du chargement de ces donnes dans le cache, celle-ci se sont retrouves dans la mme ligne de cache. Jusque l, pas de problme. Mais imaginez maintenant ce qui se passe lorsqu'un processeur accde une de ces donnes : c'est toute la ligne de cache qui est invalide par les mcanismes de cohrence des caches! Ainsi, si un thread souhaite accder une autre donne que celle modifie par l'autre thread , mais place sur la mme ligne de cache, il verra que la ligne de cache est invalide et devra alors rcuprer la bonne version ailleurs, en utilisant les mcanismes de cohrence des caches. Ce qui signifie souvent envoyer une requte sur le bus, et le saturer encore plus, voire aller chercher la bonne version de la ligne en mmoire. Ce phnomne peut faire baisser les performances de certaines applications assez rapidement. Il est assez clbre pour ses mfaits, et pour une raison simple : il est trs difficle de le faire partir ! La seule solution consiste en effet espacer nos donnes partages en mmoire de faon ce qu'elles soient places dans des lignes de cache diffrentes. Pour cela, une seule solution assez connue est de grer manuellement ce qu'on appelle l'alignement mmoire de nos donnes. Et cela n'est pas possible dans tous les langages de programmation existants : c'est possible en C, C++ et en ADA, mais d'autres langages comme le Java ne le permettent pas.

Solutions
Bref, en programmation multicurs, perdre de la performance btement est quelque chose de vraiment facile. Il est parfaitement possible de se retrouver dans des cas o un programme paralllis est plus lent qu'un programme n'utilisant qu'un seul cur/processeur. Avoir quelques pistes pour viter ce genre de choses serait donc une bonne chose. Pour cela, il n'y a pas 36 solutions pour limiter les phnomnes de cache, bus et prefetcher contention : il faut faire en sorte que les donnes manipuler soient le plus possible dans la mmoire cache, et viter le plus possible daccder la mmoire. Pour cela, le matriel volue et dispose de quelques faibles marges de manuvre. Le langage de programmation peut jouer. A ce titre, les mthodes ou fonctionnalits de nos langages de programmation peuvent beaucoup jouer sur ce genre de choses : par exemple, un code utilisant la programmation orient objet ou un garbage collector sera souvent assez mauvais dans son utilisation du cache. Ceci dit, une bonne utilisation du cache repose en ralit sur le programmeur qui peut, et doit, prendre en compte le cache ds la conception de ses programmes. Un programmeur peut parfaitement tenir compte de la localit spatiale ou temporelle lorsqu'il programme, et ce aussi bien au niveau de son algorithme : on peut citer l'existence des algorithmes cache oblivious ; du choix de ses structures de donnes : un tableau est une structure de donne aime du cache, tandis qu'une liste chaine ou un arbre n'en sont pas (bien qu'on puisse les implmenter de faon limiter la casse); ou de son code source : par exemple, le sens de parcourt d'un tableau multidimensionnel peut faire une grosse diffrence. Partager la mmoire est acceptable tant que le nombre de processeurs n'augmente pas trop. On considre qu'au del de 32 processeurs, le partage de la mmoire devient tellement couteux que les performances deviennent misrables : la mmoire n'est alors plus suffisamment rapide pour alimenter les processeurs en donnes et ceux-ci passent leur temps attendre la mmoire. Bilan : des performances limites par la mmoire au point que rajouter des processeurs ne sert rien, voire fini par tre contreproductif ! En clair : nesprez pas avoir des processeurs possdant plus de 16 32 curs dans vos ordinateurs. Il faudra trouver autre chose pour gagner en performances, comme rapprocher larchitecture de notre processeur de celle d'une carte graphique. Mais passons.

www.siteduzero.com

Partie 3 : Le partage de la mmoire

65/97

Partie 4 : Architectures NUMA et distribues


Dans les chapitres prcdents, on a vu des systmes multiprocesseurs dans lesquels les processeurs taient placs sur la mme puce de silicium (multicurs) ou sur la mme carte mre. Ces processeurs se partageaient tous la mme mmoire, et partageaient le mme bus les reliant cette dernire. Mais comme on l'a vu au premier chapitre, les systmes base de plusieurs processeurs ne sont pas tous organiss comme a. Certains ne se partagent pas la mmoire : chaque processeur a droit sa propre mmoire, et n'a pas la partager. Dans ce chapitre, nous allons voir ces systmes multiprocesseurs.

Architectures Distribues
Dans les chapitres prcdents, on a vu des systmes multiprocesseurs dans lesquels les processeurs taient placs sur la mme puce de silicium (multicurs) ou sur la mme carte mre. Ces processeurs se partageaient tous la mme mmoire, et partageaient le mme bus les reliant cette dernire. Cette solution est acceptable tant que le nombre de processeurs n'augmente pas trop. On considre qu'au del de 32 processeurs, le partage de la mmoire devient tellement couteux que les performances deviennent misrables : la mmoire n'est alors plus suffisamment rapide pour alimenter les processeurs en donnes et ceux-ci passent leur temps attendre la mmoire. Bilan : des performances limites par la mmoire au point que rajouter des processeurs ne sert rien, voire fini par tre contre-productif ! En clair : nesprez pas avoir des processeurs possdant plus de 16 32 curs dans vos ordinateurs. Il faudra trouver autre chose pour gagner en performances, comme rapprocher larchitecture de notre processeur de celle d'une carte graphique. Mais passons. Si on a vraiment besoin de puissance pure, et qu'on veut simplement rajouter des processeurs pour gagner en performances, les systmes mmoire partage sont hors--jeu. Pas question de concevoir des supercalculateurs 65536 processeurs en leur faisant partager une seule et unique mmoire. Mais heureusement, les systmes base de plusieurs processeurs ne sont pas tous organiss comme a. Certains ne se partagent pas la mmoire : chaque processeur a droit sa propre mmoire, et n'a pas la partager. Les couts de partage de la mmoire sont alors plus ou moins supprims, et on peut gagner en performances en augmentant le nombre de processeurs assez facilement. Du moins, jusqu ce que la loi d'Amdhal vienne gcher la fte. Dans ce chapitre, nous allons voir ces systmes multiprocesseurs.

C'est quoi ?
Dans les architectures distribues, chaque processeur possde sa propre mmoire, sans aucune mmoire partage entre les processeurs. Tous les processeurs sont relis entre eux via un rseau local, qui leur sert changer des donnes ou des ordres un peu particuliers.

Les processeurs peuvent ainsi accder la mmoire d'un autre processeur via le rseau local : il leur suffit de faire une demande au processeur qui dtient la donne. Cette demande va traverser le rseau local et arriver son destinataire : la donne demande est alors envoye via le rseau local et est copie dans la mmoire locale de lordinateur demandeur. Il va de soit que les communications entre les diffrents processeurs peuvent prendre un temps relativement long, et que ceux-ci sont loin d'tre ngligeables. Avec une organisation de ce genre, la qualit et les performances du rseau reliant les ordinateurs est trs important pour les performances. Gnralement, chaque ordinateur excute le mme programme que tous les autres. Mais il ne travaillera pas sur les mme donnes. Ces architectures sont donc une sous classe des architectures MIMD, qui excutent des instructions diffrentes sur des donnes diffrentes (rappelez-vous le premier chapitre). On parle plus prcisment d'architecture SPMD : Single Program Multiple Data .

www.siteduzero.com

Partie 4 : Architectures NUMA et distribues


Bien sr, rien nempche de mettre des mmoires caches entre la mmoire d'un processeur et celui-ci.

66/97

Cette fois, placer des mmoires caches ne pose strictement aucun problme : on n'a pas besoin de garantir la cohrence des caches avec ce genre de systme. De mme, toutes les histoires de synchronisation entre processeurs s'vanouissent et disparaissent pour de bon.

Diffrents types
Reste que ces ordinateurs peuvent tre regroups de diffrentes faons. Dans la majorit des cas, ces ordinateurs sont souvent relis entre eux par des rseaux locaux : les ordinateurs sont physiquement proches les uns des autres, et sont rassembls dans la mme pice, ou dans le mme btiment. Par exemple, si vous prenez un serveur assez puissant, celui utilise souvent plusieurs ordinateurs relis entre eux via un rseau de ce genre. Tous les ordinateurs seront regroups ensemble dans un mme btiment conu spcialement pour l'occasion. Pareil pour les supercalculateurs. Dans ce cas o les ordinateurs sont regroups ensemble et sont gographiquement trs trs proches, on parle souvent de clusters. Mais il existe aussi une autre possibilit : rien nempche ces ordinateurs d'tre regroups ensemble via un rseau internet. Nos ordinateurs peuvent alors tre trs loigns les uns des autres. On parle alors de Grid Computing . Ce Grid Computing est assez connu : de nombreux projets assez connus dans le monde entier se basent dessus. Le premier de ces projets de Grid Computing avoir t connu du grand public n'est autre que SETI@home, un projet international visant dtecter des signaux extraterrestres. Celui-ci analyse des signaux radio en provenance de l'espace, cette analyse tant effectue par des ordinateurs relis entre eux via internet. N'importe qui pouvait se connecter ce projet et faire travailler son ordinateur pour se projet assez facilement. Mais il s'agit du pass : le projet est aujourd'hui officiellement interrompu. D'autres projets de calculs distribus accessibles au grand public ont vu le jour depuis SETI@home. On peut citer certains programmes de calculs scientifique comme Folding@home : vous pouvez ainsi faire travailler votre ordinateur pour la recherche mdicale grce ce projet. Beaucoup de ces programmes de grid computing sont hbergs sur une plateforme centralise nomme BOINC, accessible via ce lien. Quoiqu'il en soit, ce Grid Computing a encore de beaux jours devant lui.

Des besoins en terme de suret


Les architectures distribues ont souvent un grand nombre de processeurs. Cela peut dpasser la centaine. Dans ces conditions, le loi de Murphy n'est pas une option : il y a de fortes chances qu'une dfaillance finisse par arriver tt ou tard. Les exigences en matire de suret sont trs fortes : on ne doit pas perdre de donnes, par exemple. De plus, toute dfaillance matrielle ne doit pas altrer le fonctionnement de l'ensemble du systme. Par exemple, il serait dommage que notre beau serveur ou notre beau supercalculateur ne fonctionne plus alors qu'un seul processeur est en panne. L'ensemble doit continuer fonctionner alors qu'un ou plusieurs processeurs ne fonctionnent plus. Pour ce faire, le systme d'exploitation qui fonctionne sur les diffrents ordinateurs, ainsi que le programme exploitant tout ces ordinateurs doit tre conu pour rsister ce genre de problme. De mme, toute perte de donne doit tre rendue impossible par divers mcanismes de suret. Les disques durs sont une donne critique sur les systmes distribus, et on doit viter de perdre nos donnes si jamais un disque dur vient tomber en panne. Et pour cela, on a invent le RAID, acronyme de Redondant Array of Inexpensive Disks . Le RAID est une technologie spciale qui consiste utiliser plusieurs disques durs sur lesquels on duplique nos donnes. Cela permet de gagner en suret : nos donnes sont dupliques sur plusieurs disques durs, et si l'un tombe en panne, on peut rcuprer les copies sur les autres disques durs. On peut parfois gagner en performances : au lieu de changer un morceau de donne la fois partir d'un seul disque dur, on peut lire des morceaux de donnes simultanment sur diffrents disques durs. La majorit des systmes distribus utilisent le

www.siteduzero.com

Partie 4 : Architectures NUMA et distribues


RAID.

67/97

Gnralement, chaque ordinateur d'un systme distribu a son propre disque dur attitr. L'ensemble est alors souvent second par un disque dur gnral sur lequel on va crire les donnes finales, les rsultats des calculs.

Communication inter-processeur Message Passing


Nos processeurs sont donc relis entre eux par des rseaux locaux. Ceux-ci ne peuvent pas se partager des donnes : chacun a sa propre mmoire, et partager des donnes est donc impossible. Mais cela nempche pas nos processeurs d'changer des donnes entre eux, via des rseaux locaux ou distants. Cet change prend la forme de messages, que les processeurs s'envoient entre eux via le rseau. Un processeur peut ainsi prparer des donnes envoyer un autre processeur, ajouter quelques informations sur les destinataire, la mise en forme des donnes, etc; et envoie le tout sur le rseau. Le destinataire reoit alors le tout et peut commencer travailler. Cet change est ce qu'on appelle un envoi de message. Du point de vue du logiciel, divers langages de programmation spcialiss pour les systmes distribus implmentent des mcanismes dits de passage de message, qui permettent de crer, envoyer, et recevoir des messages. On peut ainsi programmer quand et comment changer nos donnes entre des processeurs. Diverses bibliothques assez connues permettent de fournir des primitives de passage de messages pour divers langages de programmation. La plus connue de toute est sans conteste MPI, disponible sur un grand nombre de systmes d'exploitation, et pour un grand nombre de langages : C, C++, Java, FORTRAN, Ocaml, Python, etc. Le succs de MPI est tel que certains processeurs implmentent MPI directement dans leurs circuits. Cette ide, autrefois utilise uniquement sur les systmes distribus, commence faire son apparition sur les systmes multicurs et multiprocesseurs mmoire partag. Ainsi, au lieu de laisser nos threads partager des donnes, certains langages vont interdire tout partage de donne entre threads et vont les faire communiquer par des envois de messages. Bien sr, ces messages ne passent pas par un rseau, mais passent par la mmoire. Ainsi, un thread qui veut envoyer un message va copier sa donne un endroit de la mmoire avant d'envoyer la position de la donne copie l'autre thread , qui fera ce qu'il faut avec cette copie. Cela permet dempcher nos threads de se partager la mmoire explicitement : pas de verrous d'exclusion mutuelle, nuisibles pour les performances et pour la sant mentale des programmeurs. Malheureusement, le cout en terme de performance se fait souvent sentir : l'envoi d'un message se base souvent sur des interruptions inter-processeurs et de nombreuses copies/recopies mmoires couteuse en performances. Le tout est trs souvent plus lent que l'utilisation de verrous d'exclusion mutuelle, relativement bien optimiss sur les architectures mmoire partages. Pour information, certains processeurs spcialiss possdent des instructions qui permettent de prendre en charge diverses primitives de message passing directement dans le processeur. Il existe ainsi des processeurs spcialement optimiss pour la bibliothque MPI, et mme pire encore.

Les rseaux inter-processeurs


Revenons maintenant nos architectures distribues. On a vu que nos ordinateurs communiquaient entre eux via des rseaux locaux ou distants. Ces rseaux, et leur vitesse, ont une importance capitale pour la performance des architectures distribues. Nos ordinateurs ne sont pas relis n'importe comment, et une connexion intelligente entre processeurs est une ncessit. Il existe trois grandes organisations de base : centralise, hirarchique, et pair pair.

Centralise
Pour les clusters, il est impossible de relier tous les processeurs entre eux directement : un processeur ne peut pas tre reli tous les autres directement. Le nombre de cbles serait beaucoup trop important. Dans ce cas, nos ordinateurs sont tous esclaves d'un ordinateur principal. Cet ordinateur, le maitre, reoit les donnes traiter et se charge de rpartir les traitements effectuer sur les autres ordinateurs. Cet ordinateur maitre peut aussi se chercher de grer les dfaillances ou les pannes.

www.siteduzero.com

Partie 4 : Architectures NUMA et distribues

68/97

Pair pair
Pour le Grid Computing , la version centralise n'est pas forcment trs adapte. Elle peut l'tre, mais les concepteurs de ce genre de systme prfrent relier nos ordinateurs de faon non-hirarchique. Dans ce cas, chaque processeur est reli un certain nombre de voisins, avec lesquels il peut communiquer. Tout processeur d'un tel systme peut envoyer des requtes d'autres processeur. De mme, tout processeur peut rpondre aux requtes envoye par un autre : il n'y a pas d'ordinateur principal qui servirait d'intermdiaire qui grerait toutes les requtes des autres processeurs et se chargerait de les rpartir sur les esclaves.

Hirarchique
Quand le nombre de processeurs augmente, les solutions purement centralise ou purement pair pair ne marchent pas si bien que cela. Demandez un ordinateur de grer plus de 1576 processeurs lui tout seul, vous allez voir : l'ordinateur maitre va se mettre fumer s'il n'est pas trop puissant. Et demandez un processeur de choisir une destination parmi 1576 : mme rsultat ! La solution consiste alors regrouper plusieurs processeurs dans des espces densembles organiss de faon centralise,

www.siteduzero.com

Partie 4 : Architectures NUMA et distribues

69/97

comme au-dessus. Ensuite, il ne reste plus qu' relier ces ensembles entre eux. Cela peut se faire de faon centralise, comme illustr dans le schma ci-dessous, ou en Pair pair.

www.siteduzero.com

Partie 4 : Architectures NUMA et distribues

70/97

NUMA
Les architectures distribues sont assez sympathiques. Elles ne possdent pas les dfaut des architectures mmoire partage, et on peut en augmenter la puissance en rajoutant autant de processeurs que l'on souhaite. L o les systmes mmoire partage rendent lme partir de 16 32 processeurs par manque de bande passante mmoire, les architectures distribues permettent de gagner en performance correctement au del de la centaine de processeurs pour des programmes adapts. Mais ces architectures distribues ont un gros dfaut : on doit grer les transferts et les changes de donnes entre processeurs la main. Utiliser du message passing est assez lourd pour les programmeurs, et grer les transferts de donnes entre processeurs est quelques chose de fastidieux. Pour faciliter la vie des programmeurs tout en gardant des performances dcentes avec un grand nombre de processeurs, d'autres architectures ont ts inventes. Ce sont les architectures NUMA.

C'est quoi ?
Les architectures NUMA sont similaires aux architectures distribues : chaque processeur possde sa propre mmoire, et tous nos processeurs communiquent entre eux via un ou plusieurs rseaux. Mais contrairement aux architectures distribues, ces mmoires spares sont "partages", si l'on peut dire. Avec cette mthode, chaque processeur voit toutes les mmoires ce chaque processeur comme si elles taient rassembles dans une seule grosse mmoire unique. Tout se passe comme si les processeurs taient relis une gigantesque mmoire partage. En ralit, ce n'est videmment pas le cas, mais le systme d'exploitation et les diffrents processeurs se chargent de maintenir cette illusion. Nos programmeurs peuvent ainsi programmer sur ces architectures comme s'ils utilisaient une mmoire partage, et ne doivent pas se compliquer la vie avec des mcanismes de message passing assez complexes grer. En contrepartie, certains dfauts des mmoires partage n'existent pas sur ces architectures. En loccurrence, chaque processeur ayant sa propre mmoire, les phnomnes de Bus Contention vus il y a quelques chapitres disparaissent. On peut donc dpasser la fameuse limite des architectures mmoire partage, limites 16 32 processeurs.

De quoi NUMA est-il ne nom ?


Si un processeur veut accder la mmoire qui lui est attribue, cela ira trs vite. Mais dans le cas contraire, il faudra aller chercher la donne dans la mmoire d'un autre processeur en la faisant passer par le rseau d'interconnexion entre processeurs. En clair, suivant l'endroit auquel un processeur accde la fausse mmoire qui rassemble toutes les mmoires de nos processeurs, la vitesse de cette fausse mmoire ne sera pas la mme. Le temps d'accs la mmoire n'est pas uniforme. C'est cette constatation qui a donn son nom aux architectures NUMA : NUMA est en effet l'acronyme de Non Uniform Memory Acces . Quoiqu'il en soit, bien grer les temps d'accs aux diffrentes mmoires est donc un plus bien agrable. Accder de prfrence la mmoire locale notre processeur au lieu de devoir passer la un rseau pour aller lire dans la mmoire d'un autre processeur est en effet un gros gain en terme de performances. Pour ce faire, le systme d'exploitation peut jouer un rle non ngligeable : c'est en effet lui qui donne de la mmoire nos programmes quand ils en ont besoin. Mais le programmeur a aussi un rle dterminant, bien plus que le systme d'exploitation. Sur ces architectures, il est important de grer la localit de nos donnes correctement (pauvres programmeurs...) : on peut facilement rendre un programme 10 100 fois plus rapide en grant correctement la localit de celui-ci.

Noeuds
Les architectures NUMA les plus simples sont organises comme les architectures distribues.

Mais ces architectures NUMA sont tout de mme assez rares. Il arrive souvent que l'on prfre regrouper plusieurs processeurs sur la mme mmoire. Notre architecture NUMA devient donc un rassemblement d'architectures mmoire partages relies via un rseau.

www.siteduzero.com

Partie 4 : Architectures NUMA et distribues

71/97

Chacune de ces units centrales, ces regroupements d'un ou plusieurs processeurs qui sont relis via un rseau local, sont ce qu'on appelle des nuds .

Cohrence des caches, le retour !


On peut encore une fois utiliser des mmoires caches sur ce genre de machines, mais on retombe sur un problme : la cohrence des caches n'est pas assure. Autrefois, certaines architectures NUMA antdiluviennes ne possdaient pas de mcanismes matriels de cohrence des caches. La cohrence devait donc tre prise en compte directement au niveau logiciel et c'tait au programmeur de grer celle-ci. Beaucoup de programmeurs ont fini par perdre leur sant mentale. Plus personne ne souhaite voir ce genre d'horreur se reproduire, ce qui fait que de nos jours toutes les architectures NUMA disposent de mcanismes matriels de cohrence des caches.

Directory Protocol
Les architectures NUMA ont donc besoin de maintenir la cohrence de leurs caches sur un grand nombre de processeur, pas forcment relis un seul et unique bus. Dans ces conditions, il est vident qu'on ne peut pas utiliser un protocole de cohrence des caches bas sur l'utilisation de ce bus. Exit donc pour nos Snooping Protocols : on doit trouver autre chose. Cet autre chose, ce sont les Directory Protocols.

Organisation de base
Pour rappel, ces Directory Protocols sont des protocoles de cohrence des caches dans lesquels un circuit centralise toutes les informations sur nos donnes partages : dans quelle cache sont-elles, o est la dernire version, est-ce que telle ou telle ligne de cache stocke une donne partage, etc. Ce circuit centralis s'appelle le directory. Ce Directory Protocols est parfois plac en mmoire RAM, mais c'est assez rare : le plus souvent, il est plac dans un circuit part.

www.siteduzero.com

Partie 4 : Architectures NUMA et distribues

72/97

Gnralement, on a un directory par nud. Sur les architectures NUMA ces directory protocols ont un sacr avantage compar aux snooping protocols : ils n'ont pas envoyer dinformations tous les autres processeurs lorsqu'une ligne de cache est mise jour. Avec les snooping protocols , lorsqu'une ligne de cache est mise jour sur un processeur, tous les autres processeurs sont avertis, juste au cas o : mme s'ils ne possdent pas de copie de la donne qui vient d'tre modifie. Avec les directorys protocols, c'est autre chose : les diffrents processeurs ne sont pas tous relis via un seul bus qu'il doivent se partager, mais sont relis entre eux via un ou plusieurs rseau locaux. En consquence, on peut envoyer une information de mise jour un processeur en particulier, sans que tous les autres voient se message. C'est ainsi : seuls les processeurs possdant une copie de la ligne de cache modifie vont tre prvenus. De plus, ces informations peuvent tre envoyes sans que cela bloque tout le rseau local : les rseaux locaux reliant nos processeurs n'utilisent pas de bus partag entre tous les processeurs, mais utilisent une organisation pair pair ou centralise, qui permet ce genre de choses.

Directory
V oyons un peu plus en dtail ce directory. Comme on l'a vu prcdemment, notre directory stocke des informations sur nos donnes. Du point de vue de notre directory, la mmoire est dcoupe en blocs, de mme taille qu'une ligne de cache : chacun de ces blocs de mmoire peut ainsi tre charg dans le cache si le processeur en a besoin. Bien sur, le cache est plus petit que la mmoire, et il peut contenir moins de blocs que la RAM. Le directory va simplement stocker, pour chacun de ces blocs, des informations : est-ce que ce bloc de mmoire a t copi dans le cache ; est-ce que ce bloc est partag avec d'utres processeurs ; si oui lesquels ; quel est l'tat de la ligne de cache contenant ce bloc de mmoire : Shared, Invalid, etc ; et peut-tre d'autres inforamtions. On peut remarquer que plus la mmoire centrales est grande, plus le directory sera grand lui aussi. Sa taille, la quantit de mmoire dont il a besoin pour stocker ces donnes est proportionnelle la taille de la mmoire RAM du nud auquel il est attribu. Bien sr, un directory n'est pas qu'une mmoire stockant toutes ces informations : il contient aussi des circuits qui dcident quoi envoyer sur les rseaux locaux, et d'autres qui se chargent de mettre jour les informations du directory. Mais la taille du directory et la quantit de circuit qu'il utilise dpend fortement de la taille de la mmoire RAM de son nud. Bref, il existe diffrents types de directory, qui ont chacun leurs avantages et leurs inconvnients. On va voir ceux-ci dans ce qui suit.

Bit Vector
Le cas le plus simple correspond aux directory de type Bit Vector . Un directory de type Bit Vector contient deux informations principales : l'tat du bloc de mmoire (Shared, Invalid, Owned, Exclusive, Modified, etc), et des bits de prsence. Ces bits de prsence servent indiquer si la ligne de mmoire a t recopie dans le cache d'un processeur. Chaque processeur dans la machine est associ un bit de prsence : ce bit servira indiquer si notre bloc de mmoire a t recopi dans le cache de ce processeur. Bien sur, notre directory peut aussi contenir d'autres informations pour chaque bloc de mmoire, mais cela dpend alors de l'implmentation et du protocole de cohrence de cache utilis.

www.siteduzero.com

Partie 4 : Architectures NUMA et distribues

73/97

Ce directory est interrog chaque fois qu'un processeur d'un nud veut aller lire la mmoire d'un autre nud. Dans ce cas, le directory appari au nud qui contient la donne va vrifier si cette donne est dans la mmoire cache ou en mmoire. Si elle est en mmoire, alors la lecture de la donne se fait dans la mmoire RAM du nud. Sinon, le directory se dbrouille pour rcuprer la dernire version de la donne (place dans le cache) et va l'envoyer au processeur demandeur. Le directory enregistrera alors le fait qu'un autre processeur aura une copie de cette donne, et devra mmoriser quel tait le processeur demandeur de cette copie : il est mis jour. En cas d'criture d'une donne partage, le directory enverra aux autres directory des informations de mise jour, histoire de garder la cohrence des caches. En tout cas, ces directorys fonctionnent correctement tant que le nombre de processeurs n'est pas trop grand. Si celui-ci augmente, la quantit de bits de prsence augmente un peu trop et le directory fini par faire la taille d'une maison. Pas franchement idal, si vous voulez mon avis...

Coarse Grained Vector Directory


Certains systmes utilisent une amlioration du Bit Vector. Au lieu d'utiliser un bit de prsence par processeur, on peut choisir d'utiliser un bit de prsence pour plusieurs processeurs. Un bit de prsence peut ainsi tre utilis pour 2, 3, 4, etc; processeurs la fois. C'est trs utile sur les architectures NUMA qui possdent plusieurs processeurs par nuds : on utilise alors un bit de prsence par nud, et on maintient la cohrence des caches entre les processeurs d'un nud via un snooping protocol .

Dynamic Pointer Allocation


Dans les directory base de Dynamic Pointer Allocation , chaque directory est un peu plus lger. Il contient juste une adresse mmoire, qui pointe vers une liste chaine de nud. Cette liste chaine de nud contient tous les processeurs/nuds qui contiennent une copie du morceau de mmoire. Cette liste chaine est place soit dans la mmoire RAM du nud, soit dans une mmoire intgre au directory. L'avantage, c'est que dans cette liste, on ne stocke que les processeurs qui possdent une copie de la donne. Alors qu'avant, on stockait des informations sur tous les processeurs. C'est souvent avantageux car les donnes sont souvent partages avec assez peu de processeurs, et donc notre liste prend assez peu de place. Le fonctionnement de ce directory est assez simple. Quand un processeur va lire une donne dans un nud, le directory du nud a juste ajouter ce processeur dans notre liste chaine. Et inversement, quand un processeur se dbarrasse du morceau de mmoire (s'il n'en a plus besoin, par exemple), il le retire de cette liste chaine. Toute la gestion de cette liste chaine est ralise par le directory, par le hardware.

D'autres
Il existe bien sur d'autre types de directory plus ou moins complexes, mais je prfre marrter ici. Si vous tes intresss par le sujet, quelques recherches Google vous feront le plus grand bien. Ceux qui veulent aller plus loin peuvent aller lire ce document : Cache Coherency and Directory Protocols

www.siteduzero.com

Partie 4 : Architectures NUMA et distribues

74/97

Partie 5 : Le paralllisme de donnes


Comme on l'a vu dans le deuxime chapitre de ce tutoriel, la loi d'Amdhal est sacrment pessimiste. Le Thread Level Parallelism a des limites assez contraignantes, et seules certaines applications bien spcifiques pourront bnficier d'un grand nombre de processeurs. La course au nombre de curs, ou au nombre de processeurs est voue un chec aussi cuisant que le die d'un Pentium 4 sans ventilateur ! Mais la loi de Gustafson est beaucoup plus optimiste. L'avenir n'est pas dans un nombre toujours plus grand de processeurs avec une orgie de Threads tous plus parallles les uns que les autres. L'avenir, ce n'est pas le MIMD : c'est le paralllisme de donnes, le SIMD. V oyons un peu les formes que celui-ci peut prendre : instructions vectorielles, processeurs vectoriels, Streams processors, et GPGPU nous attendent.

Instructions SIMD
Nous allons maintenant continuer, et aborder une autre forme de paralllisme : le paralllisme de donnes . Traiter des donnes diffrentes en parallle a t la premire forme de paralllisme rellement exploite. Il faut dire que de nombreuses situations s'y prtes relativement bien : traitement d'image, manipulation de sons, vido, rendu 3d, etc. Et c'est pour cela que ce genre de paralllisme a t le premier tre adopt sur les architectures rcentes. Mais pour ce faire, il a quand mme fallu concevoir des processeurs et des architectures adaptes. Une solution simple est d'utiliser plusieurs processeurs qui excuteront tous la mme instruction, mais chacun sur des donnes diffrentes. Cette solution a autrefois ts utilise sur certains supercalculateurs, comme les Thinking machines CM-1 et CM-2. Ces ordinateurs possdaient environ 64000 processeurs minimalistes, qui excutaient tous la mme instruction au mme moment. Mais ce genre de solution est vraiment quelque chose d'assez lourd, qui ne peut tre efficace et rentable que sur des grosses donnes, et sur des ordinateurs chers et destins des calculs relativement importants. N'esprez pas commander ce genre dordinateurs pour nol ! Comme on va le voir, il existe d'autres manires d'exploiter le paralllisme de donnes au niveau matriel. Ainsi, de nombreux jeux d'instructions spcialiss sont apparus et des processeurs spciaux ont ts invents. Mais assez de bavardages, commenons !

Instructions SIMD
Certains processeurs possdent des instructions pouvant manipuler plusieurs donnes simultanment. Ainsi, le SIMD est intgr directement dans le jeu d'instruction du processeur, qui est spcialement conu pour en tirer partie. Il existe diffrents types d'instructions SIMD, comme on le voir dans ce qui suit. Mais dans tous les cas, celles-ci travaillent sur un ensemble de donnes de mme taille et de mme type. Ces donnes sont rassembles dans des espces de blocs de donnes, d'une taille fixe, qu'on appelle un paquet. Ces paquets contiennent plusieurs nombres entiers ou nombres flottants placs les uns cot des autres et regroups dans ce vecteur. Quand on excutera une instruction sur un vecteur, celle-ci traitera ces entiers ou flottants indpendamment : au lieu de les traiter unes par unes, les donnes prsentes dans ce paquet seront traites simultanment. Exemple avec une addition .

Pour se faire, notre processeur contient souvent une unit de calcul capable de travailler en parallle sur chaque donne d'un paquet. Comme instructions SIMD, on trouve souvent :

www.siteduzero.com

Partie 5 : Le paralllisme de donnes


des instructions arithmtiques du style additions, soustractions, multiplications, etc ; des oprations logiques, comme des ET, des OU, des dcalages ou des rotations ; des comparaisons ; ou des oprations de conversion.

75/97

Mais nos instructions prcdentes en suffisent pas, et il faut parfois effectuer des manipulations spciales sur nos paquets : des permutations, regrouper plusieurs donnes indpendantes dans un paquet, etc. Pour cela, on utilise donc des instructions supplmentaires. Nos processeurs contiennent des registres spcialiss pouvant chacun contenir un paquet. Pour traiter un paquet, il suffira alors de le charger dans un de ces registres, et xecuter une ou plusieurs instructions dessus. Cela a une consquence : les registres ayant tous une taille fixe, la taille d'un paquet est fixe une fois pour toute par le jeu d'instruction. Cela implique que suivant la taille des donnes manipuler, on pourra en placer plus ou moins dans un paquet.

On devra utiliser des instructions diffrentes suivant ce qu'on met dans le paquet : suivant la taille des donnes, et le type de celle-si, on devra effecteur des manipulations diffrentes. Par exemple, on devra utiliser deux instructions diffrente suivant le type des donnes prsentes dans le vecteur (flottant, entier, boolens, etc). Il faudra aussi grer la taille des donnes : il faudra bien savoir comment dcouper le paquet en donnes traiter en parallle. Et pour cela, on utilise diffrentes instructions suivant ce que contient le paquet : l'instruction pour manipuler des paquets contenant des entiers de 16 bits sera diffrente de celle manipulant des entiers de 32 bits.

Arithmtique sature
Les instructions SIMD sont trs souvent utilises pour manipuler des donnes censes reprsenter de la vido, ou des images, voir du son. Et cela a une certaine incidence sur la faon dont se comportent les instructions dans certaines situations. Par exemple, que se passe-il lors d'un Overflow ?

Un Overflow ? Cela mrite peut-tre quelques explications. Les instructions arithmtiques et quelques autres manipulent des nombres ou des donnes de taille fixe, qui ne peuvent prendre leurs valeurs que dans un intervalle dtermin par une valeur minimale et une valeur maximale. Si le rsultat d'un calcul sort de cet intervalle, il ne pas tre reprsent dans notre ordinateur : il se produit ce qu'on appelle un overflow . Lorsqu'on travaille avec des paquets, il est vident que si un rsultat prend plus de bits que ce qu'il est cens prendre, il vaut mieux viter qu'il dborde sur la donne d' cot. Il faut donc trouver une solution pour viter les catastrophe.

www.siteduzero.com

Partie 5 : Le paralllisme de donnes

76/97

Premire solution : on ne conserve que les bits de poids faibles du rsultat et les bits en trop sont simplement oublis. Par exemple, si on additionne deux nombres de 32 bits, le rsultats est cens prendre 33 bits, mais on ne gardera que les 32 bits de base, et on oubliera le bit en trop. Seul problme : en faisant cela, une opration sur deux nombres trs grands peut donner un nombre trs petit, et cela peut poser des problmes lorsqu'on manipule de la vido, des images ou du son. Imaginez que l'on reprsente une couleur par un entier de 8 bits (ce qui est souvent le cas) : par exemple, le blanc correspondra la valeur maximal de cet entier, et le noir 0. En ajoutant deux couleurs trs proches du blanc (et donc deux entiers assez grands), le rsultat obtenu sera un entier assez petit, qui sera donc proche du noir... Ce qui est fort peu crdible ! Et les mmes problmes peuvent arriver dans pleins d'autres situations : il vaut mieux qu'un son trop fort sature au lieu de le transformer en un son trop faible, par exemple. Mais il existe une solution : utiliser ce qu'on appelle l'arithmtique sature. Si un rsultat est trop grand au point de gnrer un overflow , on arrondi le rsultat au plus grand entier support par le processeur. Il va de soit que pas mal d'instructions SIMD utilisent cette arithmtique, vu qu'elles sont destine au traitement d'image ou de son.

Exemples : MMX, SSE et AVX


On a donc vu un peu de thorie sur ces instructions SIMD, mais un peu de pratique ne ferait surement pas de mal. On va donc voir quoi peuvent bien ressembler ces instructions avec des exemples concrets : on va tudier les instructions des processeurs x86, prsents dans nos PC. Le jeu d'instruction de nos PC qui fonctionnent sous Windows est appel le x86. C'est un jeu d'instructions particulirement ancien, apparu certainement avant votre naissance : 1978. Depuis, de plus en plus d'instructions ont t ajoutes et rajoutes : ces instructions sont ce qu'on appelle des extensions x86 . On peut citer par exemple les extensions MMX, SSE, SSE2, voir 3dnow!. Une grande quantit de ces extensions x86 sont des ajouts d'instructions SIMD. Avant toute chose, je tiens signaler que les processeurs x86 ne sont pas les seuls possder des extensions de leur jeu d'instruction SIMD. Les processeurs PowerPC, ARM, SPARC, MIPS et bien d'autres, ont eux aussi des instructions SIMD dignes de ce nom et qui mriteraient toutes un dtour. Mais il faut bien faire un choix, et j'ai personnellement dcid de voir les diffrentes instructions SIMD des processeurs x86.

MMX
Les premires instructions SIMD furent fournies par une extension x86 du nom de MMX . Ce MMX a t introduit par Intel dans les annes 1996 sur le processeur Pentium MMX et a perdur durant quelques annes avant d'tre remplac par les extensions SSE.

registres MMX
Ce MMX introduisait 8 "nouveaux registres" (vous verrez, je ne mets pas des guillemets par hasard), du nom de MM0, MM1, MM2, MM3, MM4, MM5, MM6 et MM7. Ceux-ci avaient une taille de 64 bits, ce qui leur permettait de contenir soit : un entier de 64 bits ; deux entiers de 32 bits ; quatre entiers de 16 bits ; huit entiers de 8 bits. Le MMX ne manipulait que des nombres entiers : pas de nombres flottants l'horizon.

Instructions MMX
Le MMX ajoutait aussi pas mal d'instructions SIMD assez basiques. Ces instructions correspondaient aux instructions qu'on rencontre habituellement dans un jeu d'instruction normal. Ainsi, on trouve des instructions arithmtiques, qui travaillent sur des entiers regroups dans un paquet occupant un registre MMx. On trouve ainsi des instructions d'addition et de soustraction : PADDB, qui additionne deux paquets d'entiers de 8 bits ; PADDW, qui additionne deux paquets d'entiers de 16 bits ; PADDD, qui additionne deux paquets d'entiers de 32 bits ; PADDSB, qui prend un seul paquet et additionne tous les entiers de 8 bits contenus dans ce paquet ; PADDSW qui fait la mme chose avec des paquets d'entiers de 16 bits. On trouve aussi des soustractions : PSUBB, qui soustrait deux paquets d'entiers de 8 bits ;

www.siteduzero.com

Partie 5 : Le paralllisme de donnes


PSUBW, qui soustrait deux paquets d'entiers de 16 bits ; PSUBD, qui soustrait deux paquets d'entiers de 32 bits ; La multiplication est aussi supporte, mais avec quelques petites subtilits, via l'instruction PMULLW. Ces oprations arithmtiques utilisent larithmtique sature.

77/97

Petit dtail, dans la majorit des processeurs, on trouve un ou plusieurs registre spciaux, les registres d'tat, qui stockent des bits permettant d'avoir quelques informations sur l'tat du processeur ou le rsultat d'une instruction. Sur les processeurs x86, les calculs sur les nombres entiers ont leur registre d'tat bien eux, et pareil pour les calculs sur les flottants. Par exemple, si le rsultat d'un calcul dborde et qu'un overflow a lieu, un des bits du registre d'tat sera mit 1. Il existe aussi d'autres bits qui servent stocker des informations du style : le rsultat de l'instruction est un zro, etc. Mais si on regarde bien, on n'a pas de registre d'tat pour les instructions MMX. Pire : les instructions MMX ne mettent aucun registre d'tat jour : ni celui des entiers, ni celui des flottants. Ces instructions ne manipulent pas le registre d'tat, et elles ne prviennent pas en cas d'overflow ou d'underflow si ceux-ci arrivent (pour les instructions qui ne travaillent pas en arithmtique sature). En plus de ces oprations, on retrouve aussi des oprations logiques : PAND, POR et PXOR, aux noms assez explicites ; et l'instruction PANDN, qui met zro un registre MMx. On trouve aussi des instructions de comparaisons : PCMPEQB et PCMPGTB, qui comparent deux paquets d'entiers de 8 bits ; PCMPEQW et PCMPGTW, qui comparent deux paquets d'entiers de 16 bits ; PCMPEQD et PCMPGTD, qui comparent deux paquets d'entiers de 32 bits. Les instructions commenant par PCMPEQ testent d'galit des entiers du paquet, tandis que les instructions commenant par PCMPGT testent si la seconde oprande est suprieure la premire. leur rsultat est fourni dans un paquet de 64 bits qui contient pour chaque entier : soit la valeur maximale si la condition teste est vraie, soit 0 dans le cas contraire. Cela contraste fortement au fonctionnement des instructions normales : avec des instructions x86 habituelle, le rsultat d'une comparaison est stock dans certains du registre d'tat, et n'est pas enregistr dans un registre. L encore, on voit bien que l'absence de registre d'tat pose quelques problmes. A ces instructions, on peut ajouter les instructions de dcalages et de rotations : PSLLW, PSLLD, PSLLQ : dcalage logique gauche d'entiers de 16, 32 et 64 bits ; PSLRW, PSLRD, PSLRQ : dcalage logique droite d'entiers de 16, 32 et 64 bits ; PSRAW, PSRAD : dcalage arithmtique droite d'entiers de 16, 32 bits. Et enfin, on peut rajouter des instructions de conversion et de copie entre registres MMx et la mmoire.

C'est Intel qui a invent la notion d'EPIC FAIL !


Vu le titre, je suppose que vous avez devin que le MMX avait quelques problmes. Et bien c'est le cas : il y avait un lger problme au niveau des registres MMx. Tous les processeurs x86 capables de manipuler des flottants comportent une unit de calcul flottante (une FPU) de base, capable deffectuer des oprations sur les flottants. L'ensemble des instructions machines supportes par cette unit s'appelle le jeu d'instruction x87 . En consquence, cette FPU est couramment appele la FPU x87 . Cette FPU x87 est associe 8 registres flottants de 80 bits, ainsi qu' un registre d'tat de 32 bits. Et c'est l qu'est le problme : chaque registres MMX correspondait aux 64 bits de poids faible d'un des 8 registres flottants de la x87 !

www.siteduzero.com

Partie 5 : Le paralllisme de donnes

78/97

En clair : il tait impossible d'utiliser en mme temps l'unit de calcul flottante et l'unit MMX. Le pire, c'est que ce n'tait pas un bug, mais quelque chose de rellement voulu par Intel. En faisant ainsi, il n'y avait pas sauvegarder 8 registres supplmentaires lorsqu'on appelait une fonction ou qu'on devait interrompre un programme pour en lancer un autre (changement de contexte, interruption, etc) : la sauvegarde des registres de la FPU x87 suffisait. Cela a sacrment gn les programmeurs ou les compilateurs qui voulaient utiliser le jeu d'instruction MMX.

SSE
Dans les annes 1999, une nouvelle extension SIMD fit son apparition sur les processeurs Intel Pentium 3 : le Streaming SIMD Extensions, abrvi SSE. Ce SSE fut ensuite complt, et diffrentes versions virent le jour : le SSE2, SSE3, SSE4, etc. Cette extension fit apparaitre 8 nouveaux registres, les registres XMM. Sur les processeurs 64 bits, ces registres sont doubls et on en trouve donc 16. Exemple avec les registres XMM des processeurs x86 .

En plus de ces registres, on trouve aussi un nouveau registre supplmentaire qui permet de contrler le comportement des instructions SSE : celui contient des bits qui permettront de dire au processeur que les instructions doivent arrondir leurs calculs d'une certaine faon, etc. Ce registre n'est autre que le registre MXCSR. Chose trange, seuls les 16 premiers bits de ce registres ont une utilit : les concepteurs du SSE ont surement prfrs laisser un peu de marge au cas o. Bit Utilit Mit 1 si une opration renvoie un rsultat invalide. Mit 1 si le rsultat de l'instruction est un flottant dnormal. Ces dnormaux sont des nombres flottants spciaux qu'on utilise pour reprsenter des trs petits nombres. Mit 1 si une division par zro a eu lieu. Mit 1 si overflow . Mit 1 si underflow . L'underflow est l'inverse de l'overflow , et est possible pour les nombres flottants. Si un calcul sur un nombre flottant renvoie un rsultat trop petit pour tenir dans un nombre flottants, il est arrondi 0 et on se retrouve avec un underflow . Mit 1 si une instruction flottante est oblig d'arrondir son rsultat pour le faire renter dans un nombre flottant. Si il est configur 1, tous les nombres flottants dnormaux sont considrs comme des zros dans les calculs. Cela permet dacclrer les calculs : il faut dire que la gestion des dnormaux est assez complique et que ces flottants dnormaliss ne sont pas forcment supports par certains processeurs. Si le rsultat d'un calcul est un nombre dnormalis, notre processeur va gnrer ce qu'on appelle une exception matrielle : le processeur va interrompre temporairement lexcution du programme en cours et va excuter un petit programme capable de traiter l'exception. Dans notre cas, ce programme va muler logiciellement le traitement de ces dnormaux. Mais mme lorsque la gestion des dnormaux est implmente en hardware (comme c'est le cas sur certains processeurs AMD), celle-ci reste malgr tout trs lente.

IE DE ZE OE UE PE

DAZ

www.siteduzero.com

Partie 5 : Le paralllisme de donnes

79/97

IM DM ZM OM UM PM Bits 13 14 FZ

Si configur 1, le processeur le lve pas d'exception matrielle en cas dexcution d'une opration invalide. Si 1, le processeur ne lve pas d'exception si une instruction fourni un rsultat dnormalis. Si 1, on ne lve pas d'exception en cas de division par zro, et on arrondit le rsultat. Si 1, on ne lve pas d'exception en cas d'overflow . Si 1, on ne lve pas d'exception en cas d'underflow . Si 1, on ne lve pas d'exception quand une instruction flottante arrondit son rsultat. Servent indiquer comment il faut arrondir un rsultat qui ne rentre pas dans un nombre flottant: vers zro, vers , , ou vers le flottant le plus proche. Si configur 1, on traite les underflow en arrondissant le rsultat zro.

SSE
La premire version du SSE contenait assez peu d'instructions : seulement 70. Croyez-moi, je ne vais pas toutes les lister, mais je peux quand-mme dire qu'on trouve des instructions similaires au MMX (comparaisons, oprations arithmtiques, oprations logiques, dcalages, rotations, etc), avec pas mal d'oprations en plus. On y trouvait notamment des permutations, des oprations arithmtiques supplmentaires, et pas mal d'instructions pour charger des donnes depuis la mmoire dans un registre. Petit dtail : la multiplication est gre plus simplement, et l'on a pas besoin de sembter faire mumuse avec plusieurs instructions diffrentes pour faire une simple multiplication comme avec le MMX. Le SSE premire version ne fournissait que des instructions pouvant manipuler des paquets contenant 4 nombres flottants de 32 bits (simple prcision). On peut quand mme signaler une chose : des instructions permettant de contrler le cache firent leur apparition. On retrouve ainsi des instructions qui permet d'crire ou de lire le contenu d'un registre XMM en mmoire sans le copier dans le cache. Ces instructions permettent ainsi de garder le cache propre en vitant de copier inutilement des donnes dedans. On peut citer par exemple, les instructions MOVNTQ et MOVNTPS du SSE premire version. On trouve aussi des instructions permettant de charger le contenu d'une portion de mmoire dans le cache, ce qui permet de contrler son contenu. De telles instructions de prefetch permettent ainsi de charger l'avance une donne dont on aura besoin, permettant de supprimer pas mal de cache miss. Le SSE fournissait notamment les instruction PREFETCH0, PREFETCH1, PREFETCH2 et PREFETCHNTA. Autant vous dire qu'utiliser ces instructions peut donner lieu de sacrs gains si on s'y prend correctement ! Il faut tout de mme noter que le SSE n'est pas seul "jeu d'instruction" incorporant des instructions de contrle du cache : certains jeux d'instruction POWER PC (je pense l'Altivec) ont aussi cette particularit.

SSE2
Avec le SSE2, de nouvelles instructions furent ajouts, et l'on peut maintenant utiliser en plus des paquets contenant : 2 nombres flottants 64 bits (double prcision) ; 2 entiers de 64 bits ; 4 entiers de 64 bits ; 8 entiers de 16 bits ; 16 entiers de 8 bits. Le SSE2 incorporait ainsi pas moins de 144 instructions diffrentes, instructions du SSE premire version incluses. Ce qui commenait faire beaucoup.

SSE3
Puis, vient le SSE3, avec ses 13 instructions supplmentaires. Pas grand chose signaler, si ce n'est que des instructions permettant d'additionner ou de soustraire tout les lments d'un paquet SSE ensemble, des instructions pour les nombres complexes, et plus intressant : les deux instructions MWAIT et MONITOR qui permettent de parallliser plus facilement nos programmes.

www.siteduzero.com

Partie 5 : Le paralllisme de donnes

80/97

SSE4
Le SSE4 fut un peu plus complexe et fut dclin lui-mme en 2 sous-versions. Le SSE4.1 introduit ainsi des oprations de calcul de moyenne, de copie conditionnelle de registre (un registre est copi dans un autre si le rsultat d'une opration de comparaison prcdente est vrai), de calcul de produits scalaire, de calcul du minimum ou du maximum de deux entiers, des calculs d'arrondis, et quelques autres. Avec le SSE4.2, le vice t pouss jusqu' incorporer des instructions de traitement de chaines de caractres.

AVX
Enfin, la dernire extension en date est l'AVX. Avec celle-ci, on retrouve 16 registres nomms de YMM0 YMM15, ddis aux instructions AVX et d'une taille de 256 bits. Ces registres YMM sont partags avec les registres XMM : les 128 bits de poids faible des registres YMM ne sont autre que les registres XMM. L'AVX complte le SSE et ses extensions, en rajoutant quelques instructions, et surtout en permettant de traiter des donnes de 256 bits. Son principal atout face au SSE, et que les instructions AVX permettent de prciser le registre de destination en plus des registres stockant les oprandes. Avec le SSE et le MMX, le rsultat d'une instruction SIMD tait crit dans un des deux registres d'oprande manipul par l'instruction : il fallait donc sauvegarder son contenu si on en avait besoin plus tard. Avec l'AVX, ce n'est plus le cas : on peut se passer des oprations de sauvegarde sans problme, ce qui supprime pas mal d'instructions.

www.siteduzero.com

Partie 5 : Le paralllisme de donnes

81/97

Les processeurs vectoriels


Avec nos instructions SIMD, on pouvait travailler sur des paquets de taille fixe, qui avaient la taille d'un registre. Ces donnes devaient imprativement tre places les unes aprs les autres dans la mmoire, et avaient quelques limitations : on tait oblig de traiter tous les lments stocks dans un paquet, quelque soit la situation. Bizarrement, ces techniques sont assez rudimentaires, et certains processeurs assez anciens allaient un peu plus loin. Ces processeurs sont ce qu'on appelle des processeurs vectoriels .

Architectures
Il existe deux grands types de processeurs vectoriels : les processeurs vectoriels Memory-Memory ; et les processeurs vectoriels Load-Store.

Processeur vectoriels Memory-Memory


Nous allons commencer par aborder les processeurs vectoriels qui travaillent directement en mmoire, qu'on appelle aussi les processeurs vectoriels Memory-Memory. Il faut savoir que le fait qu'ils accdent directement la mmoire a quelques consquences sur leurs architectures, et que ces processeurs possdent quelques spcificits. Un processeur vectoriel contient souvent un circuit part, charg de grer les instructions vectorielles. Il contient aussi une unit de calcul normale, qui gre les instructions qui ne sont pas vectorielles. L'architecture d'un processeur vectoriel MemoryMemory ressemble donc ceci :

Alors certes, suivant le processeur, l'unit scalaire peut aussi avoir des registres ddis, mais c'est un dtail.

Processeurs vectoriels Load-Store


Maintenant, nous allons voir une autre forme d'architecture : les processeurs vectoriels de type Load-Store, qui travaillent avec des registres vectoriels. Ces registres vectoriels ne sont rien d'autres que ces registres pouvant contenir des vecteurs, ou des morceaux de vecteur d'une taille fixe (128, 256, 512 bits). C'est la mme chose que les registres qui stockaient des paquets sur les processeurs utilisant des instructions SIMD.

www.siteduzero.com

Partie 5 : Le paralllisme de donnes

82/97

Avoir de tels registres est assez avantageux : on peut rutiliser des donnes places dans ce registres au lieu de devoir aller lire nos paquets, nos vecteurs, en mmoire RAM chaque fois qu'on veut les rutiliser. C'est pour cela que les architectures vectorielles sont presque toutes des architectures vectorielles de type Load-Store. Jusque ici, on ne voit pas vraiment la diffrence entre les processeurs vectoriels de type Load-Store et les processeurs dots d'instructions SIMD, si ce n'est les units de calcul vectorielles spares. Mais comme vous allez le voir, il y a une diffrence.

Pipelining
La diffrence tient dans la faon dont sont traits nos paquets, nos vecteurs. Avec les instructions SIMD, on traitait chaque lment sparment, dans une seule grosse unit de calcul. On dispose d'une grosse unit de calcul qui peut effectuer certaines instructions, et qui traite tous les lments d'un paquet simultanment. Ce n'est pas le cas des processeurs vectoriels, qui font autrement : ils travaillent la chaine, ou avec des units de calcul spars ! Ces processeurs pipelinent leurs units de calcul vectorielles ! Par pipeliner, on veut dire que lexcution de chaque instruction sera dcoupe en plusieurs tapes, indpendantes les unes des autres. Au lieu d'attendre que lexcution d'un opration sur une donne soit termine avant de passer la suivante, on peut ainsi commencer le traitement d'une nouvelle donne sans avoir attendre que l'ancienne soit termine. Par exemple, on peut charger la prochaine instruction pendant qu'on excute la prcdente : c'est un peu ce qu'on a vu dans le chapitre sur la paralllisme un seul processeur. Aprs tout, ces deux tapes sont compltement indpendantes et utilisent des circuits spars. Et rien nempche de dcouper les tapes de chargement d'une instruction et leur excution par l'unit de calcul en plusieurs tapes bien distinctes, et de refaire pareil.

Cela ressemble un peu au fonctionnement d'une chaine de montage, dans laquelle on dcoupe la fabrication d'un objet en pleins de sous-tapes qu'on effectue les unes aprs les autres dans des boxes diffrents. C'est trs diffrent des instructions SIMD qui manipulaient des lvements indpendants dans des units de calculs spares. Pour donner un exemple, on peut donner l'exemple d'une multiplication flottante effectue entre deux registres. Son excution peut tre dcompose en plusieurs tapes. Par exemple, on peut avoir 3 tapes : une premire tape E qui va additionner les exposants et grer les diverses exceptions ; une tape M qui va multiplier les mantisses ;

www.siteduzero.com

Partie 5 : Le paralllisme de donnes


et enfin une tape A qui va arrondir le rsultat. Lexcution de notre opration flottante sur un vecteur donnerait donc quelque chose dans le genre :

83/97

Chaque ligne correspond au traitement d'un nouvel lment dans un vecteur, dans un paquet SIMD.

Startup et Dead Time


Quand une instruction vectorielle sexcute, le fait qu'elle soit pipeline va avoir de drles de consquences : une instruction vectorielle met un certain temps avant d'tre vraiment efficace. Pour expliquer pourquoi, on va reprendre l'exemple de notre multiplication flottante vu au-dessus. Supposons que je travaille sur un vecteur de 8 nombres flottants. Au dpart, je commencerais xecuter la premire tape sur le premier lment : je ne traiterais alors que mon premier lment, et seulement celui-ci. Puis, au cycle d'horloge suivant, j'aurais fini l'tape 1 du premier lment, qui passera ltape 2. En mme temps, je commencerais l'tape 1 du second lment du vecteur. Je traiterais alors deux lments en mme temps, mais dans des tapes diffrentes, et donc dans des circuits spars. Au fur et mesure, jexcuterais un nombre de plus en plus important dlments en mme temps, et je finirais alors par avoir chaque circuit ddi une tape occups traiter un lment : je serais alors en rgime de crte.

www.siteduzero.com

Partie 5 : Le paralllisme de donnes

84/97

Tout se passe comme si je mettais un certain temps avant d'arriver mon rgime de croisire. Durant un certain temps, je n'ai pas commenc traiter suffisamment dlments pour que toutes mes tapes soient occupes. Il me faut donc attendre un certain temps, un certain nombre de cycles d'horloges pour que toutes mes tapes soient totalement utilises. Ce temps de dmarrage est strictement gal du nombre d'tapes ncessaires pour effectuer une instruction. La mme chose arrive vers la fin du vecteur, quand il ne reste plus suffisamment dlments traiter pour remplir toutes les tapes.

Comme on le voit, la dure du rgime de croisire n'est pas norme. Elle dpend fortement du nombre d'tapes de notre instruction, et du nombre dlments traiter. Plus on doit traiter dlments dans notre vecteur (plus celui-ci est long), et moins on effectue d'tape, plus on atteint et quitte le rgime optimal rapidement.

Solutions
Pour amortir ces temps de dmarrage et de fin, durant lesquels une bonne partie des tapes, des circuits de notre ALU, sont inutiliss, certains processeurs vectoriels utilisent la ruse. Ils peuvent dmarrer une nouvelle instruction vectorielle sans attendre la fin d'une instruction prcdente. On peut ainsi pipeliner les traitements l'intrieur d'une instruction vectorielle, mais aussi entre deux oprations vectorielles. Elles peuvent ainsi se chevaucher pour remplir les vides.

www.siteduzero.com

Partie 5 : Le paralllisme de donnes

85/97

Par contre, il faut absolument que le nombre d'tapes d'une instruction soit suffisamment petit, pour viter les problmes lorsque deux instructions vectorielles qui se suivent manipulent le mme vecteur. Il faut que la premire instruction aie fini de manipuler un lment avant que la suivante ne veuille commencer le modifier. Pour cela, il faut avoir des vecteurs suffisamment qui contiennent plus dlments qu'il n'y a d'tapes pour effectuer notre instruction.

Chaining
Cette technique du pipeline peut encore tre amliore dans certains cas particuliers. Imaginons que l'on aie trois paquets : A, B et C. Pour chaque ime lment de ces paquets, je souhaite effectuer le calcul . Mon processeur ne disposant pas d'instruction permettant de faire en une fois ce calcul, je dois utiliser deux instruction vectorielles : une d'addition, et une autre de multiplication. On pourrait penser que l'on doit effecteur d'abord la multiplication des paquets B et C, stocker le rsultat dans un paquet temporaire, et effectuer l'addition de ce tableau avec le paquet A. Mais en rusant un peu, on saperoit qu'on peut simplifier le tout et utiliser notre pipeline plus correctement. Notre processeur peut en effet fusionner ces deux instructions indpendantes et les traiter en interne comme s'il sagissait d'une instruction unique. Au lieu d'effectuer la multiplication, puis l'addition sparment pour chaque lment du paquet, il peut effectuer la multiplication et l'addition pour le premier lment, puis continuer avec le second, etc...En gros, il fusionne plusieurs instructions vectorielles en une seule instruction vectorielle qui regroupe les deux. Il s'agit de ce qu'on appelle le Vector Chaining . Mais dans notre processeur vectoriel, les deux calculs sont excuts l'un aprs lautre, avant de passer llment suivant. Pour ce faire, on doit modifier notre pipeline de faon ce que le rsultat de chaque tape d'un calcul soit rutilisable au cycle dhorloge suivant. Ainsi, dans l'exemple du dessus, le rsultat de notre multiplication doit tre rutilisable sans attendre par notre unit de calcul une fois qu'il est calcule : on ne doit pas avoir besoin de l'enregistrer en mmoire pour pouvoir calculer avec.

Multiple Lanes
Cependant, on doit prciser que certains de nos processeurs vectoriels peuvent utiliser plusieurs ALU pour acclrer leur temps de calcul. En clair, ils sont capables de traiter plusieurs lments d'un vecteur en une fois, chacun de ces lments allant dans une ALU. Vu que nos ALU sont pipelines, rien nempche dexcuter plusieurs instructions vectorielles diffrentes dans une seule ALU, chaque instruction tant une tape diffrente. Et quand le tout est coupl aux techniques de Chaining , a envoie du bois !

Accs mmoires
La faon dont ces instructions vectorielles accdent la mmoire est assez intressante. Mais quoiqu'il en soit, il faut savoir que les donnes manipules par nos instructions vectorielles ne passent pas par la mmoire cache. Il arrive aussi que nos processeurs vectoriels ne possdent souvent aucune mmoire cache, ou trs peu. Il faut dire que la mmoire cache est utile lorsque les donnes qu'on va placer dedans sont rutilises plusieurs fois. Dans ce cas, la donne est charge dans le cache depuis la RAM, et est ensuite accde dans le cache : le premier accs est aussi lent qu'un accs mmoire normal, mais les accs

www.siteduzero.com

Partie 5 : Le paralllisme de donnes

86/97

suivants vont aller lire ou crire dans la mmoire cache, bien plus rapide que la RAM. Mais dans le cas qui nous intresse, savoir l'utilisation majeure qui est faite des instructions vectorielle, la donne n'est accde qu'une seule fois : elles servent souvent itrer dans un tableau, et encore : chaque lment doit tre trait indpendamment des autres. En clair, on n'accde qu'une seule fois un lment de notre tableau, et on l'oublie pour un moment. Notre mmoire cache est alors inutile : le seul accs fait sur notre donne sera aussi lent avec ou sans cache. Avec aussi peu de rutilisation des donnes, nos instructions vectorielles peuvent se passer de mmoires caches.

Pipelining des accs mmoires


Reste que nos accs mmoires sont un problme : ils sont lents. Pour limiter la casse, nos processeurs vectoriels disposent de diverses astuces. Dj, les accs mmoires sont pipelins : cela signifie qu'on pet commencer un accs mmoire sans attendre que le prcdent soit fini. Ainsi, nos accs mmoires se "recouvrent", et on peut en xecuter plusieurs en mme temps : c'est plus rapide que de les effectuer un par uns. Mais comme on va le voir, ce n'est pas la seule astuce.

L'exemple du dessus n'est pas l'chelle : en ralit, les accs mmoires durent plus longtemps, et le nombre d'accs mmoire simultans doit tre beaucoup plus grand si on veut garder des performances correctes. Ce qui fait qu'en gnral, les processeurs vectoriels supportent souvent un grand nombre d'accs mmoires simultan, qui peut monter plusieurs centaines d'accs mmoires.

Patterns d'accs mmoire


Nos instructions vectorielles ont des fonctionnalits assez intressantes : elles peuvent fournir au processeur des informations sur la faon dont le processeur doit accder la mmoire. Cela lui permet de savoir la position en mmoire (l'adresse) de la prochaine donne charger : sans a, notre processeur serait bien ennuy.

Accs contigus
Gnralement, ces instructions vont accder des donnes places les unes cot des autres en mmoire. L'instruction a juste dire au processeur de toujours charger la donne immdiatement suivante.

www.siteduzero.com

Partie 5 : Le paralllisme de donnes

87/97

Pour amliorer les performances lors de tels accs la mmoire, nos processeurs vectoriels sont souvent associs des mmoires interleaved . Le nom fait peur, je l'avoue, mais il sera moins impressionnant une fois que vous aurez lus les explications qui suivent. Une mmoire interleaved est une mmoire spcialement conue pour que les accs des donnes conscutives soit le plus rapide possible. Elle est en ralit constitue de plusieurs mmoires assembles dans une seule et unique barrette d'une certaine faon. Si vous ne le savez pas encore, sachez que notre mmoire est dcoupe en petits blocs de mmoire, qui contiennent gnralement 8 bits (un octet). Pour pouvoir identifier ces blocs de mmoires, ces bytes, on leur attribue chacun un numro qui permet de les identifier : leur adresse mmoire. Sur les mmoires interleaved , les bytes dont les adresses mmoires sont conscutives sont placs dans des sous-mmoires conscutives. Cela permet de limiter le temps mit pour accder notre byte. L'accs un boitier prend toujours un peu de temps : c'est le fameux temps d'accs. Si on place deux bytes ayant des adresses conscutives dans le mme boitier, et qu'on souhaite lire/crire ces deux bytes, on devra attendre que l'accs au premier byte soit fini avant de pouvoir accder au suivant (sauf si la mmoire est multiports, mais bref). En clair : on ne peut effectuer qu'un seul accs la fois sur des bytes conscutifs.

Mais ce n'est valable qu'avec les mmoires qui ne sont pas des mmoires interleaved ! Avec les mmoires interleaved , la donne est diffrentes : des bytes conscutifs sont localiss dans des boitiers diffrentes qui peuvent tre accds en parallle. On peut ainsi charger plusieurs mots contigus en mmoires sparment : au lieu de charger notre donne bit par bit, on peut en charger 64 d'un coup sans aucun problmes. Mais attention : cela ne marche qu'avec des accs mmoires conscutifs. Dans le cas d'accs mmoires pas vraiment conscutifs en mmoire et plutt loigns, on doit xecuter nos accs mmoires uns par uns.

Accs en strides
Il arrive aussi assez souvent que l'on accde qu' certains lments tous spars par une mme distance. Par exemple, si on fait des calculs de gomtrie dans l'espace, on peut trs bien ne vouloir traiter que les coordonnes sur l'axe des x, sans instructionner sur l'axe des y ou des z. De tels accs sont ce qu'on appelle des accs en stride.

L'instruction a juste dire au processeur de toujours charger la donne situe x cases plus loin, le x tant souvent fourni via l'instruction (il est incorpor dans la suite de de bits de l'instruction). C'est une des grosses diffrences entre les processeurs vectoriels et les instructions SIMD qu'on a vu auparavant : ces dernires ne grent pas les accs mmoires complexes (comme ceux en strides), et se contentent daccder des donnes contigus. Cela pouvait poser quelques problmes : si jamais on ne veux traiter que certains lments, on ne pouvait pas utiliser d'instructions SIMD. Les processeurs vectoriels n'ont pas ce genre de problmes. Pour grer efficacement ces accs en Stride, la mmoire de l'ordinateur doit tre conue pour. Gnralement, cela signifie placer plusieurs mmoires spares et les rassembler dans une seule grosse mmoire. Gnralement, ces petites sous-mmoires spares sont appeles des bancs mmoires . Vu que nos accs en stride sont souvent assez loigns, cela signifie qu'ils seront assez distants dans la mmoire. En utilisant des bancs mmoires de bonne taille, on peut faire en sorte qu'avec des strides assez longs, les lments accder soient placs dans des bancs spars, accessibles sparment. On se retrouve alors dans la mme situation qu'avec les mmoires interleaved : on peut commencer aller chercher llment suivant alors que le prcdent n'est

www.siteduzero.com

Partie 5 : Le paralllisme de donnes


pas encore disponible.

88/97

Accs en Scatter/gather
Les processeurs vectoriels incorporent aussi un autre type d'accs la mmoire, le scatter-gather ! Oui, le nom est barbare et il faudra vous y faire. Pour comprendre ce que peut tre cet accs mmoire, il faut savoir certaines choses. Dans un programme, on doit parfois accder des donnes qui sont assez disperses dans la mmoire. Et dans ce cas, difficile d'utiliser des instructions vectorielles. Les accs en scatter et en gather cherchent rsoudre ce genre de problmes en rassemblant des donnes disperses en mmoire dans un registre cense contenir des paquets. Pour cela, on va fournir notre processeur un paquet d'index qui stockera les adresses mmoires des donnes disperses rassembler dans un paquet. Quand une instruction excutera un accs en scatter, les bytes de ces adresses seront rapatries depuis la mmoire et rassembles dans un paquet.

Bien sr, il existe aussi linverse : on peut crire les donnes d'un paquet dans des emplacements mmoires disperss. Ce genre d'accs permet ainsi d'utiliser des instructions SIMD ou des instructions vectorielles plus souvent, et donc de gagner en performances. Alors certes, on ne gagne pas beaucoup, mais c'est toujours a de pris !

Quelques registres utiles


Nos processeurs vectoriels utilisent donc des paquets, des vecteurs de taille fixe, dtermine par l'architecture de la machine. C'est exactement la mme chose que pour les instructions SIMD. Mais nos processeurs vectoriels incorporent diverses techniques que de simples instructions SIMD n'ont pas forcment, mme si elles gagneraient tre reprises sur celles-ci.

Vector Length Register


Pour comprendre ces techniques, il faut savoir que nos paquets SIMD et autres vecteurs de machines vectorielles sont utiliss pour manipuler des tableaux de faon efficiente. Ces tableaux ne sont rien d'autre qu'un rassemblement de donnes de mme type (et donc de mme taille) places les unes la suite des autres en mmoire. Mais ces tableaux n'ont pas forcment la mme taille qu'un paquet SIMD. Si j'ai un tableau de 1000 lments, j'aurais du mal le traiter intgralement en utilisant des instructions vectorielles ou des instructions SIMD qui ne peuvent grer que 64 lments. Dans ce cas, on est oblig de rpter notre instruction appliquer au tableau plusieurs fois, sur des morceaux de tableaux diffrents (on utilise une simple boucle).

www.siteduzero.com

Partie 5 : Le paralllisme de donnes

89/97

Cela peut poser quelques problmes. Reprenons notre exemple avec un tableau de 1000 lments, et des paquets de 64 lments. V ous remarquerez que 1000 / 64, a ne tombe pas juste : si on dcoupe notre tableau, on se retrouvera avec 15 paquets complets de 64 lments, et il restera 40 lments traiter. C'est ainsi : la taille d'un tableau n'est pas forcment multiple de la taille d'un paquet SIMD ou d'un vecteur, et il reste quelques lments qu'on doit traiter. Comment faire pour traiter ces lments restants ? Dans ce cas, deux solutions : soit on les traite en utilisant l'unit de calcul normale, et c'est au compilateur de grer le tout (via des techniques de Strip Mining ), soit on demande de l'aide au processeur. Notre processeur peut fournir un Vector Length Register qui indique combien dlments on doit traiter dans notre vecteur. On peut ainsi dire au processeur : je veux que tu ne traite que les 40 premiers lments prsents d'un paquet, et que tu oublie les autres. Gnralement notre instruction va traiter tout les lments d'un vecteur, sauf quand on arrive la fin d'un tableau : on configure alors notre Vector Length Register pour traiter seulement ce qui reste.

Vector Mask Register


Ces instructions SIMD sont trs utiles quand on doit effectuer un traitement identique sur un ensemble de donnes identiques. Mais ds que le traitement effectuer sur nos donnes varie suivant le rsultat d'un test ou d'un branchement, les choses se gtent. Mine de rien, avec une instruction vectorielle, on est oblig de calculer et de modifier tous les lments d'un paquet : il est impossible de zapper certains lments d'un paquet dans certaines conditions. Par exemple, imaginons que je veuille seulement additionner les lments d'un paquet ensemble s'ils sont positifs : je ne peux pas le faire avec une instruction vectorielle "normale". Du moins, pas sans aide. Pour rsoudre ce problmes, certaines processeurs vectoriels dignes de ce nom utilisent un Vector Mask Register. Celui-ci permet de stocker des informations qui permettront de slectionner certaines donnes et pas d'autres pour faire notre calcul. Ce Vector Mask Register va stocker des bits pour chaque flottant prsent dans le vecteur traiter. Si ce bit est 1, notre instruction doit sexcuter sur la donne associe ce bit. Sinon, notre instruction ne doit pas la modifier. On peut ainsi traiter seulement une partie des registres stockant des vecteurs SIMD.

www.siteduzero.com

Partie 5 : Le paralllisme de donnes

90/97

GPGPU et Streams Processors


Dans les chapitres prcdents, on a parl des instructions SIMD, et des processeurs vectoriels. Ces deux types de processeurs sont particulirement adapts pour traiter des donnes en parallle. Mais compar ce qu'on va voir, ces architectures vont passer pour des crations d'amateurs ! Nous allons parler des cartes graphiques modernes.

Architecture d'un GPU


Mais c'est quoi le rapport entre les cartes graphiques et le paralllisme ?

Et bien les cartes graphiques intgrent des processeurs spcialiss dont l'architecture est fortement parallle. Ces derniers sont appels des Graphic Processing Unit, abrvies GPU. Ces dernires annes, ces GPU sont devenus de plus en plus programmables, et l'ide d'utiliser ceux-ci pour effectuer des calculs gnriques s'est peu peu rpandue. De nos jours, nos GPU peuvent effectuer des taches qui auraient t dvolues au processeur il y a quelques annes. Des couches logicielles comme CUDA ou OpenMP permettent ainsi de parallliser des programmes et en excuter certaines portions sur ces GPU. Aussi, comprendre le fonctionnement de ces GPU devient un impratif pour faire du calcul haute performances. V oyons un peu ce que nos GPU ont dans le ventre.

Vieux GPUs
Cela fait un moment que les cartes graphiques de nos ordinateurs sont devenues programmables. Sur les premires cartes graphiques programmables, on pouvait crer des Shaders, des petits programmes permettant de manipuler des pixels ou des donnes gomtriques (des Vertex). Pour traiter ces Shaders, notre carte graphique incorporait des units de traitement, capables dexcuter des instructions sur des pixels ou des donnes gomtriques. Ces units de traitement n'taient ni plus ni moins que de processeurs assez rudimentaires, capables d'effectuer des instructions entires et flottantes.

Et ces derniers n'taient pas identiques : les instructions qu'ils taient capables d'effectuer n'taient pas les mmes suivant que ces processeurs traitaient de la gomtrie ou des pixels. Avec le temps, l'ide d'utiliser ces processeurs pour autre chose que des traitement graphique fit son chemin. Ces processeurs furent alors un peu modifis.

Stream Processors
De nos jours, ces processeurs sont tous identiques et peuvent servir faire aussi bien des calculs graphiques que des calculs

www.siteduzero.com

Partie 5 : Le paralllisme de donnes

91/97

sur des donnes quelconques. Ces processeurs sont ce qu'on appelle des Streams Processors, des processeurs spcialement conus pour excuter des suites d'instructions sur un grand nombre de donnes. Sur ces processeurs, des programmes, nomms Kernels, sont appliqus entirement un tableau de donne que l'on appelle un Stream. Dans nos cartes graphiques actuelles, ce Stream est dcoup en morceaux qui seront chacun traits sur un Stream Processor. Chacun de ces morceaux est appel un Thread . V ous remarquerez que le terme Thread est ici utilis dans un sens diffrent de celui utilis prcdemment. Faites attention ! Quoiqu'il en soit, ces processeurs ressemblent fortement aux processeur vectoriels ou aux processeurs utilisant des instructions SIMD. Un GPU actuel est souvent compos de plusieurs de ces Streams Processors, placs ensembles sur une mme puce, avec quelques autres circuits annexes, utiliss dans les taches de rendu 3D. Ces Streams Processors sont alors pilots par un gros micro-contrleur qui se charge de dcouper le Stream traiter en Threads, avant de les rpartir sur les diffrents Streams Processors de la puce.

On remarque que le dcoupage du Stream en Threads se fait lexcution. C'est assez diffrent de ce qu'on trouve sur les processeurs SIMD ou vectoriels. Sur ces derniers, les instructions du processeur travaillent sur des donnes de taille fixe : pour traiter un tableau complet, on doit utiliser une boucle pour traiter celui-ci morceau par morceau. Rien de tout cela sur nos cartes 3D : on envoie notre carte 3D des informations sur le tableau manipuler et celle-ci se dbrouille toute seule pour le dcouper en morceau et les rpartir sur les processeurs disponibles. L'ensemble est souvent complt par quelques petites mmoires, qui servent stocker des constantes ou des textures de faon temporaire, et qui sont utilisables par tous les Streams Processors de la puce. On retrouve parfois plusieurs mmoires caches partages entre les diffrents Streams Processors. C'est de plus en plus le cas sur les cartes graphiques actuelles.

Hirarchie mmoire
Pour commencer, nous allons voir grosso modo ce que l'on trouve dans un Stream Processor. Et pour cela, on va commencer par la faon dont il gre la mmoire, qui est assez spciale. V oici quoi ressemble l'architecture d'un Stream Processor :

www.siteduzero.com

Partie 5 : Le paralllisme de donnes

92/97

A premire vue, les schmas du dessus ne ressemblent rien de connu. Et pourtant on peut dj faire quelques remarques assez intressantes. Dj, il n'y a pas de mmoires caches. Ensuite, l'ensemble ressemble fortement ce qu'on trouve sur les processeurs vectoriels, part que les registres vectoriels sont scinds en plusieurs exemplaires.

Pas de cache de donnes


En thorie, les Streams Processors contiennent peu de mmoires caches, comme pour les processeurs vectoriels. Et encore, c'est quand ils en ont. Il faut dire que les Streams Processors sont, comme les processeurs vectoriels, conus pour manipuler des tableaux de donnes plus ou moins complexes. Aprs tout, nos Streams ne sont rien d'autres que des tableaux. Gnralement, ces tableaux ont une faible localit temporelle : quand on accde une donne dans un tableau, il est rare qu'on doive la rutiliser plus tard. Dans ces conditions, utiliser des mmoires caches est contre-productif. Il faut dire que celles-ci ont ts conues pour stocker des donnes afin de pouvoir les rutiliser. Avec un cache, on accde une premire fois notre donne depuis la mmoire (ce qui est lent) et on la copie dans le cache, pour la rutiliser plus rapidement (un cache est plus rapide que la RAM). On voit bien que si on utilise notre donne une seule fois, notre cache ne sert rien. Autant copier directement nos donnes dans les registres. C'est pour cela que les premiers processeurs vectoriels et les Streams processors ont peu ou pas de cache. Les premiers Streams Processors, comme l'Imagine, n'avaient strictement aucun cache. Je tiens toutefois nuancer ce que j'ai dit plus haut : si notre Stream Processor ne contient pas de mmoire cache pour les donnes, ce n'est pas le cas pour les instructions. Aprs tout, si l'on doit excuter ces instructions plusieurs fois de suite sur des donnes diffrentes, autant viter de les charger de la mmoire chaque fois. Pour viter cela, les suites d'instructions excuter sont stockes dans une petite mmoire une bonne fois pour toute. Il s'agit bel et bien d'une petite mmoire cache. De plus, les cartes graphiques rcentes utilisent de plus en plus de mmoires caches dissmines un peu partout sur leur puce. On trouve ainsi des caches L1, L2, L3, etc, certains tant partags, d'autres non. Ces caches sont utiles lorsque l'on utilise la carte graphique via des couches logicielles de GPGPU comme CUDA ou OpenMP : on excute alors du code assez gnraliste, qui possde une localit pas trop mauvaise. Dans ces conditions, les mmoires caches peuvent se rendre utiles.

Register Files
Sur un processeur, les registres sont tous regroups dans de grosses mmoires RAM qu'on appelle des Register Files. On en trouve dans des tas de processeurs, et pas seulement dans des Streams Processors. Dans nos Streams Processors, on remarque que l'on en a plusieurs. On trouve d'abord quelques Local Register File . Ceux-ci sont directement connects aux units de calcul. C'est dans ces Local Register File que nos units de calcul vont aller chercher les donnes qu'elles doivent manipuler. Plus bas, ces Local Register Files sont relis un Register File plus gros, le Global Register File , lui-mme reli la mmoire.

www.siteduzero.com

Partie 5 : Le paralllisme de donnes

93/97

C'est trs diffrent de ce qu'on trouve sur les processeurs plus habituels. D'habitude, on ne dispose pas de plusieurs couches de Register Files. A la place, on trouve une couche de registres, une couche de cache, une couche de mmoire. Sur les processeurs vectoriels, on trouve a la place une couche de registres vectoriels, avec une mmoire RAM. Alors pourquoi trouve-on plusieurs couches de registres ? Le fait est que nos Streams Processors et nos GPU disposent d'une grande quantit d'units de calcul. Et cela peut facilement aller plus d'une centaine ou d'un millier d'ALU ! Si on devait relier toutes cas units de calcul un gros Register File, cela poserait pas mal de problme. Si on faisait a, on se retrouverait avec un Register File norme, lent, et qui chaufferait beaucoup trop. Pour garder un Register Files rapide et pratique, on est oblig de limiter le nombre d'units de calcul connectes dessus, ainsi que le nombre de registres contenus dans le Register File. La solution est donc de casser notre gros Register File en plusieurs plus petits, relis un Register File plus gros, capable de communiquer avec la mmoire. Ainsi, nos units de calcul vont aller lire ou crire dans un Local Register File trs rapide.

Notre Global Register File va en quelque sorte d'intermdiaire entre la mmoire RAM et le Local Register File. Un peu comme une mmoire cache. La diffrence entre ce Global Register File et un cache vient du fait que les caches sont souvent grs par le matriel, tandis que ces Register Files sont grs via des instructions machines. Le processeur dispose ainsi d'instructions pour transfrer des donnes entre les Register Files ou entre ceux-ci et la mmoire. Leur gestion peut donc tre dlgue au logiciel, qui saura les utiliser au mieux. Notre Global Register File va servir stocker un ou plusieurs Threads destins tre traits par notre Stream Processor. Il peut aussi servir transfrer des donnes entre les Local Register Files, o stocker des donnes globales, utilises par des Clusters d'ALU diffrents. Quand nos Local Register File, ils vont servir stocker des morceaux de Threads en cours de traitement : tous les rsultats temporaires vont aller dans ce Local Register File, afin d'tre lus ou crits le plus rapidement possible.

Du cot de la RAM
Les transferts de donnes entre la mmoire et le Global Register File ressemble fortement ceux qu'on trouve sur les processeurs vectoriels. Gnralement, un Stream Processor possde quelques instructions capables de transfrer des donnes entre ce Global Register File et la mmoire RAM. Et on trouve des instructions capables de travailler sur un grand nombre de donnes simultanes, des accs mmoires en Stride, en Scatter-Gather, etc. La mmoire RAM d'un Stream Processor est souvent une mmoire spciale, divise en plusieurs sous-mmoires auxquelles on peut accder indpendamment. Ces mmoires indpendantes sont ce qu'on appelle des bancs mmoires, et on les a dj voqus dans le chapitre sur les processeurs vectoriels. Cette mmoire RAM est une mmoire qui possde souvent un gros dbit binaire. Je rappelle que le dbit binaire d'une mmoire

www.siteduzero.com

Partie 5 : Le paralllisme de donnes

94/97

est le nombre de bits qu'elle peut transfrer en une seule seconde. Par contre, cela ne signifie pas que cette mmoire est rapide : le temps mit pour lire ou crire une donne est assez important. Il ne faut pas faire la confusion entre le dbit binaire et le temps d'accs. Pour faire une analogie avec les rseaux, le dbit binaire peut tre vu comme le dbit de la mmoire, alors que le temps d'accs serait similaire au ping . Il est parfaitement possible d'avoir un ping lev avec une connexion qui tlcharge trs vite, et inversement. Pour la mmoire, c'est similaire.

Une histoire de latence


Comme je l'ai dit plus haut, la mmoire RAM est trs lente. Et quand on n'a pas de mmoires caches pour diminuer la latence des accs mmoire, on doit trouver une autre solution. Au lieu de chercher diminuer la latence, les Streams Processors vont plutt chercher cacher cette dernire.

Long Pipeline
La premire solution pour cela consiste permettre notre Stream Processor de continuer excuter des instructions pendant que l'on accde la mmoire. Avec ce principe, on peut remplir les vides dus l'accs mmoire par des instructions indpendantes de la donne lire ou crire. Cela fonctionne condition que l'on aie suffisamment d'instructions excuter et que les lectures soient rares. Autant dire que c'est un cas assez rare. Et surtout, cela dpend fortement de la qualit du programme qui sexcute, et de la qualit du compilateur qui a servi compiler ce dernier.

SIMT
Depuis les Radeon X1800, on prfre utiliser une autre technique : le Single Instruction Multiple Threads , aussi appel SIMT . L'ide est simplement de permettre notre processeur de grer un grand nombre de Threads et de changer de Thread si l'un d'entre eux doit accder la mmoire. Mais attention, comme je l'ai dit plus haut, sur un GPU, un Thread n'est rien d'autre qu'un morceau de donne similaire aux paquets SIMD vus dans les chapitres prcdents. Avec le SIMT , si jamais on doit accder la mmoire, on change de Thread . Au lieu de perdre son temps attendre, on va ainsi remplir les vides en commenant traiter d'autres donnes.

Jeu d'instruction
On peut se demander de quoi sont capables ces fameux Streams Processors, quels calculs ils sont capables d'effectuer. De ce point de vue, si on regarde les instructions qu'ils sont capables de faire, cela a beaucoup volu au fil du temps. Les premiers processeurs traitant des Shaders avaient un jeu d'instruction assez restreint. On ne pouvait qu'effectuer des calculs arithmtiques les uns la suite des autres, sans possibilits d'effectuer des choix (conditions if, else, etc), et sans pouvoir rpter des instructions (boucles). On trouvait de quoi faire des additions, des soustractions, des multiplications, mais rien de vraiment folichon. Au fil du temps, les GPU sont devenus de plus en plus programmables, et des instructions de tests et de branchements

www.siteduzero.com

Partie 5 : Le paralllisme de donnes

95/97

ont fait leur apparition. Les GPU sont devenus capables d'effectuer des boucles, des tests voire mme des appels de fonctions. En mme temps, les calculs qu'ils peuvent effectuer se sont enrichis. Gnralement, un Stream Processor peut effectuer des calculs sur des nombres entiers ou sur des nombres virgule qu'on appelle des nombres virgule flottante. On trouve les classiques additions, multiplications et oprations logiques (OU, ET, XOR, NON, etc).On trouve aussi des instructions qui permettent de calculer le maximum de deux nombres, leur moyenne, ou leur minimum). On a aussi des instructions d'arrondis pour les nombres virgule flottante. Parfois, le Stream Processor est capable d'effectuer des oprations plus complexes comme des exponentielles, des racines carres, le calcul de cosinus, etc. On peut aussi citer le fait que les GPU rcents supportent des instructions atomiques, utilise pour partager la mmoire. Cela peut servir dans certains algorithmes. , , des sinus, des

Plusieurs ALU
Pour effectuer ces calculs, chaque Stream Processor contient des units de calcul. Ces units de calcul d'un Stream Processor ne sont pas forcment identiques. On peut parfaitement avoir quelques ALU capables d'effectuer des additions et multiplications (plus quelques oprations trs simples du genre), cot d'une grosse ALU capable d'effectuer des oprations couteuses et lentes (exponentielle, etc). On trouve aussi souvent une unit charge de la gestion des accs mmoires, et notamment des transferts de donnes entre le Local Register File et le Global Register File. Ces units de calcul ne sont pas commandes n'importe comment : il faut bien rpartir nos calculs sur nos units de calcul pour les faire fonctionner en parallle. Il existe deux grandes faons de les piloter sur un Stream processor ou un GPU.

VLIW
Premire solution : faire de nos Streams Processors des processeurs VLIW. Sur ces processeurs VLIW, nos instructions sont regroupes dans ce qu'on appelle des Bundles, des sortes de super-instructions. Ces bundles sont dcoups en slots, en morceaux de taille bien prcise, dans lesquels il va venir placer les instructions lmentaires faire xecuter. Instruction Slot 1 Addition VLIW Slot 2 Multiplication 3 slots Slot 3 Dcalage gauche

0111 1111 0000 0110 1111 0101 0110 1001 0101

Chaque slot sera attribu une unit de calcul bien prcise. Par exemple, le premier slot sera attribu la premire ALU, la second une autre ALU, le troisime la FPU, etc. Ainsi, l'unit de calcul excutant l'instruction sera prcise via la place de l'instruction lmentaire, le slot dans lequel elle se trouve. Qui plus est, vu que chaque slot sera attribu une unit de calcul diffrente, le compilateur peut se dbrouiller pour que chaque instruction dans un bundle soit indpendante de toutes les autres instructions dans ce bundle. Lorsqu'on excute un bundle, il sera dcompose par le squenceur en petites instructions lmentaires qui seront chacune attribue l'unit de calcul prcise par le slot qu'elles occupent. Pour simplifier la tache du dcodage, on fait en sorte que chaque slot aie une taille fixe.

SIMD
Autre solution : faire en sorte que nos units de calcul soient utilises par des instructions SIMD. Ainsi, toutes nos units de calculs sont identiques et excutent simultanment la mme instruction chaque cycle d'horloge. On se retrouve ainsi avec la mme situation que pour les processeurs vectoriels avec plusieurs lanes ou comme avec les processeurs utilisant des instructions SIMD utilisant une ALU par donne d'un paquet SIMD. Cette solution est assez rcente et date des GPU Geforce 8800. Les cartes graphiques rcentes utilisent toutes des instructions SIMD et non des instructions VLIW. Il faut dire que le VLIW est certes trs adapt pour les calculs de rendu 3D, mais est nettement moins adapt aux calculs gnralistes. Et avec l'apparition de CUDA et des autres openMP et librairies de traitement GPGPU, le VLIW commence perdre de lintrt.

Prdication
Ces instructions SIMD sont trs utiles quand on doit effectuer un traitement identique sur un ensemble de donnes identiques. Mais ds que le traitement effectuer sur nos donnes varie suivant le rsultat d'un test ou d'un branchement, les choses se gtent. Mine de rien, avec une instruction vectorielle, on est oblig de calculer et de modifier tous les lments d'un paquet : il est impossible de zapper certains lments d'un paquet dans certaines conditions. Par exemple, imaginons que je veuille

www.siteduzero.com

Partie 5 : Le paralllisme de donnes


seulement additionner les lments d'un paquet ensemble s'ils sont positifs : je ne peux pas le faire avec une instruction vectorielle "normale". Du moins, pas sans aide.

96/97

Pour rsoudre ce problmes, nos Streams Processors et nos GPU utilisent une technique vue sur les processeurs vectoriels : le Vector Mask Register. Celui-ci permet de stocker des informations qui permettront de slectionner certaines donnes et pas d'autres pour faire notre calcul. Ce Vector Mask Register va stocker des bits pour chaque flottant prsent dans le vecteur traiter. Si ce bit est 1, notre instruction doit sexcuter sur la donne associe ce bit. Sinon, notre instruction ne doit pas la modifier. On peut ainsi traiter seulement une partie des donnes via une instruction VLIW ou SIMD.

Nos Streams Processors ont souvent une grande quantit d'instructions pour mettre jour ce Vector Mask Register, suivant les circonstances. Certaines instructions peuvent ainsi comparer le contenu de deux registres, donnes par donnes, et dcider quoi mettre dans ce registre de masquage en fonction de chaque rsultat.

www.siteduzero.com

Vous aimerez peut-être aussi