Wikilivres frwikibooks https://fr.wikibooks.org/wiki/Accueil MediaWiki 1.46.0-wmf.22 first-letter Média Spécial Discussion Utilisateur Discussion utilisateur Wikilivres Discussion Wikilivres Fichier Discussion fichier MediaWiki Discussion MediaWiki Modèle Discussion modèle Aide Discussion aide Catégorie Discussion catégorie Transwiki Discussion Transwiki Wikijunior Discussion Wikijunior TimedText TimedText talk Module Discussion module Event Event talk Photographie/Personnalités/S/Edward Linley Sambourne 0 53955 762870 762559 2026-04-03T21:54:11Z Marcus Cyron 4892 ([[c:GR|GR]]) [[c:COM:FR|File renamed]]: [[File:Sambourne Apple Girl.jpg]] → [[File:Sambourne Apple Girl - 1.jpg]] 762870 wikitext text/x-wiki {{Ph s Personnalités}} '''Edward Linley Sambourne''' était un caricaturiste et photographe anglais, né à Pentonville le 4 Janvier 1844 et décédé le 3 Août 1910. Il a travaillé notamment pour le ''Punch''. == Galerie de photographies == <gallery widths="240px" heights="240px"> File:Kate Manning by Edward Linley Sambourne (1888) - 2.jpg File:Edward Linley Sambourne Lady.jpg File:LG as a Gypsy Edward Linley Sambourne 1898.jpg File:Sambourne Apple Girl - 1.jpg File:Kate Manning by Edward Linley Sambourne (1888) - 3.jpg File:Sambourne Modieuze dame op Cromwell Road.jpg </gallery> ig1faro3pduzx34bsdr6pnrp9g1taqa 762871 762870 2026-04-03T22:05:34Z Marcus Cyron 4892 ([[c:GR|GR]]) [[c:COM:FR|File renamed]]: [[File:Sambourne Modieuze dame op Cromwell Road.jpg]] → [[File:Woman walking Cromwell Road by Edward Linley Sambourne (June 1906) - 1a.jpg]] 762871 wikitext text/x-wiki {{Ph s Personnalités}} '''Edward Linley Sambourne''' était un caricaturiste et photographe anglais, né à Pentonville le 4 Janvier 1844 et décédé le 3 Août 1910. Il a travaillé notamment pour le ''Punch''. == Galerie de photographies == <gallery widths="240px" heights="240px"> File:Kate Manning by Edward Linley Sambourne (1888) - 2.jpg File:Edward Linley Sambourne Lady.jpg File:LG as a Gypsy Edward Linley Sambourne 1898.jpg File:Sambourne Apple Girl - 1.jpg File:Kate Manning by Edward Linley Sambourne (1888) - 3.jpg File:Woman walking Cromwell Road by Edward Linley Sambourne (June 1906) - 1a.jpg </gallery> 3p3s8o2a0f0wrscd47nwytv95jiar1i Fonctionnement d'un ordinateur/L'architecture de base d'un ordinateur 0 65780 762847 762748 2026-04-03T19:18:23Z Mewtow 31375 /* Les coprocesseurs */ 762847 wikitext text/x-wiki Dans les chapitres précédents, nous avons vu comment représenter de l'information, la traiter et la mémoriser avec des circuits. Mais un ordinateur n'est pas qu'un amoncellement de circuits et est organisé d'une manière bien précise. Il est structuré autour de trois circuits principaux : * un '''processeur''', qui manipule l'information et donne un résultat ; * une '''mémoire''' qui mémorise les données à manipuler ; * les '''entrées/sorties''', qui permettent à l'ordinateur de communiquer avec l'extérieur. [[File:Architecture Von Neumann.png|centre|vignette|upright=2|Architecture d'un système à mémoire.]] Pour faire simple, le processeur est un circuit qui s'occupe de faire des calculs. Rien d'étonnant à cela. Je rappelle que tout est codé par des nombres dans un ordinateur, ce qui fait que manipuler des nombres revient simplement à faire des calculs. Un ordinateur n'est donc qu'une grosse calculatrice améliorée, et le processeur est le composant qui fait les calculs. La mémoire s'occupe purement de la mémorisation des données, des nombres sur lesquelles faire des calculs. Pour être plus précis, il y a deux mémoires : une pour les données proprement dites, une autre pour le programme à exécuter. La première est la '''mémoire RAM''', la seconde est la '''mémoire ROM'''. Nous détaillerons ce que sont ces deux mémoires dans la suite du chapitre, mais sachez que nous avions déjà rencontré ces deux types de mémoires dans les chapitres sur les registres et les mémoires adressables. Les entrées-sorties permettent au processeur et à la mémoire de communiquer avec l'extérieur et d'échanger des informations avec des périphériques. Les '''périphériques''' regroupent, pour rappel, tout ce est branché sur un ordinateur, mais n'est pas à l'intérieur de celui-ci. Le processeur, les mémoires et les entrées-sorties communiquent ensemble via un '''réseau d'interconnexions'''. Le terme est assez barbare, mais rien de compliqué sur le principe. C'est juste un ensemble de fils électriques qui relie les différents éléments d'un ordinateur. Les interconnexions sont souvent appelées le bus de communication, mais le terme est un abus de langage, comme on le verra plus bas. Afin de simplifier les explications, on va supposer que le réseau d'interconnexion est le suivant. Tout est connecté au processeur. Il y a des interconnexions entre le processeur et la mémoire RAM, d'autres interconnexions entre processeur et mémoire ROM, et d'autres entre le processeur et les entrées-sorties. Nous verrons que d'autres réseaux d'interconnexions fusionnent certaines interconnexions, pour les partager entre la ROM et la RAM, par exemple. Mais pour le moment, gardez le schéma ci-dessous en tête. [[File:Réseau d'interconnexion avec un processeur au centre.png|centre|vignette|upright=2|Réseau d'interconnexion avec un processeur au centre]] ==Les mémoires RAM et ROM== La mémoire est le composant qui mémorise des informations, des données. Dans la majorité des cas, la mémoire est composée de plusieurs '''cases mémoire''', chacune mémorisant plusieurs bits, le nombre de bits étant identique pour toutes les cases mémoire. Dans le cas le plus simple, une case mémoire mémorise un '''octet''', un groupe de 8 bits. Mais les mémoires modernes mémorisent plusieurs octets par case mémoire : elles ont des cases mémoires de 16, 32 ou 64 bits, soit respectivement 2/4/8 octets. De rares mémoires assez anciennes utilisaient des cases mémoires contenant 1, 2, 3, 4, 5, 6 7, 13, 17, 23, 36 ou 48 bits. Mais ce n'était pas des mémoires électroniques, aussi nous allons les passer sous silence. Tout ce qu'il faut savoir est que la quasi-totalité des mémoires électronique a un ou plusieurs octets par case mémoire. Pour simplifier, vous pouvez imaginer qu'une mémoire RAM est un regroupement de registre, chacun étant une case mémoire. C'est une description pas trop mauvaise pour décrire les mémoires RAM, qu'on abordera dans ce qui suit. {|class="wikitable" |+ Contenu d'une mémoire, case mémoire de 16 bits (deux octets) |- ! Case mémoire N°1 | 0001 0110 1111 1110 |- ! Case mémoire N°2 | 1111 1110 0110 1111 |- ! Case mémoire N°3 | 0001 0000 0110 0001 |- ! Case mémoire N°4 | 1000 0110 0001 0000 |- ! Case mémoire N°5 | 1100 1010 0110 0001 |- ! ... | ... |- ! Case mémoire N°1023 | 0001 0110 0001 0110 |- ! Case mémoire N°1024 | 0001 0110 0001 0110 |} Dans ce cours, il nous arrivera de partir du principe qu'il y a un octet par case mémoire, par souci de simplification. Mais ce ne sera pas systématique. De plus, il nous arrivera d'utiliser le terme adresse pour parler en réalité de la case mémoire associée, par métonymie. ===La capacité mémoire=== Bien évidemment, une mémoire ne peut stocker qu'une quantité finie de données. Et à ce petit jeu, certaines mémoires s'en sortent mieux que d'autres et peuvent stocker beaucoup plus de données que les autres. La '''capacité''' d'une mémoire correspond à la quantité d'informations que celle-ci peut mémoriser. Plus précisément, il s'agit du nombre maximal de bits qu'une mémoire peut contenir. Elle est le produit entre le nombre de cases mémoire, et la taille en bit d'une case mémoire. Toutes les mémoires actuelles utilisant des cases mémoire d'un ou plusieurs octets, ce qui nous arrange pour compter la capacité d'une mémoire. Au lieu de compter cette capacité en bits, on préfère mesurer la capacité d'une mémoire avec le nombre d'octets qu'elle contient. Mais les mémoires des PC font plusieurs millions ou milliards d'octets. Pour se faciliter la tâche, on utilise des préfixes pour désigner les différentes capacités mémoires. Vous connaissez sûrement ces préfixes : kibioctets, mébioctets et gibioctets, notés respectivement Kio, Mio et Gio. {|class="wikitable" |- !Préfixe!!Capacité mémoire en octets!!Puissance de deux |- ||Kio||1024||2<sup>10</sup> octets |- ||Mio||1 048 576||2<sup>20</sup> octets |- ||Gio||1 073 741 824||2<sup>30</sup> octets |} On peut se demander pourquoi utiliser des puissances de 1024, et ne pas utiliser des puissances un peu plus communes ? Dans la majorité des situations, les électroniciens préfèrent manipuler des puissances de deux pour se faciliter la vie. Par convention, on utilise souvent des puissances de 1024, qui est la puissance de deux la plus proche de 1000. Or, dans le langage courant, kilo, méga et giga sont des multiples de 1000. Quand vous vous pesez sur votre balance et que celle-ci vous indique 58 kilogrammes, cela veut dire que vous pesez 58 000 grammes. De même, un kilomètre est égal à 1000 mètres, et non 1024 mètres. Autrefois, on utilisait les termes kilo, méga et giga à la place de nos kibi, mebi et gibi, par abus de langage. Mais peu de personnes sont au courant de l'existence de ces nouvelles unités, et celles-ci sont rarement utilisées. Et cette confusion permet aux fabricants de disques durs de nous « arnaquer » : Ceux-ci donnent la capacité des disques durs qu'ils vendent en kilo, méga ou giga octets : l’acheteur croit implicitement avoir une capacité exprimée en kibi, mébi ou gibi octets, et se retrouve avec un disque dur qui contient moins de mémoire que prévu. ===Lecture et écriture : mémoires ROM et RWM=== Pour simplifier grandement, on peut grossièrement classer les mémoires en deux types : les ''Read Only Memory'' et les ''Read Write Memory'', aussi appelées mémoires ROM et mémoires RWM. Pour les '''mémoires ROM''', on ne peut pas modifier leur contenu. On peut y récupérer une donnée ou une instruction : on dit qu'on y accède en lecture. Mais on ne peut pas modifier les données qu'elles contiennent. Quant aux '''mémoires RWM''', on peut y accéder en lecture (récupérer une donnée stockée en mémoire), mais aussi en écriture : on peut stocker une donnée dans la mémoire, ou modifier une donnée existante. Tout ordinateur contient au minimum une ROM et une RWM (souvent une mémoire RAM), les deux n'ont pas exactement le même rôle. Pour simplifier, la mémoire ROM mémorise le programme à exécuter, la mémoire RWM stocke des données. Il a existé des ordinateurs où la mémoire RWM était une mémoire magnétique, voire acoustique, mais ce n'est plus le cas de nos jours. Pour les ordinateurs modernes, la mémoire RWM est une mémoire électronique. Pour faire la différence avec ces anciennes mémoires RWM, elle est appelée la '''mémoire RAM'''. Il s'agit d'une mémoire qui stocke temporairement des données que le processeur doit manipuler (on dit qu'elle est volatile). Elle s'efface complètement quand on coupe l'alimentation de l'ordinateur. Outre le programme à exécuter, la mémoire ROM peut mémoriser des constantes, des données qui ne changent pas. Elles ne sont jamais modifiées et gardent la même valeur quoi qu'il se passe lors de l'exécution du programme. En conséquence, elles ne sont jamais accédées en écriture durant l'exécution du programme, ce qui fait que leur place est dans une mémoire ROM. La mémoire RWM est alors destinée aux données temporaires, qui changent ou sont modifiées lors de l'exécution du programme, et qui sont donc manipulées aussi bien en lecture et en écriture. La mémoire RWM mémorise alors les variables du programme à exécuter, qui sont des données que le programme va manipuler. Pour les systèmes les plus simples, la mémoire RWM ne sert à rien de plus. [[File:Espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=2.5|Espaces d'adressage sur une archi harvard modifiée]] Pour donner un exemple de données stockées en ROM, on peut prendre l'exemple des anciennes consoles de jeu 8 et 16 bits. Les jeux vidéos sur ces consoles étaient placés dans des cartouches de jeu, précisément dans une mémoire ROM à l'intérieur de la cartouche de jeu. La ROM mémorisait non seulement le code du jeu, le programme du jeu vidéo, mais aussi les niveaux et les ''sprites'' et autres données graphiques. Une conséquence est que les consoles 8/16 bits n'avaient pas besoin de beaucoup de RAM, comparé aux ordinateurs de l'époque, vu qu'une grande partie des données utiles étaient dans une ROM directement accessible par le processeur. À l'opposé, les micro-ordinateurs devaient copier les données d'un jeu depuis une disquette dans la mémoire RAM, ce qui demandait d'avoir plus de RAM. Le passage au support CD sur les consoles 32 bits a eu la même conséquence. Le processeur ne pouvant pas lire directement le CD à sa guise, il fallait copier les données du CD en RAM. D'où l'apparition de temps de chargement assez longs, inexistants sur support cartouche. ===L'adressage mémoire=== Sur une mémoire RAM ou ROM, on ne peut lire ou écrire qu'une case mémoire, qu'un registre à la fois : une lecture ou écriture ne peut lire ou modifier qu'une seule case mémoire. Techniquement, le processeur doit préciser à quel case mémoire il veut accéder à chaque lecture/écriture. Pour cela, chaque case mémoire se voit attribuer un nombre binaire unique, l''''adresse''', qui va permettre de le sélectionner et de l'identifier celle-ci parmi toutes les autres. En fait, on peut comparer une adresse à un numéro de téléphone (ou à une adresse d'appartement) : chacun de vos correspondants a un numéro de téléphone et vous savez que pour appeler telle personne, vous devez composer tel numéro. Les adresses mémoires en sont l'équivalent pour les cases mémoire. [[File:Adressage mémoire.png|centre|vignette|upright=2|Exemple : on demande à la mémoire de sélectionner la case mémoire d'adresse 1002 et on récupère son contenu (ici, 17).]] L'adresse mémoire est générée par le processeur. Le processeur peut parfaitement calculer des adresses, en extraire du programme qu'il exécute, et bien d'autres choses. Nous détaillerons d'ailleurs les mécanismes pour dans les chapitres portant sur les modes d'adressage du processeur. Mais pour le moment, nous avons juste besoin de savoir que c'est le processeur qui envoie des adresses aux mémoires RAM et ROM. Les adresses générées par le processeur sont alors envoyées à la RAM ou la ROM via une connexion dédiée, un ensemble de fils qui connecte le processeur à la mémoire : le '''bus d'adresse mémoire'''. L'adresse sélectionne une case mémoire, le processeur peut alors récupérer la donnée dedans pour une lecture, écrire une donnée pour l'écriture. Pour cela, un second ensemble de fil connecte le processeur à la RAM/ROM, mais cette fois-ci pour échanger des données. Il s'agit du '''bus de données mémoire'''. Les deux sont souvent regroupés sous le terme de '''bus mémoire'''. Un ordinateur contient toujours une RAM et une ROM, ce qui demande aux bus mémoire de s'adapter à la présence de deux mémoires. Il y a alors deux solutions, illustrées dans les deux schémas ci-dessous. Avec la première, il y a un seul bus mémoire partagé entre la RAM et la ROM, comme illustré ci-dessous. Une autre solution utilise deux bus séparés : un pour la RAM et un autre pour la ROM. Nous verrons les différences pratiques entre les deux à la fin du chapitre. Pour le moment, nous allons partir du principe qu'il y a un bus pour la mémoire ROM, et un autre bus pour la RAM. [[File:CPT-System-Architecture-gapfill1-ANS.svg|centre|vignette|upright=2|Architecture avec une ROM et une RAM.]] [[File:Réseau d'interconnexion avec un processeur au centre.png|centre|vignette|upright=2|Réseau d'interconnexion avec un processeur au centre]] ===L'alignement mémoire : introduction=== Plus haut, nous avions dit qu'il y a une adresse par case mémoire, chaque case mémoire contenant un ou plusieurs octets. Mais les processeurs modernes partent du principe que la mémoire a un octet par adresse, pas plus. Et ce même si la mémoire reliée au processeur utilise des cases mémoires de 2, 3, 4 octets ou plus. D'ailleurs, la majorité des mémoires RAM actuelle a des cases mémoires de 64 bits, soit 8 octets par case mémoire. Les raisons à cela sont multiple, mais nous les verrons en détail dans le chapitre sur l'alignement mémoire. Toujours est-il qu'il faut distinguer les '''adresses mémoire''' et les '''adresses d'octet''' gérées par le processeur. Le processeur génère des adresses d'octet, qui permettent de sélectionner un octet bien précis. L'adresse d'octet permet de sélectionner un octet parmi tous les autres. Mais la mémoire ne comprend pas directement cette adresse d'octet. Heureusement, l'octet en question est dans une case mémoire bien précise, qui a elle-même une adresse mémoire bien précise. L'adresse d'octet est alors convertie en une adresse mémoire, qui sélectionne la case mémoire adéquate, celle qui contient l'octet voulu. La case mémoire entière est lue, puis le processeur ne récupère que les données adéquates. Pour cela, des circuits d'alignement mémoire se chargent de faire la conversion entre adresses du processeur et adresse mémoire. Nous verrons cela dans le détail dans le chapitre sur l'alignement mémoire. Il existe des mémoires qui n'utilisent pas d'adresses mémoire, mais passons : ce sera pour la suite du cours. ==Le processeur== Dans les ordinateurs, l'unité de traitement porte le nom de '''processeur''', ou encore de '''''Central Processing Unit''''', abrévié en CPU. Le rôle principal du processeur est de faire des calculs. La plupart des processeurs actuels supportent au minimum l'addition, la soustraction et la multiplication. Quelques processeurs ne gèrent pas la division, qui est une opération très gourmande en circuit, peu utilisée, très lente. Il arrive que des processeurs très peu performants ne gèrent pas la multiplication, mais c'est assez rare. Un processeur ne fait pas que des calculs. Tout processeur est conçu pour effectuer un nombre limité d'opérations bien précises, comme des calculs, des échanges de données avec la mémoire, etc. Ces opérations sont appelées des '''instructions'''. Les plus intuitives sont les '''instructions arithmétiques''', qui font des calculs, comme l'addition, la soustraction, la multiplication, la division. Mais il y a aussi des '''instructions d'accès mémoire''', qui échangent des données entre la mémoire RAM et le processeur. Les autres instructions ne sont pas très intuitives, aussi passons-les sous silence pour le moment, tout deviendra plus clair dans les chapitres sur le processeur. ===Le processeur exécute un programme, une suite d'instructions=== Tout processeur est conçu pour exécuter une suite d'instructions dans l'ordre demandé, cette suite s'appelant un '''programme'''. Ce que fait le processeur est défini par la suite d'instructions qu'il exécute, par le programme qu'on lui demande de faire. Les instructions sont exécutées dans un ordre bien précis, les unes après les autres. L'ordre en question est décidé par le programmeur. La totalité des logiciels présents sur un ordinateur sont des programmes comme les autres. Le programme à exécuter est stockée dans la mémoire de l'ordinateur. C'est ainsi que l'ordinateur est rendu programmable : modifier le contenu de la mémoire permet de changer le programme exécuté. Mine de rien, cette idée de stocker le programme en mémoire est ce qui a fait que l’informatique est ce qu'elle est aujourd’hui. C'est la définition même d'ordinateur : appareil programmable qui stocke son programme dans une mémoire modifiable. Une instruction est codée comme les données : sous la forme de suites de bits. Telle suite de bit indique qu'il faut faire une addition, telle autre demande de faire une soustraction, etc. Pour simplifier, nous allons supposer qu'il y a une instruction par adresse mémoire. Sur la grosse majorité des ordinateurs, les instructions sont placées les unes à la suite des autres dans l'ordre où elles doivent être exécutées. Un programme informatique n'est donc qu'une vulgaire suite d'instructions stockée quelque part dans la mémoire de l'ordinateur. {|class="wikitable" |+ Exemple de programme informatique |- ! Adresse ! Instruction |- ! 0 | Copier le contenu de l'adresse 0F05 dans le registre numéro 5 |- ! 1 | Charger le contenu de l'adresse 0555 dans le registre numéro 4 |- ! 2 | Additionner ces deux nombres |- ! 3 | Charger le contenu de l'adresse 0555 |- ! 4 | Faire en XOR avec le résultat antérieur |- ! ... | ... |- ! 5464 | Instruction d'arrêt |} Pour exécuter une suite d'instructions dans le bon ordre, le processeur détermine à chaque cycle quelle est la prochaine instruction à exécuter. Pour cela, le processeur mémorise l'adresse de l'instruction en cours dans un registre : le '''Program Counter'''. Je rappelle que des instructions consécutives sont dans des adresses consécutives. Pour passer à la prochaine instruction, il suffit donc d'incrémenter le ''program counter''. : Si une instruction prend plusieurs octets, plusieurs adresses, il suffit de l'incrémenter du nombre d'octets/adresses. D'autres processeurs font autrement : chaque instruction précise l'adresse de la suivante, directement dans la suite de bit représentant l'instruction en mémoire. Ces processeurs n'ont pas besoin de calculer une adresse qui leur est fournie sur un plateau d'argent. Sur des processeurs aussi bizarres, pas besoin de stocker les instructions en mémoire dans l'ordre dans lesquelles elles sont censées être exécutées. Mais ces processeurs sont très très rares et peuvent être considérés comme des exceptions à la règle. Nous venons de voir qu'un processeur contient un registre appelé le ''program counter''. Mais il n'est pas le seul. Pour pouvoir fonctionner, tout processeur doit mémoriser un certain nombre d’informations nécessaires à son fonctionnement, qui sont mémorisées dans des '''registres de contrôle'''. La plupart ont des noms assez barbares (registre d'état, ''program counter'') et nous ne pouvons pas en parler à ce moment du cours. Nous les verrons en temps voulu, mais il est important de préciser qu'ils existent. ===L'intérieur d'un processeur=== Fort de ce que nous savons, nous pouvons expliquer ce qu'il y a à l'intérieur d'un processeur. Le premier point est qu'un processeur fait des calculs, ce qui implique qu'il contient des circuits de calcul. Ils sont regroupés dans une ou plusieurs '''unités de calcul'''. Nous avons déjà vu comment fabriquer une unité de calcul simple, dans un chapitre dédié, et c'est la même qui est présente dans un processeur. Du moins dans les grandes lignes, les circuits des processeurs modernes étant particulièrement optimisés. Il en est de même pour les autres circuits de calcul comme ceux pour les multiplications/division/autres. Si le processeur fait des calculs, qu'en est-il des opérandes ? Et où sont mémorisés les résultats des opérations ? Pour cela, le processeur incorpore des '''registres généraux'''. Les registres généraux servent à mémoriser les opérandes et résultats des instructions. Ils mémorisent des données, contrairement aux registres de contrôle mentionnés plus haut. Le nombre de registres généraux dépend grandement du processeur. Les tout premiers processeurs se débrouillaient avec un seul registre, mais les processeurs actuels utilisent plusieurs registres, pour mémoriser plusieurs opérandes/résultats. Mais la présence de registres est source de pas mal de petites complications. Par exemple, il faut échanger les données entre la RAM et les registres, il faut gérer l'adressage des registres, etc. Il s'agit là de détails que nous expliquerons dans les chapitres sur le processeur. Le processeur contient enfin un circuit pour interpréter les instructions, appelé l''''unité de contrôle'''. Elle lit les instructions depuis la mémoire, interprète la suite de bit associée, et commande le reste du processeur pour qu'il exécute l'instruction. Ses fonctions sont assez variées, mais nous allons simplifier en disant qu'elle configure l'unité de calcul et les registres pour faire le bon calcul. Son rôle est d'analyser la suite de bit qui constitue l'instruction, et d'en déduire quelle opération effectuer. Elle gère aussi l'accès à la mémoire RAM, et notamment ce qui est envoyé sur son bus d'adresse. : Dans ce qui suit, on suppose que le ''program counter'' fait partie de l'unité de contrôle. Pour résumer, un processeur contient une unité de calcul, des registres et une interface avec la mémoire RAM. Le tout est interconnecté, afin de pouvoir échanger des données. L’ensemble forme le '''chemin de données''', nom qui trahit le fait que c'est là que les données se déplacent et sont traitées. Il faut aussi ajouter l'unité de contrôle pour commander le tout. Elle lit les instructions en mémoire, puis commande le chemin de données pour que l'instruction soit exécutée correctement. [[File:Microarchitecture d'un processeur.png|centre|vignette|upright=2|Microarchitecture d'un processeur]] Un processeur parait donc assez simple expliqué comme ça, mais il y a de nombreuses subtilités. L'une d'entre elle est liée aux registres, et notamment à la manière dont on les utilise. Pour expliquer ces subtilités, nous allons voir comment les premiers processeurs fonctionnaient, avant de passer aux processeurs un peu plus modernes. Les tout premiers processeurs n'utilisaient qu'un seul registre, ce qui fait que l'utilisation des registres était très simple. Le passage à plusieurs registres a complexifié le tout. ===D'où viennent les adresses ?=== Il est maintenant temps de répondre à une question qui s'était posée dans la section sur l'adressage : d'où viennent les adresses envoyées à la mémoire ? Pour ce qui est des adresses des instructions, on connait déjà la réponse : c'est le ''program counter'' qui gère tout. Mais pour les données, il y a deux possibilités, qui correspondent à deux types de données : les données statiques et les données dynamiques. Les '''données statiques''' sont les plus simples : elles existent durant toute la durée de vie du programme. Tant que celui-ci s'exécute, il aura besoin de ces données. En conséquence, il leur réserve une place en mémoire, qui est toujours la même. La donnée se situe donc à une adresse bien précise, qui ne change jamais. Attention cependant : les données peuvent être modifiées, changer de valeur. Le programme écrit dans les donnée statiques, c'est même assez fréquent. Ce sont ne sont pas forcément des données constantes ! Pour les données statiques, elles sont toujours placées à la même adresse mémoire. Pour le dire autrement, l'adresse mémoire est une constante, qui ne change pas. L'adresse est connue avant d’exécuter le programme, le programme a été codé pour utiliser cette adresse pour telle donnée, on a réservé une adresse pour la donnée voulue. Et même si vous quittez le programme et vous le relancez plusieurs jours après, l'adresse mémoire sera la même avant et après. Et cela permet d'utiliser l''''adressage direct'''. L'idée est que les instructions précisent donc l'adresse à lire ou écrire. Pour cela, l'adresse est intégrée dans l’instruction elle-même. Pour rappel, l'instruction est codée par une suite de bit en mémoire RAM/ROM, dont certains précisent l'opération à faire, les autres servant à autre chose. L'idée est que certains bits précisent l'adresse mémoire de la donnée à lire. Les instructions sont donc du genre : * ''LOAD 56'' - lit l'adresse numéro 56 et copie son contenu dans un registre; * ''STORE R5 , adress 99'', copie le registre R5 dans l'adresse 99 ; * ''ADD R5 , adress 209'' : additionne le registre R5 avec le contenu de l'adresse 209. S'il existe des données statiques, c'est signe qu'il existe des '''données dynamiques'''. Ces dernières sont des données qui sont créées ou détruites selon les besoins. Pour comprendre d'où viennent ces données dynamiques, prenons le cas d'une personne qui écrit du texte sur un ordinateur. Le texte qu'il écrit est mémorisé dans la RAM de l’ordinateur, puis est sauvegardé sur le disque dur quand il sauvegarde son document. Au fur et à mesure qu'il écrit du texte, la RAM utilisée par ce texte augmente. Donc, ce texte est une donnée dynamique, dont la taille varie dans le temps. Pour gérer des données dynamiques, la plupart des systèmes d'exploitation incorporent des fonctionnalités d''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent réclamer de la mémoire au système d'exploitation, pour y placer les données qu'ils souhaitent. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec l'allocation mémoire, les données n'ont pas de place fixe en mémoire. Leur adresse mémoire peut varier d'une exécution du programme à l'autre. Pire que ça : les données peuvent être déplacées dans la mémoire RAM, si besoin. En clair : l'adresse est déterminée lors de l'exécution du programme. L'adresse est alors soit calculée, soit lue depuis la mémoire RAM, soit déterminée autrement. Toujours est-il qu'elle se retrouve dans un registre général. Pour gérer ces adresse variables, les processeurs utilisent l''''adressage indirect'''. L'adressage indirect permet d'utiliser une adresse qui est dans un registre de données. L'adresse en question peut être envoyée à la mémoire RAM sans problème, le processeur peut automatiquement connecter le registre adéquat sur le bus d'adresse. Au tout début de l'informatique, les processeurs ne supportaient que l'adressage direct, pas plus. L'adressage indirect n'était tout simplement pas possible. Avec l'adressage direct, l'adresse à lire est extraite directement des instructions, par l'unité de contrôle. Le bus d'adresse de la RAM est alors relié directement à l'unité de contrôle, le bus de données est relié aux registres. [[File:Architecture Harvard avec adressage direct uniquement.png|centre|vignette|upright=2|Architecture Harvard avec adressage direct uniquement]] Mais dès les années 70, l'adressage indirect est apparu, rendant la programmation bien plus simple. Mais cela a demandé quelques adaptation au niveau du processeur. Avec l'adressage indirect, le bus d'adresse est connecté aux registres. Il a donc fallu rajouter un multiplexeur pour que le processeur décide de relier le bus d'adresse soit aux registres, soit à l'unité de contrôle. ===Un ordinateur peut avoir plusieurs processeurs=== La plupart des ordinateurs n'ont qu'un seul processeur, ce qui fait qu'on désigne avec le terme d''''ordinateurs mono-processeur'''. Mais il a existé (et existe encore) des '''ordinateurs multi-processeurs''', avec plusieurs processeurs sur la même carte mère. L'idée était de gagner en performance : deux processeurs permettent de faire deux fois plus de calcul qu'un seul, quatre permettent d'en faire quatre fois plus, etc. C'est très courant sur les supercalculateurs, des ordinateurs très puissants conçus pour du calcul industriel ou scientifique, mais aussi sur les serveurs ! Dans le cas le plus courant, ils utilisent plusieurs processeurs identiques : on utilise deux processeurs Core i3 de même modèle, ou quatre Pentium 3, etc. Pour utiliser plusieurs processeurs, les programmes doivent être adaptés. Pour cela, il y a plusieurs possibilités : * Une première possibilité, assez intuitive, est d’exécuter des programmes différents sur des processeurs différents. Par exemple, on exécute le navigateur web sur un processeur, le lecteur vidéo sur un autre, etc. * La seconde option est de créer des programmes spéciaux, qui utilisent plusieurs processeurs. Ils répartissent les calculs à faire sur les différents processeurs. Un exemple est la lecture d'une vidéo sur le web : un processeur peut télécharger la vidéo pendant le visionnage et bufferiser celle-ci, un autre processeur peut décoder la vidéo, un autre décoder l'audio. De tels programmes restent des suites d'instructions, mais ils sont plus complexes que les programmes normaux, aussi nous les passons sous silence. * La troisième option est d’exécuter le même programme sur les différents processeurs, mais chaque processeur traite son propre ensemble de données. Par exemple, pour un programme de rendu 3D, quatre processeurs peuvent s'occuper chacun d'une portion de l'image. [[File:Architecture de Von Neumann Princeton multi processeurs.svg|centre|vignette|upright=2|Architecture de Von Neumann Princeton multi processeurs]] De nos jours, les ordinateurs grand public les plus utilisés sont dans un cas intermédiaire, ils ne sont ni mono-, ni multi-processeur. Ils n'ont qu'un seul processeur, dans le sens où si on ouvre l'ordinateur et qu'on regarde la carte mère, il n'y a qu'un seul processeur. Mais ce processeur est en réalité assez similaire à un regroupement de plusieurs processeurs dans le même boitier. Il s'agit de '''processeurs multicœurs''', qui contiennent plusieurs cœurs, chaque cœur pouvant exécuter un programme tout seul. La différence entre cœur et processeur est assez difficile à saisir, mais pour simplifier : un cœur est l'ensemble des circuits nécessaires pour exécuter un programme. Chaque cœur dispose de toute la machinerie électronique pour exécuter un programme, à savoir des circuits aux noms barbares comme : un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits d'un processeur ne sont présents qu'en un seul exemplaire dans un processeur multicœur, comme les circuits de communication avec la mémoire ou les circuits d’interfaçage avec la carte mère. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Un processeur double-cœur est équivalent à avoir deux processeurs dans l'ordinateur, un processeur quadruple-cœur est équivalent à avoir quatre processeurs dans l'ordinateur, etc. Ces processeurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. ===Les coprocesseurs=== Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs qui complémentaient un processeur principal. Les ordinateurs de ce type avaient un processeur principal, le '''CPU''', qui était secondé par un ou plusieurs coprocesseurs. Sauf exception, le CPU et le coprocesseur exécutent des programmes différents, ils travaillent en parallèle. Les coprocesseurs les plus connus sont les '''coprocesseurs pour le rendu 2D/3D''' et les '''coprocesseurs sonores'''. Ils ont eu leur heure de gloire sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles de cette génération ou antérieure. Ils s'occupaient respectivement de calculer les graphismes des jeux vidéos, et de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un co-processeur sonore. Le processeur principal était un Motorola 68000, alors que le co-processeur sonore était un processeur Z80. L'accès aux périphériques est quelque chose sur lequel nous passerons plusieurs chapitres dans ce cours. Mais sachez que l'accès aux périphériques peut demander pas mal de puissance de calculs. Le CPU principal peut faire ce genre de calculs par lui-même, mais il n'est pas rare qu'un '''coprocesseur d'IO''' soit dédié à l'accès aux périphériques. Un exemple assez récent est celui de la console de jeu Nintendo 3DS. Elle disposait d'un processeur principal de type ARM9, d'un coprocesseur pour les divisions qu'on abordera plus bas, et d'un second processeur ARM7. L'ARM 7 était utilisé comme coprocesseur d'I/O, ainsi que pour l'émulation de la console GBA. [[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]] Les '''coprocesseurs arithmétiques''' sont un peu à part des autres. Ils permettent de faire certains calculs que le processeur ne peut pas faire. Les plus connus d'entre eux étaient utilisés pour implémenter les calculs en virgule flottante, à une époque où les CPU de l'époque ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Les coprocesseurs arithmétiques étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans co-processeur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Sans ces coprocesseurs, les calculs flottants étaient émulés en logiciel, par des fonctions et libraires spécialisées, très lentes. Certaines applications conçues pour le coprocesseur étaient capables d'en tirer profit : des logiciels de conception assistée par ordinateur, par exemple. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants. Un exemple récent de coprocesseur est celui utilisé sur la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques. Les co-processeurs arithmétiques se distinguent des autres car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les co-processeurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les co-processeurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du co-processeur. Les instructions sont exécutées soit par le CPU, soit par le co-processeur, une par une. En clair, le CPU et le co-processeur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''. ==Les entrées-sorties== Tous les circuits vus précédemment traitent des données codées en binaire. Ceci dit, les données ne sortent pas de n'importe où : l'ordinateur contient des composants électroniques qui traduisent des informations venant de l’extérieur en nombres. Ces composants sont ce qu'on appelle des '''entrées'''. Par exemple, le clavier est une entrée : l'électronique du clavier attribue un nombre entier (''scancode'') à une touche, nombre qui sera communiqué à l’ordinateur lors de l'appui d'une touche. Pareil pour la souris : quand vous bougez la souris, celle-ci envoie des informations sur la position ou le mouvement du curseur, informations qui sont codées sous la forme de nombres. La carte son évoquée il y a quelques chapitres est bien sûr une entrée : elle est capable d'enregistrer un son, et de le restituer sous la forme de nombres. S’il y a des entrées, on trouve aussi des '''sorties''', des composants électroniques qui transforment des nombres présents dans l'ordinateur en quelque chose d'utile. Ces sorties effectuent la traduction inverse de celle faite par les entrées : si les entrées convertissent une information en nombre, les sorties font l'inverse : là où les entrées encodent, les sorties décodent. Par exemple, un écran LCD est un circuit de sortie : il reçoit des informations, et les transforme en image affichée à l'écran. Même chose pour une imprimante : elle reçoit des documents texte encodés sous forme de nombres, et permet de les imprimer sur du papier. Et la carte son est aussi une sortie, vu qu'elle transforme les sons d'un fichier audio en tensions destinées à un haut-parleur : c'est à la fois une entrée, et une sortie. Les '''entrées-sorties''' incluent toutes les entrées et sorties, et même certains composants qui sont les deux à la fois. Il s'agit d'un terme générique, qui regroupe des composants forts différents. Dans ce qui va suivre, nous allons parfois parler de périphériques au lieu d'entrées-sorties, mais les deux termes ne sont pas équivalents. Dans le détail, les entrées-sorties regroupent : * Les '''périphériques''' sont les composants connectés sur l'unité centrale. Exemple : les claviers, souris, webcam, imprimantes, écrans, clés USB, disques durs externes, la Box internet, etc. * Les '''cartes d'extension''', qui se connectent sur la carte mère via un connecteur, comme les cartes son ou les cartes graphiques. * D'autres composants sont soudés à la carte mère mais sont techniquement des entrées-sorties : les cartes sons soudées sur les cartes mères actuelles, par exemple. ===L'interface avec le reste de l'ordinateur=== Les entrées-sorties sont très diverses, fonctionnent très différemment les unes des autres. Mais du point de vue du reste de l'ordinateur, les choses sont relativement standardisées. Du point de vue du processeur, les entrées-sorties sont juste des paquets de registres ! Tous les périphériques, toutes les entrées-sorties contiennent des '''registres d’interfaçage''', qui permettent de faire l'intermédiaire entre l'entrée/sortie et le reste de l'ordinateur. L'entrée/sortie est conçu pour réagir automatiquement quand on écrit dans ces registres. [[File:Registres d'interfaçage.png|centre|vignette|upright=2|Registres d'interfaçage.]] Les registres d’interfaçage sont assez variés. Les plus évidents sont les '''registres de données''', qui permettent l'échange de données entre le processeur et les périphériques. Pour échanger des données avec l'entrée/sortie, le processeur a juste à lire ou écrire dans ces registres de données. On trouve généralement un registre de lecture et un registre d'écriture, mais il se peut que les deux soient fusionnés en un seul registre d’interfaçage de données. Si le processeur veut envoyer une donnée à une entrée/sortie, il a juste à écrire dans ces registres. Inversement, s'il veut lire une donnée, il a juste à lire le registre adéquat. Mais le processeur ne fait pas que transmettre des données à l'entrée/sortie. Le processeur lui envoie aussi des « commandes », des valeurs numériques auxquelles l'entrée/sortie répond en effectuant un ensemble d'actions préprogrammées. En clair, ce sont l'équivalent des instructions du processeur, mais pour l'entrée/sortie. Par exemple, les commandes envoyées à une carte graphique peuvent être : affiche l'image présente à cette adresse mémoire, calcule le rendu 3D à partir des données présentes dans ta mémoire, etc. Pour recevoir les commandes, l'entrée/sortie contient des ''registres de commande'' qui mémorisent les commandes envoyées par le processeur. Quand le processeur veut envoyer une commande à l'entrée/sortie, il écrit la commande en question dans ce ou ces registres. Enfin, beaucoup d'entrée/sortie ont un ''registre d'état'', lisible par le processeur, qui contient des informations sur l'état de l'entrée/sortie. Ils servent notamment à indiquer au processeur que l'entrée/sortie est disponible, qu'il est en train d’exécuter une commande, qu'il est occupé, qu'il y a un problème, qu'il y a une erreur de configuration, etc. ===Les adresses des registres d’interfaçage=== Les registres des périphériques sont identifiés par des adresses mémoires. Et les adresses sont conçues de façon à ce que les adresses des différentes entrées/sorties ne se marchent pas sur les pieds. Chaque entrée/sortie, chaque registre, chaque contrôleur a sa propre adresse. D'ordinaire, certains bits de l'adresse indiquent quel est le destinataire. Certains indiquent quel est l'entrée/sortie voulue, les restants indiquant le registre de destination. Il existe deux organisations possibles pour les adresses des registres d’interfaçages. La première possibilité est de séparer les adresses pour les registres d’interfaçage et les adresses pour la mémoire. Le processeur doit avoir des instructions séparées pour gérer les périphériques et adresser la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. [[File:Espaces d'adressages séparés entre mémoire et périphérique.png|centre|vignette|upright=2.5|Espaces d'adressages séparés entre mémoire et périphérique]] L'autre méthode mélange les adresses mémoire et des entrées-sorties. Si on prend par exemple un processeur de 16 bits, où les adresses font 16 bits, alors les 65536 adresses possibles seront découpées en deux portions : une partie ira adresser la RAM/ROM, l'autre les périphériques. On parle alors d''''entrées-sorties mappées en mémoire'''. L'avantage est que le processeur n'a pas besoin d'avoir des instructions séparées pour les deux. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] Pour résumer, communiquer avec une entrée/sortie est similaire à ce qu'on a avec les mémoires. Il suffit de lire ou écrire dans des registres d’interfaçage, qui ont chacun une adresse mémoire. Le problème est que le système d'exploitation ne connaît pas toujours le fonctionnement d'une entrée/sortie : il faut installer un programme qui va s'exécuter quand on souhaite communiquer avec l'entrée/sortie, et qui s'occupera de tout ce qui est nécessaire pour le transfert des données, l'adressage du périphérique, etc. Ce petit programme est appelé un driver ou '''pilote de périphérique'''. La « programmation » périphérique est très simple : il suffit de savoir quoi mettre dans les registres, et c'est le pilote qui s'en charge. ==Les architectures Harvard et Von Neumann== Après avoir vu le processeur, les mémoires et les entrées-sorties, voyons voir comment le tout est interconnecté. Tous les ordinateurs ne sont pas organisés de la même manière, pour ce qui est de leurs bus. Mais pour comprendre pourquoi, nous devons regarder qui communique avec qui, dans un ordinateur. Pour rappel, les données sont placées en mémoire RAM, alors que les instructions sont placées en mémoire ROM. Le processeur lit des instructions dans la mémoire ROM, il lit et écrit dans la mémoire RAM, et accède aux registres d’interfaçage des entrées-sorties. Il y a donc besoins de trois interconnexions : CPU-ROM, CPU-RAM et CPU-IO. [[File:Réseau d'interconnexion avec un processeur au centre.png|centre|vignette|upright=2|Réseau d'interconnexion avec un processeur au centre]] Il parait intéressant d'utiliser trois interconnexions, au minimum CPU-ROM, CPU-RAM et CPU-IO. Néanmoins, faire ainsi a de nombreux désavantages. Déjà, il faut pouvoir brancher tout ça sur le processeur. Et celui-ci n'a pas forcément assez de broches pour. Aussi, il est parfois préférable de mutualiser des bus, à savoir de connecter plusieurs composants sur un même bus. Par exemple, on peut mutualiser le bus pour la mémoire RAM et pour la mémoire ROM. Il faut dire que les deux bus sont des bus mémoire, avec un bus d'adresse, un bus de données, et surtout : des bus de commande similaires. Les mutualiser est alors très simple, et permet d'économiser pas mal de broches. [[File:Réseau d'interconnexion avec un processeur au centre et une architecture Harvard.png|centre|vignette|upright=2|Réseau d'interconnexion avec un processeur au centre et une architecture Harvard]] Cette mutualisation nous amène naturellement à parler de la distinction entre les architectures Harvard d'un côté et les architectures Von Neumann de l'autre. Elle est très liée au fait d'utiliser soit un bus mémoire unique, soit des bus séparés pour la ROM et la RAM. Voyons cela en détail. ===Les architectures Harvard et Von Neumann : des bus séparés ou unifiés=== Avec l''''architecture Harvard''', la mémoire ROM et la mémoire RAM sont reliées au processeur par deux bus séparés. Il y a un bus RAM pour la mémoire RAM, un bus ROM pour la mémoire ROM. L'avantage de cette architecture est qu'elle permet de charger une instruction et une donnée simultanément : une instruction chargée sur le bus relié à la mémoire programme, et une donnée chargée sur le bus relié à la mémoire de données. Et cela simplifie fortement la conception du processeur. [[File:Harvard Architecture.png|centre|vignette|upright=2|Architecture Harvard, avec une ROM et une RAM séparées.]] Avec l''''architecture Von Neumann''', mémoire ROM et mémoire RAM sont reliées au processeur par un bus unique. Le bus unique qui relie processeur, RAM et ROM, s'appelle le '''bus mémoire'''. Un défaut de ces architecture est qu'elles ne peuvent pas charger une instruction et une donnée en même temps. Et cela pose quelques problèmes pour la conception du processeur. Par contre, nous verrons dans ce qui suit qu'utiliser un bus mémoire partagé est bien plus flexible et permet des choses que les architectures Harvard ne peuvent pas faire. [[File:Architecture Von Neumann, avec deux bus séparés.png|centre|vignette|upright=2|Architecture Von Neumann, avec deux bus séparés.]] ===Les architectures Harvard et Von Neumann : des espaces d'adressage séparés ou unifiés=== La distinction précédente se base sur les connexions entre RAM, ROM et processeur. Mais il existe une autre distinction, très liée, qui est souvent utilisée comme seconde définition des architectures Harvard/Von Neumann. Elle est liée aux adresses mémoire que le processeur peut gérer. Prenons un processeur 16 bits, par exemple, qui gère naturellement des adresses de 16 bits. Il peut gérer 2^16 adresses, soit 64 kibioctets de mémoire. L'ensemble de ces adresses est appelé un '''espace d'adressage'''. Mais comment cet espace d'adressage est utilisé pour adresser une RAM et une ROM ? Sur les architectures Harvard, le processeur voit deux mémoires séparées avec leur lot d'adresses distinctes. Une même adresse peut donc correspondre soit à la mémoire ROM, soit à la mémoire RAM, suivant le bus utilisé. L'espace d'adressage est donc doublé, dupliqué, avec un pour la ROM, un autre pour la RAM. Rien d'étonnant à cela : il y a deux bus d'adresses, chacun correspondant à un espace d'adressage. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] Avec l'architecture Von Neumann, la RAM et la ROM doivent se partager les adresses mémoires disponibles. Il n'y a qu'un seul espace d'adressage qui est coupé en deux, avec une partie pour la ROM et une autre pour la RAM. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM occupe une partie des adresses, la mémoire RAM utilise le reste. La répartition des adresses est réalisée par les circuits de décodage d'adresse mentionnés plus haut. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] Les '''architectures Harvard modifiées''' sont des intermédiaires entre architectures Harvard et architectures Von Neumann, bien qu'elles penchent bien plus du côté des architectures Harvard. Précisons que la terminologie n'est pas claire, beaucoup d'auteurs mettent des définitions différentes derrière ces deux termes. Mais dans ce cours, nous utiliserons une définition très stricte de ce qu'est une architecture Harvard modifiée. Une architecture Harvard modifiée est une architecture Harvard, où le processeur peut lire des données constantes depuis la mémoire ROM. Nous avions vu plus haut que les mémoires ROM peuvent mémoriser, en plus d'un programme exécutable, des données constantes, qui ne varient pas. Les architectures Harvard pures ne permettent pas de lire des données de ce genre depuis la mémoire ROM, alors que les architectures Harvard modifiées le permettent. Une architecture Harvard modifiée dispose d'une instruction pour lire les données en mémoire RWM, et d'une instruction pour lire des données en mémoire ROM. Il y a donc deux versions de l'instruction LOAD, qui copient la donnée dans un registre général, mais dont la source de la donnée est différente. Une autre possibilité, plus rare, est que une instruction de copie, qui copie une constante depuis la mémoire ROM vers la mémoire RAM. Le cas le plus commun est l'utilisation de deux instructions LOAD séparées. [[File:Espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=2.5|Espaces d'adressage sur une archi harvard modifiée]] Ceci étant dit, revenons à la distinction entre architecture Harvard et Von Neumann. Il faut noter que la RAM et la ROM n'ont pas forcément la même taille. Et ce que ce soit sur une architecture Harvard que sur une architecture Von Neumann, mais c'est plus facile à expliquer sur une architecture Harvard. On peut par exemple imaginer une architecture Harvard qui utilise des adresses de 16 bits pour la ROM, et seulement 8 bits pour la RAM. Le résultat est qu'il peut adresser 64 kibioctets de ROM, mais seulement 256 octets de RAM. Les deux bus d'adresse sont alors de taille différente, l'un faisant 8 bits, l'autre 16. Quelques processeurs 8 bits étaient dans ce cas, comme on le verra dans le chapitre sur les CPU 8bits. Mais d'autres processeurs utilisent des valeurs différentes, avec par exemple des adresses de 16 bits pour la RAM, mais de 20 bits pour la ROM, ou inversement. Sur une architecture Von Neumann, tout dépend de comment les adresses sont réparties. La solution la plus simple découpe l'espace d'adressage en deux parties égales, avec la RAM qui est dans la moitié basse (qui part de l'adresse 0 jusqu'à l'adresse au milieu), alors que la ROM est dans la moitié haute (entre l'adresse du milieu et l'adresse maximale). Mais ce n'est pas la seule possibilité, la limite entre RAM et ROM peut être mise n'importe où. Prenons par exemple un processeur 32 bits, capable de gérer 4 milliards d'adresse. Il est parfaitement possible de réserver 128 mébioctets de poids fort à la mémoire ROM, et de laisser le reste à la mémoire RAM. ===Le décodage d'adresse sur les architectures Von Neumann=== Pour résumer, les architectures Harvard et Von Neumann se distinguent sur deux points : * L'accès à la RAM et à la ROM se font par des bus séparés sur l'architecture Harvard, sur le même bus avec l'architecture Von Neumann. * Les adresses pour la mémoire ROM et la mémoire RAM sont séparées sur les architectures Harvard, partagées sur l’architecture Von Neumann. Les architectures Von Neumann utilisent donc un seul bus pour connecter la RAM et la ROM au processeur. Mais cela ne parait pas intuitif : comment deux composants peuvent se connecter aux mêmes fils ? Parce que c'est ce qu'implique le fait de partager un bus. Si je prends une mémoire RAM et une mémoire ROM, toutes deux de 8 bits, elles seront connectées à un bus mémoire de 8 bits. Intuitivement, on se dit qu'il y aura des conflits, du genre : la RAM et la ROM vont accéder au bus en même temps, comment savoir si une adresse est destinée à la RAM ou la ROM, etc ? Tous ces problèmes sont résolus avec une solution très simple : à chaque instant, seule une mémoire est connectée au bus. L'idée est que les mémoires sont connectées ou déconnectées du bus selon les besoins. Si le processeur veut envoyer lire une donnée en mémoire RAM, il déconnecte la mémoire ROM du bus. Et inversement, s'il veut lire une instruction, il déconnecte la RAM et connecte la ROM. Pour cela, les mémoires RAM et ROM possèdent une entrée ''Chip Select'' ou ''Output Enable'', qui agit comme une sorte de bouton ON/OFF. Lorsqu'on met un 1 sur cette entrée, la mémoire se connectera au bus. Ses entrées et sorties fonctionneront normalement, elle pourra recevoir des adresses, envoyer ou recevoir des données, tout sera normal. Par contre, si on met un 0 sur cette entrée, la mémoire se "désactive", ses entrée-sorties ne répondent plus aux sollicitations extérieures. Pire que ça : elles sont électriquement déconnectées. Au total, tout cela demande de gérer deux bit ''Chip Select''/''Output Enable'' : un pour la RAM, un pour la ROM. Et ces deux bits sont configurés pour chaque accès mémoire, pour chaque lecture ou écriture. Pour cela, un circuit de '''décodage d'adresse''' prend en entrée l'adresse mémoire à lire/écrire, et active/désactive les mémoires RAM/ROM selon les besoins. Il prend l'adresse et configure les bits ''Chip Select''/''Output Enable''. [[File:Décodage d'adresse sur une architecture Von Neumann.png|centre|vignette|upright=2|Décodage d'adresse sur une architecture Von Neumann.]] L'implémentation la plus simple réserve la moitié des adresses pour la RAM, l'autre moitié pour la ROM. Typiquement, la ROM prend la moitié basse, la RAM la moitié haute. Dans ce cas, activer/désactiver la RAM et la ROM se fait avec seulement le bit de poids fort de l'adresse. Si le bit de poids fort est à 1, alors on accède à la RAM et la ROM doit être désactivée. Mais si ce bit est à 0, alors on accède à la moitié basse et il faut désactiver la RAM. Une remarque intéressante : le fait de séparer la mémoire en deux parts égales permet de simuler une architecture Harvard à partir d'une architecture Von Neumann. Par exemple, le tout premier processeur d'Intel, le 4004, était l'un de ceux là. La RAM et la ROM sont reliés au même bus, et il y a donc un unique espace d'adressage, qui est séparé en deux parties égales. Le truc est que le processeur traite les deux parties égales comme deux espaces d'adressage séparés. Le processeur se débrouille pour cacher le fait qu'il y a un espace d'adressage unique coupé en deux, ce qui fait que les programmeurs voient bien deux espaces d'adressages distincts. [[File:Décodage d'adresse sur une architecture Von Neumann basique.png|centre|vignette|upright=2|Décodage d'adresse sur une architecture Von Neumann basique.]] Pour résumer, quand une adresse est envoyée sur le bus, les deux mémoires vont la recevoir mais une seule va répondre et se connecter au bus. Le décodage d'adresse garantit que seule la mémoire adéquate réponde à un accès mémoire. Le décodage d'adresse est réalisé par la carte mère, par un composant dédié. Le mécanisme peut être utilisé pour combiner plusieurs RAM en une seule, idem avec les ROM. Pour comprendre l'idée, je vais prendre l'exemple de l'IBM PC, un des tout premier PC existant. Nous étudierons ce PC dans une section dédiée, à la fin du chapitre, aussi je vais passer rapidement dessus. Tout ce que je vais faire est vous présenter la carte mère du PC, et vous demander de faire est de compter les mémoires ROM et mémoires RAM sur la carte mère : [[File:IBM 5150 Motherboard.svg|centre|vignette|upright=3|Carte mère de l'IBM 5150, un modèle de l'IBM PC.]] Si vous remarquerez qu'il y a 5 mémoires ROM et 8 à 32 mémoires RAM. Le fait est que le processeur voit les différentes mémoires ROM comme une seule mémoire ROM. Idem avec les mémoires RAM : elle font chacune 2 kibioctets, et l'ensemble est vu par le processeur comme une seule RAM de 16 à 64 kibioctets. Et cela grâce aux circuits de décodage d'adresse, qui sont situés en haut à droite de la carte mère. Pour comprendre l'idée, prenons l'exemple d'un processeur 16 bits, capable de gérer 64 kibioctets de mémoire. L'espace d'adressage est découpé en quatre portions, de 16 kibioctets chacune. Une portion est réservée à une ROM de 16 kibioctet, les autres sont chacune réservée à une RAM de 16 kibioctet. Le décodage d'adresse sélectionne alors la mémoire adéquate en utilisant les deux bits de poids fort de l'adresse. * S'ils valent 00, alors c'est la mémoire ROM qui est activée, connectée au bus. * S'ils valent 01, alors c'est la première mémoire RAM qui est connectée au bus. * S'ils valent 10, alors c'est la seconde mémoire RAM qui est connectée au bus. * S'ils valent 11, alors c'est la troisième mémoire RAM qui est connectée au bus. [[File:Décodage d'adresse sur une architecture Von Neumann, utilisant plusieurs RAM et une ROM.png|centre|vignette|upright=3|Décodage d'adresse sur une architecture Von Neumann, utilisant plusieurs RAM et une ROM]] ===L'impact sur la conception du processeur=== Plus haut, j'ai parlé d'un des avantages des architectures Harvard : elles peuvent lire une instruction en même temps qu'elles accèdent à une donnée. La donnée est lue/écrite en RAM, alors que l'instruction est lue en ROM. Et cela permet de simplifier l'intérieur du processeur. Pas de beaucoup, mais c'est déjà ça de pris. Voyons maintenant comment cela impacte l'intérieur du processeur. Tout ce dont vous avez à vous rappeler est la séparation entre chemin de données et unité de contrôle, et que les registres généraux sont dans le premier, le ''program counter'' dans la seconde. Avec une architecture Harvard, les instructions et les données passent par des bus différent : bus ROM pour les instructions, bus RAM pour les données. L'intuition nous dit que le bus pour la mémoire ROM est connecté à l'unité de contrôle, alors que le bus pour la RAM est connecté au chemin de données. Et dans les grandes lignes, c'est vrai. La logique est imparable pour ce qui est des bus de données. Mais il y a une petite subtilité pour les bus d'adresse. Pour comprendre comment le processeur exploite ces deux bus, voyons ce qui transite dessus. Pour la mémoire ROM, elle reçoit l'adresse de l'instruction à lire, elle renvoie l'instruction adéquate. Pour cela, le ''program counter'' est envoyé sur le bus d'adresse, l'instruction sur le bus de données. Pour la mémoire RAM, elle échange des données avec les registres généraux, les registres pour les données. Les adresses utilisées pour la RAM viennent elles soit du chemin de données, soit de l'unité de contrôle, tout dépend du mode d'adressage. Mais le ''program counter'' n'est pas impliqué. [[File:Architecture Harvard - échanges de données.png|centre|vignette|upright=2|Architecture Harvard - échanges de données]] Les architectures Harvard modifiées doivent cependant rajouter une connexion entre le bus ROM et les registres généraux. C'est nécessaire pour charger une donnée constante depuis la mémoire ROM. Rappelons que la donnée constante est copiée dans un registre général, donc dans le chemin de données. [[File:Architecture Harvard modifiée - implémentation du processeur.png|centre|vignette|upright=2|Architecture Harvard modifiée - implémentation du processeur]] Avec les architectures Von Neumann, il y a un seul bus qui est relié à la fois au chemin de données et à l'unité de contrôle. Si le processeur lit une instruction, le bus doit être relié à l'unité de contrôle. Par contre, s'il accède à une donnée, il doit être relié au chemin de données (le bus d'adresse peut éventuellement être connecté au séquenceur, si celui-ci fournit l'adresse à lire). Il faut donc utiliser un paquet de multiplexeurs et de démultiplexeurs pour faire la connexion au bon endroit. [[File:Architecture Von Neumann - implémentation du processeur.png|centre|vignette|upright=2|Architecture Von Neumann - implémentation du processeur]] Une instruction se fait en deux temps : on charge l'instruction depuis la mémoire ROM, puis on l'exécute. Avec une architecture Harvard, tout cela se fait en un seul cycle d'horloge, vu que charger la ROM et accéder aux données peut se faire en même temps. Pas avec les architectures Von Neumann, qui doivent libérer le bus mémoire après avoir chargé une instruction. Elles n'ont pas le choix : elles chargent l'instruction lors d'un premier cycle d'horloge, puis l'exécutent lors du second. Pour cela, ils incorporent un registre appelé le '''registre d'instruction''', qui mémorise l'instruction chargée. L'instruction est copiée dans ce registre lors du premier cycle, puis est utilisée lors du second cycle. Le registre permet de ne pas oublier l’instruction entre les deux cycles. Le registre d'instruction est obligatoire sur les architectures Von Neumann. En comparaison, il est facultatif sur les architectures Harvard. Elles peuvent en avoir un, pour des raisons techniques, mais ce n'est pas obligatoire. [[File:Registre d'instruction.png|centre|vignette|upright=2|Registre d'instruction.]] ===Les architectures Von Neumann sont plus flexibles=== Sur les architectures Harvard, le processeur sait faire la distinction entre programme et données. Les données sont stockées dans la mémoire RAM, le programme est stocké dans la mémoire ROM. Les deux sont séparés, accédés par le processeur sur des bus séparés, et c'est ce qui permet de faire la différence entre les deux. Il est impossible que le processeur exécute des données ou modifie le programme. Du moins, tant que la mémoire qui stocke le programme est bien une ROM. Par contre, sur les architectures Von Neumann, il est impossible de distinguer programme et données, sauf en ajoutant des techniques de protection mémoire avancées. La raison est qu'il est impossible de faire la différence entre donnée et instruction, vu que rien ne ressemble plus à une suite de bits qu'une autre suite de bits. Et c'est à l'origine d'un des avantages majeur de l'architecture Von Neumann : il est possible que des programmes soient copiés dans la mémoire RWM et exécutés dans celle-ci. Un cas d'utilisation familier est celui de votre ordinateur personnel. Le système d'exploitation et les autres logiciels sont copiés en mémoire RAM à chaque fois que vous les lancez. Mais cet exemple implique un disque dur, ce qui rend les choses plus compliquées que prévu. Un autre exemple serait la compilation de code à la volée, mais il ne sera pas très parlant. Un exemple plus adapté serait celui où la ROM mémorise un programme compressée dans la mémoire ROM, qui est décompressé pour être exécuté en mémoire RAM. Le programme de décompression est stocké en mémoire ROM et est exécuté au lancement de l’ordinateur. Cette méthode permet d'utiliser une mémoire ROM très petite et très lente, tout en ayant un programme rapide (si la mémoire RWM est rapide). Il est aussi possible de créer des programmes qui modifient leurs propres instructions : cela s'appelle du '''code auto-modifiant'''. Ce genre de choses servait autrefois sur des ordinateurs rudimentaires, au tout début de l'informatique. A l'époque, les adresses à lire/écrire devaient être écrites en dur dans le programme, dans les instructions exécutées. Pour gérer certaines fonctionnalités des langages de programmation qui ont besoin d'adresses modifiables, comme les tableaux, on devait corriger les adresses au besoin avec du code auto-modifiant. De nos jours, le code automodifiant est utilisée occasionnellement pour rendre un programme indétectable dans la mémoire (les virus informatiques utilisent beaucoup ce genre de procédés). L'impossibilité de séparer données et instructions est à l'origine de problèmes assez fâcheux. Il est parfaitement possible que le processeur charge et exécute des données, qu'il prend par erreur pour des instructions. C'est le cas quand des pirates informatiques arrivent à exploiter des bugs. Il arrive que des pirates informatiques vous fournissent des données corrompues, qui contiennent un virus ou un programme malveillant est caché dans les données. Les bugs en question permettent d'exécuter ces données, donc virus. Pour éviter cela, le système d'exploitation peut marquer certaines zones de la mémoire comme non-exécutable, c’est-à-dire que le système d'exploitation interdit d’exécution de quoi que ce soit qui est dans cette zone. Mais ce n'est pas parfait. Toujours est-il que tout cela est impossible sur les architectures Harvard. Et ce serait très limitant. Imaginez : pas possible de lancer un programme depuis le disque dur ou une clé USB, le programme doit impérativement être dans une mémoire ROM, pas de compilation à la volée, etc. Que des techniques très utilisées dans l'informatique moderne. Malgré ses défauts, les architectures Von Neumann ne sont pas les plus utilisées pour rien. Les architectures Harvard sont concrètement utilisées uniquement dans l'informatique embarquée, sur des microcontrôleurs très spécifiques. ==Le bus de communication avec les entrées-sorties== Le processeur, la mémoire et les entrées-sorties sont connectées par un ou plusieurs '''bus de communication'''. Ce bus n'est rien d'autre qu'un ensemble de fils électriques sur lesquels on envoie des zéros ou des uns. Pour communiquer avec la mémoire, il y a trois prérequis qu'un bus doit respecter : pouvoir sélectionner la case mémoire (ou l'entrée-sortie) dont on a besoin, préciser à la mémoire s'il s'agit d'une lecture ou d'une écriture, et enfin pouvoir transférer la donnée. Pour cela, on doit donc avoir trois bus spécialisés, bien distincts, qu'on nommera le bus de commande, le bus d'adresse, et le bus de donnée. * Le '''bus de données''', sur lequel s'échangent les données entre les composants. * Le '''bus de commande''' pour configurer la mémoire et les entrées-sorties. * Le '''bus d'adresse''', facultatif, permet de préciser quelle adresse mémoire il faut lire/écrire. Chaque composant possède des entrées séparées pour le bus d'adresse, le bus de commande et le bus de données. Par exemple, une mémoire RAM possédera des entrées sur lesquelles brancher le bus d'adresse, d'autres sur lesquelles brancher le bus de commande, et des broches d'entrée-sortie pour le bus de données. Précisons cependant que le bus de commande n'est pas exactement le même entre des mémoires RAM/ROM et des entrées-sorties. [[File:Bus general schematic.svg|centre|vignette|upright=2|Contenu d'un bus, généralités.]] ===Le réseau d'interconnexion : généralités=== Reprenons où nous nous étions arrêté. Avant de voir les architectures Harvard et Von Neumann, nous avions dit que le processeur, les mémoires et les entrées-sorties sont reliées entre eux par un réseau d'interconnexion. Nous venons de voir qu'il est possible de mutualiser certains bus, notamment celui de la mémoire RAM et celui de la mémoire ROM. Mais il est possible de faire la même chose pour les entrées-sorties. Là encore, il est possible de regrouper le bus mémoire avec les bus pour les entrées-sorties. Voyons ce que cela implique. {| |[[File:Réseau d'interconnexion avec un processeur au centre.png|centre|vignette|upright=2|Réseau d'interconnexion avec une architecture Harvard.]] |[[File:Réseau d'interconnexion avec un processeur au centre et une architecture Harvard.png|centre|vignette|upright=2|Interconnexions d'une architecture Von Neumann.]] |} Avant de poursuivre, nous devons préciser quelque chose d'important. Sur les ordinateurs modernes, les entrées-sorties peuvent accéder à la mémoire RAM. Les ordinateurs modernes intègrent des techniques de '''''Direct Memory Access''''' (DMA) qui permettent aux entrées-sorties de lire ou d'écrire en mémoire RAM. Les transferts DMA se font sans intervention du processeur. Ils permettent de copier un bloc de plusieurs octets, dans deux sens : de la mémoire RAM vers une entrée-sortie, ou inversement. Le DMA demande d'ajouter un circuit dédié sur la carte mère : le contrôleur DMA. Il effectue la copie d'un paquet d'octets de la RAM vers l'entrée-sortie ou dans l'autre sens. [[File:Réseau d'interconnexion avec un processeur au centre, et direct memory access.png|centre|vignette|upright=2|Réseau d'interconnexion avec un processeur au centre, et direct memory access]] ===Les bus systèmes=== La première solution utilise un bus unique, celui-ci est appelé le '''bus système''', aussi appelé ''backplane bus''. Le bus système est connecté à la mémoire RAM, la mémoire ROM, au processeur, et aux entrées-sorties. Tous les composants présents dans l'ordinateur sont connectés à ce bus, sans exception. De tels bus avaient pour avantage la simplicité. Le processeur n'est connecté qu'à un seul bus, ce qui utilise peu de broches et économise des fils. La mutualisation des bus est totale, le câblage est plus simple, la fabrication aussi. [[File:Architecture minimale d'un ordinateur.png|centre|vignette|upright=2|Architecture minimale d'un ordinateur.]] Un bus système contient un bus d'adresse, de données et de commande. Un bus système se marie bien avec des entrées-sorties mappées en mémoire. La conséquence est que le bus d'adresse ne sert pas que pour l'accès à la mémoire RAM/ROM, mais aussi pour l'accès aux entrées-sorties. Il y a moyen d'implémenter un système d'adresse séparés avec, mais c'est pas l'idéal. [[File:Architecture Von Neumann avec les bus.png|centre|vignette|upright=2|Architecture Von Neumann avec les bus.]] Un bus système n'a pas de limitations quant aux échanges de données. Le processeur peut communiquer directement avec les mémoires et les entrées-sorties, les entrées-sorties peuvent communiquer avec la mémoire RAM, etc. Notamment, un bus système peut implémenter le ''Direct Memory Access''. Il suffit juste de connecter un contrôleur DMA sur le bus système. Le contrôleur DMA est considéré comme une entrée-sortie, ses registres sont mappés en mémoire et sont donc accessibles directement par le processeur. [[File:Bus système avec controleur DMA.png|centre|vignette|upright=2|Bus système avec contrôleur DMA.]] Si on suit la définition à la lettre, un bus système est systématiquement une architecture Von Neumann, vu que la mémoire ROM et la mémoire RAM sont reliées sur le bus système. La conséquence est que les circuits de décodage d'adresse sont présents. Ils sont toujours sur la carte mère, et sont plus ou moins à côté du bus système. Cependant, le décodage d'adresse est parfois étendu pour tenir compte des entrées-sorties. Les entrées-sorties soudées sur la carte mère ont elles aussi des entrées ''Chip Select'' ou quelque chose de similaire. Le décodage d'adresse peut alors les activer ou les désactiver suivant l'adresse envoyée sur le bus d'adresse. C'est ce qui arrive quand le processeur écrit dans un registre d’interfaçage : il envoie l'adresse de ce registre sur le bus d'adresse, le circuit de décodage d'adresse active seulement l'entrée-sortie associée. Il faut noter que ce n'est pas systématique, il existe des techniques pour se passer de décodage d'adresse. Mais nous en reparlerons dans le chapitre sur les bus de communication. [[File:Chipselectfr.png|centre|vignette|upright=1.5|Exemple détaillé.]] Les bus systèmes sont certes très simples, mais ils ont aussi des désavantages. Par exemple, il faut éviter que le processeur et les entrées-sorties se marchent sur les pieds, ils ne peuvent pas utiliser le bus en même temps. De tels conflits d'accès au bus système sont fréquents et ils réduisent la performance, comme on le verra dans le chapitre sur les bus. De plus, un bus système a le fâcheux désavantage de relier des composants allant à des vitesses très différentes : il arrivait fréquemment qu'un composant rapide doive attendre qu'un composant lent libère le bus. Le processeur était le composant le plus touché par ces temps d'attente. Elle était utilisée sur les tout premiers ordinateurs, pour sa simplicité. Elle était parfaitement adaptée aux anciens composants, qui allaient tous à la même vitesse. De nos jours, les ordinateurs à haute performance ne l'utilisent plus trop, mais elle est encore utilisée sur certains systèmes embarqués, en informatique industrielle dans des systèmes très peu puissants. ===Les bus d'entrées-sorties=== Les bus systèmes ont de nombreux problèmes, ce qui fait que d'anciens ordinateurs faisaient autrement. A la place d'un bus système unique, ils utilisent un bus séparé pour les mémoires, et un autre séparé pour les entrées-sorties. Le bus spécialisé pour la mémoire est appelé le '''bus mémoire''', l'autre bus est appelé le '''bus d'entrées-sorties'''. Le bus mémoire est généralement relié à la fois à la mémoire RAM et à la mémoire ROM, les exceptions ne sont pas rares, cependant. [[File:Bus mémoire séparé du bus pour les IO.png|centre|vignette|upright=2|Bus mémoire séparé du bus pour les IO]] Les bus d'entrée-sorties peuvent être spécialisés et simplifiés. Par exemple, ils peuvent avoir un bus de commande différent de celui de la mémoire, qui utilise nettement moins de fils. Le bus d'adresse peut aussi être réduit, et utiliser des adresses plus courtes que celles du bus mémoire. Les bus de données peuvent aussi être de taille différentes. Il est ainsi possible d'avoir un bus mémoire capable de lire/écrire 64 bits à la fois, alors que la communication avec les entrées-sorties se fait octet par octet ! En général, les bus d'entrée-sortie sont assez petits, ils ont une taille de 8 ou 16 bits, même si le bus mémoire est plus grand. Cela permet de ne pas gaspiller trop de broches. Ajouter un bus d'entrée-sortie n'est donc pas très gourmand en broches et en fils. : Il est en théorie possible d'avoir une fréquence différente pour les deux bus, avec un bus mémoire ultra-rapide et un bus pour les entrées-sorties est un bus moins rapide. Mais il faut que le processeur soit prévu pour, et c'est très rare. Niveau performances, le processeur peut théoriquement accéder à la mémoire en attendant qu'une entrée/sortie réponde, mais il faut que le processeur soit prévu pour, et ce n'est pas de la tarte. Par contre, cela implique d'avoir des adresses séparées pour les registres d’interfaçage et la mémoire. En clair : pas d'entrée-sortie mappée en mémoire ! Un autre problème est que les entrées-sorties ne peuvent pas communiquer avec la mémoire directement, elles doivent passer par l'intermédiaire du processeur. En clair : pas de ''Direct Memory Access'' ! Les deux sont des défauts rédhibitoires pour les programmeurs système, notamment pour ceux qui codent les pilotes de périphériques. Pour résumer, les défauts sont assez problématiques : pas d'entrées-sorties mappées en mémoire, pas de ''Direct Memory Access'', économie de broches limitée. Les deux premiers sont des défauts majeurs, qui font que de tels bus ne sont pas utilisés dans les ordinateurs modernes. A la place, ils utilisent une troisième solution, distincte des bus systèmes et des bus d'entrée-sorties. ===Les bus avec répartiteur=== Il existe une méthode intermédiaire, qui garde deux bus séparés pour la mémoire et les entrées-sorties, mais élimine les problèmes de brochage sur le processeur. L'idée est d'intercaler, entre le processeur et les deux bus, un '''circuit répartiteur'''. Il récupère tous les accès et distribue ceux-ci soit sur le bus mémoire, soit sur le bus des périphériques. Le ou les répartiteurs s'appellent aussi le '''''chipset''''' de la carte mère. C'était ce qui était fait à l'époque des premiers Pentium. À l'époque, la puce de gestion du bus PCI faisait office de répartiteur. Elle mémorisait des plages mémoires entières, certaines étant attribuées à la RAM, les autres aux périphériques mappés en mémoire. Elles utilisaient ces plages pour faire la répartition. [[File:IO mappées en mémoire avec séparation des bus.png|centre|vignette|upright=2|IO mappées en mémoire avec séparation des bus]] Niveau adresses des registres d'interfacage, il est possible d'avoir soit des adresses unifiées avec les adresses mémoire, soit des adresses séparées. L'usage d'un répartiteur ne pose pas de problèmes particuliers pour implémenter le DMA. La seule contrainte est que le contrôleur DMA soit intégré dans le répartiteur. Les échanges entre IO et mémoire passent par le répartiteur, qui fait le pont entre bus mémoire et bus des IO. [[File:Implémentation du DMA avec un répartiteur.png|centre|vignette|upright=2|Implémentation du DMA avec un répartiteur]] ==Les microcontrôleurs et ''system on chip''== Parfois, on décide de regrouper la mémoire, les bus, le CPU et les ports d'entrée-sortie dans un seul circuit intégré, un seul boitier. L'ensemble forme alors ce qu'on appelle un '''''System on Chip''''' (système sur une puce), abrévié en SoC. Le nom est assez explicite : un SoC comprend un système informatique complet sur une seule puce de silicium, microprocesseurs, mémoires et périphériques inclus. Ils incorporent aussi des ''timers'', des compteurs, et autres circuits très utiles. [[File:ARMSoCBlockDiagram.svg|centre|vignette|upright=2|SoC basé sur un processeur ARM, avec des entrées-sorties typiques de celles d'un µ-contrôleur. Le support du bus CAN, d'Ethernet, du bus SPI, d'un circuit de PWM (génération de signaux spécifiques), de convertisseurs analogique-digital et inverse, sont typiques des µ-contrôleurs.]] Le terme SoC regroupe des circuits imprimés assez variés, aux usages foncièrement différents et à la conception distincte. Les plus simples d’entre eux sont des microcontrôleurs, qui sont utilisés pour des applications à base performance. Les plus complexes sont utilisés pour des applications qui demandent plus de puissance, nous les appellerons SoC haute performance. La relation entre SoC et microcontrôleurs est assez compliquée à expliquer, la terminologie n'est pas clairement établie. Il existe quelques cours/livres qui séparent les deux, d'autres qui pensent que les deux sont très liés. Dans ce cours, nous allons partir du principe que tous les systèmes qui regroupent processeur, mémoire et quelques périphériques/entrées-sorties sont des SoC. Les microcontrôleurs sont donc un cas particulier de SoC, en suivant cette définition. ===Les microcontrôleurs=== Les '''microcontrôleurs''' sont des composants utilisés dans l'embarqué ou d'informatique industrielle. Leur nom trahit leur rôle. Ils sont utilisés pour contrôler de l'électroménager, des chaines de fabrication dans une usine, des applications robotiques, les alarmes domestiques, les voitures. De manière générale, on les trouve dans tous les systèmes dits embarqués et/ou temps réel. Ils ont besoin de s'interconnecter à un grand nombre de composants et intègrent pour cela un grand nombre d'entrée-sorties. Les microcontrôleurs sont généralement peu puissants, et doivent consommer peu d'énergie/électricité. [[File:Microcontroller 8051.gif|centre|vignette|upright=2.5|Microcontrôleur Intel 8051.]] Un microcontrôleur tend à intégrer des entrées-sorties assez spécifiques, qu'on ne retrouve pas dans les SoC destinés au grand public. Un microcontrôleur est typiquement relié à un paquet de senseurs et son rôle est de commander des moteurs ou d'autres composants. Et les entrées-sorties intégrées sont adaptées à cette tâche. Par exemple, ils tendent à intégrer de nombreux convertisseurs numériques-analogiques pour gérer des senseurs. Ils intègrent aussi des circuits de génération de signaux PWM spécialisés pour commander des moteurs, le processeur peut gérer des calculs trigonométriques (utiles pour commander la rotation d'un moteur), etc. Fait amusant, on en trouve dans certains périphériques informatiques. Par exemple, les anciens disques durs intégraient un microcontrôleur qui contrôlait plusieurs moteurs/ Les moteurs pour faire tourner les plateaux magnétiques et les moteurs pour déplacer les têtes de lecture/écriture étaient commandés par ce microcontrôleur. Comme autre exemple, les claviers d'ordinateurs intègrent un microcontrôleur connecté aux touches, qui détecte quand les touches sont appuyées et qui communique avec l'ordinateur. Nous détaillerons ces deux exemples dans les chapitres dédiés aux périphériques et aux disques durs, tout deviendra plus clair à ce moment là. La majorité des périphériques ou des composants internes à un ordinateur contiennent des microcontrôleurs. ===Les SoC haute performance=== Les SoC les plus performants sont actuellement utilisés dans les téléphones mobiles, tablettes, ''Netbook'', ''smartphones'', ou tout appareil informatique grand public qui ne doit pas prendre beaucoup de place. La petite taille de ces appareils fait qu'ils gagnent à regrouper toute leur électronique dans un circuit imprimé unique. Mais les contraintes font qu'ils doivent être assez puissants. Ils incorporent des processeurs assez puissants, surtout ceux des ''smartphones''. C'est absolument nécessaire pour faire tourner le système d'exploitation du téléphone et les applications installées dessus. Niveau entrées-sorties, ils incorporent souvent des interfaces WIFI et cellulaires (4G/5G), des ports USB, des ports audio, et même des cartes graphiques pour les plus puissants d'entre eux. Les SoC incorporent des cartes graphiques pour gérer tout ce qui a trait à l'écran LCD/OLED, mais aussi pour gérer la caméra, voire le visionnage de vidéo (avec des décodeurs/encodeurs matériel). Par exemple, les SoC Tegra de NVIDIA incorporent une carte graphique, avec des interfaces HDMI et VGA, avec des décodeurs vidéo matériel H.264 & VC-1 gérant le 720p. Pour résumer, les périphériques sont adaptés à leur utilisation et sont donc foncièrement différents de ceux des microcontrôleurs. [[File:Phone hardware.png|centre|vignette|upright=2|Hardware d'un téléphone. On voit qu'il est centré autour d'un SoC, complété par de la RAM, un disque dur de faible capacité, de quoi gérer les entrées utilisateurs (l'écran tactile, les boutons), et un modem pour les émissions téléphoniques/2G/3G/4G/5G.]] Un point important est que les processeurs d'un SoC haute performance sont... performants. Ils sont le plus souvent des processeurs de marque ARM, qui sont différents de ceux utilisés dans les PC fixe/portables grand public qui sont eux de type x86. Nous verrons dans quelques chapitres en quoi consistent ces différences, quand nous parlerons des jeux d'instruction du processeur. Autrefois réservé au monde des PCs, les processeurs multicœurs deviennent de plus en plus fréquents pour les SoC de haute performance. Il n'est pas rare qu'un SoC incorpore plusieurs cœurs. Il arrive même qu'ils soient foncièrement différents, avec plusieurs cœurs d'architecture différente. La frontière entre SoC haute performance et microcontrôleur est de plus en plus floue. De nombreux appareils du quotidien intègrent des SoC haute performance, d'autres des microcontrôleurs. Par exemple, les lecteurs CD/DVD/BR et certains trackers GPS intègrent un SoC ou des processeurs dont la performance est assez pêchue. À l'opposé, les systèmes domotiques intègrent souvent des microcontrôleurs simples. Malgré tout, les deux cas d'utilisation font que le SoC/microcontrôleur est connecté à un grand nombre d'entrées-sorties très divers, comme des capteurs, des écrans, des LEDs, etc. [[File:GPS tracker Hardware Architecture.png|centre|vignette|upright=2|Hardware d'un tracker GPS.]] ==Étude de quelques exemples d'architectures== Après avoir vu la théorie, nous allons voir des exemples réels d'ordinateurs. Dans ce qui suit, nous allons voir des ordinateurs assez anciens, pour une raison simple : ils collent assez bien à l''''architecture de base''' vue plus haut, avec un CPU, une RAM et une ROM, quelques entrées-sorties. Tous les ordinateurs modernes, mais aussi dans les smartphones, les consoles de jeu et autres, utilisent une architecture grandement modifiée et améliorée, avec un grand nombre de périphériques, disques durs/SSD, un grand nombre de mémoires différentes, etc. Il pourrait sembler pertinent d’étudier des microcontrôleurs ou des ''System On Chip'', en premier lieu. Mais nous éviterons soigneusement de tels systèmes pour le moment. La raison est qu'ils ont un grand nombre d'entrées-sorties, qui sont peu familières. Attendez-vous à avoir près d'une vingtaine ou centaine d'entrée-sorties différentes pour de tels systèmes. Le tout est très complexe, bien trop pour un premier exemple. A la place, nous allons voir précisément des exemples plus simples : les premiers PC, et des consoles de jeu 8 et 16 bits. Bien que ce soit des systèmes très simples, ils sont cependant plus complexes que l'architecture de base. Et leur avantages/désavantages sont un peu inverse l'un de l'autre. Si on devait résumer les différences, on aurait ceci : * Les PC ont plus d'entrées-sorties que les consoles, bien que nettement moins que pour les microcontrôleurs/SoC. * Les PC utilisent des disques durs, les consoles font avec soit des cartouches de jeu, soit des CD/DVD. * Les PC utilisent des cartes électroniques séparées pour le son et l'écran, les consoles utilisent des circuits soudés sur la carte mère, qui sont souvent des co-processeurs. * Les PC ont une mémoire ROM soudées sur la carte mère, les consoles 8 bits font sans. Les PC et micro-ordinateurs ont plus d'entrées-sorties que les consoles. Même si on mets de côté les périphériques, ils ont aussi beaucoup de composants soudées sur la carte mère. En comparaison, les consoles de jeu 8/16 bits se débrouillent avec : une cartouche de jeu et une manette en entrée, une sortie vidéo et une sortie son. Un autre point important est l'absence de disque dur ou de lecteur CD. La présence d'un disque dur ou d'un lecteur CD/DVD complexifie tout de suite l'architecture des PC. Il faut leur réserver un bus dédié ou les connecter à un bus système, cela demande d'ajouter des circuits sur la carte mère, etc. Et surtout, il faut expliquer comment l'ordinateur exécute des programmes, ce qui demande de parler de l'interaction avec le disque dur et la ROM du BIOS. Rien de tout cela sur les consoles de jeu 8 et 16 bits. Elles utilisent à la place des cartouches de jeu, qui intègrent une mémoire ROM, pour mémoriser les données du jeu, voire son code. Pas besoin de parler des mémoires de stockage, on est beaucoup plus proche de l'architecture de base avec une ROM unique. Par contre, n'allez pas croire que tout est rose avec les consoles 8/16 bits. Il y a quelques différences qui font qu'elles sont plus complexes qu'un PC sur certains points. Les PC utilisent des cartes électroniques à brancher sur la carte mère pour alimenter l'écran et les hauts-parleurs/casques, alors que les consoles de jeu utilisent des souvent co-processeurs dédiés pour le son et les graphismes. La différence parait mineure, mais elle avantage les consoles. Nous avons déjà expliqué ce que sont les co-processeurs plus haut, aussi les co-processeurs des consoles nous paraitrons familiers. On n'a pas à s’embêter à expliquer ce que sont les cartes d'extension, les bus associés et tout ce qui va avec, cela peut être retardé pour la section sur l'architecture des PC. La gestion de la cartouche de jeu est aussi un peu subtile à comprendre, bien que ce soit bien plus simple à comprendre qu'un système avec un disque dur. Les cartouches de jeu intègrent une mémoire ROM, pour mémoriser les données du jeu, voire son code. Et le processeur doit exécuter le code depuis cette mémoire ROM. La conséquence est que les consoles 8/16 bits utilisent une architecture Harvard, avec un bus relié à la cartouche pour lire les instructions. Mais si ce n'était que ça... Les cartouches mémorisent aussi les données pour les graphismes, ce qui fait que le co-processeur vidéo doit lui aussi lire la cartouche pour récupérer ces données... ===L'architecture de la TurboGraphX-16=== La console PC Engine, aussi appelée TurboGraphX, est une ancienne console 8 bits. Elle contient un processeur 65C02, 8 kibioctets de RAM, un port manettes, une carte son et une carte vidéo. La '''carte son''' est le composant qui s'occupe de commander les haut-parleurs et de gérer tout ce qui a rapport au son. La '''carte graphique''' est le composant qui est en charge de calculer les graphismes, tout ce qui s'affiche à l'écran. Sur cette console, les cartes son et graphique ne sont PAS des co-processeurs, ce sont des circuits électroniques dits fixes. C'est totalement différent de ce qu'on a sur les consoles modernes, aussi le préciser est important. Bien que la carte graphique ne soit pas un processeur, elle a 64 kibioctets de RAM rien que pour elle. La RAM en question est séparée de la RAM normale, c'est un circuit intégré séparé. Et c'est un cas très fréquent, qui reviendra par la suite. La majeure partie des cartes graphiques dispose de leur propre '''mémoire vidéo''', totalement réservée à la carte graphique. La RAM vidéo est connectée à la carte graphique via un bus séparé. Le processeur est souvent connecté à ce bus, afin de pouvoir écrire des données dedans, mais ce n'est pas le cas ici. [[File:Architecture de la PC Engine, aussi appelée TurboGrafx-16.png|centre|vignette|upright=2.5|Architecture de la PC Engine, aussi appelée TurboGrafx-16]] L'architecture de la console était particulièrement simple. Le processeur était le centre de l'architecture, tout était connecté dessus. Il y a un bus pour la cartouche de jeu, un autre pour la RAM, un autre pour les manettes, un autre pour carte son, et un dernier pour la carte graphique. Le fait d'avoir un bus par composant est assez rare et ce n'est le cas ici que parce des conditions particulières sont remplies. Déjà, il y a peu d'entrée-sorties. Ensuite, les bus font tous 8 bits, vu que le processeur est un CPU 8 bits. Avec 5 connexions de 8 bits, le tout utilise 40 broches, ce qui est beaucoup, mais totalement gérable. Par contre, les choses changerons pour les autres consoles. Au final, l'organisation des bus peut s'expliquer avec ce qu'on a vu dans la section sur les bus de communication. La console utilise une architecture Harvard, car la ROM et la RAM utilisent des bus différents. De plus, il y a des bus dédiés aux entrées-sorties, séparés des bus mémoire. Enfin, la carte graphique a droit à ses propres bus pour lire dans la cartouche et dans sa RAM vidéo dédiée. ===L'architecture de la console de jeu NES=== Maintenant, nous allons voir la console de Jeu Famicom, aussi appelée la NES en occident. Elle a une architecture centrée sur un processeur Ricoh 2A03, similaire au processeur 6502, un ancien processeur autrefois très utilisé et très populaire. Le processeur est associé à 2 KB de mémoire RAM. Sur certaines cartouches, on trouve une RAM utilisée pour les sauvegardes, qui est adressée par le processeur directement. Première variation par rapport à l'architecture de la console précédente : l'ajout de la RAM pour les sauvegardes dans les cartouches. Niveau carte graphique, une différence importante est que la carte graphique est connectée à la cartouche de jeu via un autre bus, afin de pouvoir lire les sprites et textures du jeu dans la cartouche. [[File:Architecture de la NES.png|centre|vignette|upright=2.5|Architecture de la NES]] La différence avec l'architecture précédente est que des bus ont été fusionnés. Comme dit plus haut, le système utilise une architecture Harvard, vu que la ROM est dans la cartouche, alors que la RAM est soudée à la carte mère. Par contre, la Famicon utilise un bus dédié aux entrées-sorties. Il est utilisé pour la carte son et la carte graphique, seules les manettes sont sur un bus à part. Ce qui fait qu'on devrait plutôt parler de bus de sorties, mais passons... L'essentiel est qu'on n'est plus tout à fait dans le cas de la console précédente, avec un bus par composant. ===L'architecture de la SNES=== L'architecture de la SNES est illustrée ci-dessous. Les changements pour le processeur et la RAM sont mineurs.La RAM a augmenté en taille et passe à 128 KB. Pareil pour la RAM de la carte vidéo, qui passe à 64 KB. Par contre, on remarque un changement complet au niveau des bus, de la carte graphique et de la carte son. [[File:Architecture de la SNES.png|centre|vignette|upright=2|Architecture de la SNES]] La console utilise un '''bus système unique''', sur lequel tout est connecté : ROM, RAM, entrées-sorties, etc. La seule exception est pour les manettes, qui sont encore connectées directement sur le processeur, via un bus séparé. La transition vers un bus système s'explique par le fait que la console est maintenant de 16 bits, ce qui fait que les bus doivent être plus larges. Le processeur adresse des mémoires RAM et ROM plus grandes, ce qui double la taille de leurs bus. De plus, les entrées-sorties aussi ont besoin d'un bus plus large. Le processeur n'ayant pas un nombre illimité de broches, la seule solution est de fusionner les bus en un seul bus système. Un autre changement est que la carte graphique est maintenant composée de deux circuits séparés. Encore une fois, il ne s'agit pas de coprocesseurs, mais de circuits non-programmables. Par contre, la carte son est remplacée par deux coprocesseurs audio ! De plus, les deux processeurs sont connectés à une mémoire RAM dédiée de 64 KB, comme pour la carte graphique. L'un est un processeur 8 bits (le DSP), l'autre est un processeur 16 bits. Un point très intéressant : certains jeux intégraient des coprocesseurs dans leurs cartouches de jeu ! Par exemple, les cartouches de Starfox et de Super Mario 2 contenait un coprocesseur Super FX, qui gérait des calculs de rendu 2D/3D. Le Cx4 faisait plus ou moins la même chose, il était spécialisé dans les calculs trigonométriques, et diverses opérations de rendu 2D/3D. En tout, il y a environ 16 coprocesseurs d'utiliser et on en trouve facilement la liste sur le net. La console était conçue pour, des pins sur les ports cartouches étaient prévues pour des fonctionnalités de cartouche annexes, dont ces coprocesseurs. Ces pins connectaient le coprocesseur au bus des entrées-sorties. Les coprocesseurs des cartouches de NES avaient souvent de la mémoire rien que pour eux, qui était intégrée dans la cartouche. ===L'architecture de la Megadrive et de la néo-géo=== Passons maintenant la console de jeu Megadrive, une console 16 bits. Elle a une architecture similaire à celle de la néo-géo, une autre console bien plus puissante, sorti à peu près en même temps. Elle intègre deux processeurs : un CPU Motorola 68000, et un co-processeur audio Z80. Le Z80 et le Motorola 68000 étaient deux processeurs très populaires à l'époque. Le Z80 est une sorte de version améliorée de l'Intel 8088 utilisé sur les anciens PC et de nombreuses consoles utilisaient des Z80 comme processeur principal. Mais ici, il est utilisé comme co-processeur audio, sans doute car il était familier pour les programmeurs de l'époque, pour son cout réduit, sa bonne disponibilité, et bien d'autres avantages liés à sa production de masse. Le Motorola 68000 était un processeur 16 bits, alors que le Z80 est un processeur 8 bits. Et cette différence fait que l'on ne peut pas connecter directement les deux sur le même bus, ou du moins pas facilement. La solution retenue est d'utiliser deux bus séparés : un bus de 16 bits connecté au 68000, un bus de 8 bits connecté au Z80. Le premier bus est un bus système sur lequel est connecté le 68000, 64 kibioctets de RAM, la cartouche de jeu, et la carte graphique. Le second bus est un bus de 8 bits, plus court, relié au Z80, à un synthétiseur sonore, et 8 kibioctets de RAM. Les deux bus sont connectés à un '''''chipset''''', un circuit répartiteur, qui fait le pont entre les deux bus. Les manettes sont connectées sur le ''chipset''. [[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]] Cet exemple nous montre que les bus systèmes sont certes très simples, mais aussi inflexibles. Ils fonctionnent bien quand les composants branchés dessus sont tous des composants 8 bits, ou sont tous de 16 bits, ou tous 32 bits. Mais dès qu'on mélange composants 8, 16, 32 ou 64 bits, les choses deviennent plus compliquées. Il est alors préférable d'utiliser des bus séparés, avec des répartiteurs pour faire le pont entre les différents bus. Et nous verrons que le problème s'est posé lui aussi sur les PC. ===L'architecture des anciennes consoles Playstation : beaucoup de co-processeurs=== Les consoles que nous venons d'aborder étaient des consoles 8 ou 16 bits. A partir des consoles 32 bits, leur architecture s'est rapprochée de celle des PC, avec un usage plus complexes de répartiteurs. La XBOX était très semblable à un PC : le processeur était un Pentium 3 modifié, la carte graphique était une Geforce 3 modifiée, les 64 mébioctets de RAM était la même mémoire DDR que celle des PC, le répartiteur secondaire était un ''chipset'' nForce de NVIDIA, etc. Mais les Playstation 1, 2 et 3 se distinguent de leur contemporains. Elles disposent de très nombreux co-processeurs, qui sont en plus très variés. La Playstation 1 a été une des premières console à utiliser les CD-ROM comme support de stockage, en remplacement des cartouches. La conséquence est que la console contient une mémoire ROM, soudée à la carte mère, de 512 kibioctets. Elle contient aussi 2 mébioctets de RAM, une carte graphique avec 1 mébioctet de mémoire vidéo, un processeur, et de quoi gérer les périphériques. Il y a un co-processeur audio spécialisé, avec 512 kibioctets de RAM, ce qui nous est familier. Par contre, les autres co-processeurs ne le sont pas. Déjà, le lecteur de CD-ROM est associé à des circuits sur la carte mère, il y a tout un sous-système dédié au lecteur de CD. Il y a un contrôleur qui sert d'interface avec le lecteur proprement dit, mais aussi deux co-processeurs audio et 32 kibioctets de RAM. Les co-processeurs audio servent à lire des CD sans trop utiliser le second co-processeur audio, ils lui servent de complément. Ensuite, le processeur incorpore plusieurs cœurs, avec un cœur principal et plusieurs co-processeurs. Le premier est un co-processeur système, qui est utilisé pour gérer la mémoire cache intégrée au processeur, pour des fonctionnalités appelées interruptions et exceptions, ainsi que pour configurer le processeur. Le second est un co-processeur arithmétique spécialisé dans les calculs en virgule flottante, très importants pour le rendu 3D. Enfin, il y a un décodeur vidéo, qui n'est pas un co-processeur, mais un circuit non-programmable, spécialisé dans le décodage vidéo. De nos jours, ce circuit aurait été intégré dans la carte graphique, mais il était intégré dans le processeur sur la Playstation 2. Pour le reste, le processeur est la figure centrale de la console. Il est connecté à 4 bus : un pour la RAM, un pour la carte graphique, un pour les manettes, un autre pour le reste. Le dernier bus est connecté au système audio et au système pour le lecteur CD. Ce serait un bus d'entrée-sortie, s'il n'était pas connecté à la mémoire ROM. Vous avez bien lu : la mémoire ROM est reliée au bus d'entrée-sortie. [[File:Architecture de la Playstation.png|centre|vignette|upright=2.5|Architecture de la Playstation]] La Playstation 2 est composé d'un processeur, couplé à 32 Mébioctets de RAM, et d'un paquet de co-processeurs. Plus de co-processeurs que la PS1. Le processeur principal n'est pas la même que celui de la PS1, mais il a une architecture similaire. Il intègre un décodeur vidéo sur le même circuit intégré, ainsi que deux co-processeur. Les co-processeurs ne sont cependant pas les mêmes. Le co-processeur système disparait et est remplacé par un second co-processeur arithmétique. Les deux co-processeurs arithmétiques sont spécialisés dans les nombres flottants, avec quelques différences entre les deux. Par exemple, le second co-processeur gérait des calculs trigonométriques, des exponentielles, des logarithmes, et d'autres fonctions complexes du genre ; mais pas le premier co-processeur. Ils sont reliés à 4 kibioctets de RAM pour le premier, 16 kibioctets de RAM pour le second ; qui sont intégrées dans le processeur et non-représentés dans le diagramme ci-dessous. La PS2 intègre aussi un co-processeur d'entrées-sorties. Pour information, il s'agit du processeur principal de la Playstation 1, qui est ici utilisé différemment, suivant que l'on place un jeu PS1 ou PS1 dans la console. Si on met un jeu PS1, il est utilisé pour émuler la Playstation 1, afin de faire tourner le jeu PS1 sur la PS2. Si on met un jeu PS2, il est utilisé comme co-processeur d'entrée-sortie et fait l'interface entre CPU et entrées-sorties. Il est relié à 2 mébioctets de RAM, soit exactement la même quantité de mémoire que la Playstation 1. Tous les périphériques sont connectés au co-processeur d'entrées-sortie. Pour cela, le co-processeur d'entrées-sortie est relié à deux bus dédiés aux périphériques. Le premier bus est relié aux manettes, aux ports USB et aux ports pour cartes mémoires. Le second bus est relié à la carte son, la carte réseau, le lecteur DVD, et un port PCMIA. Notons que la carte son intègre un co-processeur audio, qui n'est pas représenté dans le diagramme ci-dessous. [[File:Playstation 2 architecture.png|centre|vignette|upright=2.5|Playstation 2 architecture]] ==L'architecture des PC et son évolution== Après avoir vu les consoles, nous allons maintenant voir les anciens PC, des années 80 ou 90. Le tout premier PC était techniquement l''''IBM PC'''. Par la suite, de nombreux ordinateurs ont tenté de reproduire l'IBM PC originel, avec parfois quelques modifications mineures. De tels ordinateurs ''IBM PC compatibles'', ont été très nombreux, pour des raisons diverses. Le fait d'utiliser des composants banalisés, facilement disponibles, ainsi qu'une bonne documentation de l'IBM PC originel, a grandement aidé. Les IBM PC compatibles ont progressivement évolué pour donner les PC actuels. L'IBM PC compatible a donné naissance à de nombreux standards divers. ===L'IBM PC originel et l'IBM PC XT=== [[File:IBM PC XT 02.jpg|vignette|IBM PC XT.]] Nous allons commencer par voir l'IBM PC originel, et son successeur : l'IBM Personal Computer XT. Nous les appelerons tous deux l'IBM PC. L'IBM PC utilisait un processeur Intel 8088, qui était un processeur 8 bits. Ils utilisaient un bus système unique, appelé le '''bus XT'''. Le bus système allait à 4.77 MHz, soit la même fréquence que le processeur. C'était un bus de 8 bits, ce qui collait parfaitement avec les processeurs 8 bits commercialisés par Intel à l'époque. L'IBM PC comprenait une mémoire ROM avec de quoi faire fonctionner le PC. La ROM en question contenait un programme minimal, appelé le '''BIOS''', sans lequel le PC ne fonctionnait pas du tout. Il servait de base pour le système d'exploitation et MS-DOS ne fonctionnait pas sans elle. De nos jours, son rôle est plus limité : sans elle, le PC ne démarre pas. Mais nous détaillerons cela dans le prochain chapitre. En plus de la ROM pour le BIOS, l'IBM PC avait quatre mémoires ROM dédiée au langage de programmation BASIC. Lorsque le PC démarrait, il ne bootait pas un système d'exploitation, mais lançait l'interpréteur pour le langage BASIC. De nos jours, ce serait l'équivalent d'un ordinateur qui boote directement sur du Python, à savoir la console Python que vous avez peut-être déjà utilisé si vous avez testé Python. Ceux qui ont déjà touché à un ordinateur de l'époque savent ce que ca veut dire, mais c'est malheureusement très difficile à expliquer sans ce genre d'expérience. Toujours est-il que c'était une sorte de norme à l'époque : les ordinateurs bootaient généralement sur un interpréteur BASIC. [[File:XT Bus pins.svg|vignette|Connecteur du bus XT.]] Les PC étaient conçus pour qu'on branche des '''cartes d'extension''', à savoir des cartes électroniques qu'on branchait sur la carte mère, à l'intérieur du PC. Les cartes d'extension de l'époque étaient surtout des cartes son ou des cartes graphiques, mais aussi des cartes pour brancher des péripéhriques. par exemple, on pouvait ajouter deux cartes graphiques dans l'IBM PC originel : l'''IBM Monochrome Display Adapter'' et/ou la ''IBM Color Graphics Adapter''. De nos jours, les cartes son sont intégrées à la carte mère, mais les cartes graphiques sont restées des cartes d'extension. Les cartes d'extension étaient branchées sur un '''connecteur XT''', qui était directement relié au bus XT. Le connecteur XT est illustré ci-contre, mais ne vous en souciez pas trop pour le moment. La carte mère de l'IBM PC avait 5 connecteurs de ce type, qu'on pouvait peupler avec autant de cartes d'extension. L'IBM Personal Computer XT est passé à 8 connecteurs XT, soit trois de plus. Pour ce qui est des périphériques, l'IBM PC avait plusieurs connecteurs : un port série, un port parallèle, un port pour le clavier, et un port pour un lecteur cassette. Le clavier et le lecteur cassette étaient connectés directement sur la carte mère, qui contenait quelques circuits pour gérer le clavier. Par contre, les deux premiers n'étaient pas connectés à la carte mère. Le port série était en réalité une carte d'extension, branchée sur un connecteur XT. Et il en est de même pour le port parallèle. Pour ce qui est des supports de stockage, l'IBM PC originel n'avait pas de disque dur et n'avait que des lecteurs de disquette. De plus, le lecteur de disquette n'était pas connecté directement sur la carte mère, mais était connecté à une carte d'extension, branchée sur un connecteur XT. La carte d'extension avait deux connecteurs, un par lecteur de disquette, ce qui fait que les deux lecteurs de disquettes pouvaient être branchés sur une seule carte d'extension. L'IBM Personal Computer XT a ajouté un disque dur, sauf sur quelques sous-modèles spécifiques. Le PC avait aussi un petit haut-parleur capable de faire des bips. Pour résumer, l'IBM PC originel se reposait beaucoup sur les cartes d'extension, sa carte mère contenait peu de choses. Enfin, peu de choses... Il y avait un processeur Intel 8088, éventuellement un coprocesseur flottant 8087, de la RAM, de la ROM, et des circuits intégrés assez divers. En voici la liste, certains vous seront familiers, d'autres vous seront inconnus à ce stade du cours : * les circuits de décodage d'adresse ; * un contrôleur DMA intel 8273 ; * un contrôleur d'interruption 8259 ; * un contrôleur de bus Intel 8288 pour gérer le bus XT ; * un générateur d'horloge Intel 8284 et un diviseur de fréquence ; * un ''timer'' Intel 8253, le même que celui étudié dans le chapitre sur les ''timers'' ; * un contrôleur parallèle 8255. Les multiplexeurs, registres et portes logiques, sont des circuits de décodage d'adresse, qui permettent de combiner plusieurs RAM en une seule, idem avec la mémoire ROM. Si vous verrez qu'il y a 5 mémoires ROM : une ROM pour le BIOS, et quatre autres ROM pour le BASIC. Les 4 ROM du BASIC sont combinées en une seule mémoire ROM. Pour les RAM, il y en a 8 à 32, qui sont combinées en une seule RAM de 16 à 64 kibioctets. [[File:IBM 5150 Motherboard.svg|centre|vignette|upright=3|Carte mère de l'IBM 5150, un modèle de l'IBM PC.]] ===L'architecture d'un IBM PC compatible 16 bits=== Les PC suivants sont passés à des processeurs 16 bits, mais c'était toujours des processeurs x86 d'Intel, à savoir des Intel 286 et 386. La RAM a grossi, quelques entrées-sorties ont été ajoutées, mais l'architecture globale est plus moins resté le même. C'est surtout au niveau du bus et des périphériques que les changements majeurs ont eu lieu. [[File:ISA Bus pins.svg|vignette|Connecteur ISA.]] Les PC 16 bits utilisaient un bus système unique, sur lequel tout était connecté : le processeur, la RAM, la ROM, les cartes d'extension et tout le reste. Le bus en question s'appelait le '''bus AT''', mais il a rapidement été renommé en '''bus ISA''' (''Industry Standard Architecture''). Le bus ISA était prévu pour avoir une compatibilité avec le bus 8 bits de l'IBM PC originel. D'ailleurs, cela se ressent jusque dans le connecteur utilisé : le connecteur ISA est un connecteur XT qu'on a fusionné avec un second connecteur pour l'étendre de 8 à 16 bits. Les PC 16 bits avaient toujours un port série, un port parallèle, un clavier, un lecteur de disquette et des cartes d'extension. Des disques durs pouvaient être ajoutés, aussi. Mais pour ces périphériques, un changement majeur a eu lieu comparé à l'IBM PC originel. L'IBM PC originel utilisait des cartes d'extension pour tout, sauf le clavier. Mais maintenant, les périphériques ne sont plus connectés à une carte d'extension. A la place, les circuits de la carte d'extension sont déplacés sur la carte mère. Mais n'allez pas croire qu'ils étaient connectés directement au bus ISA, il y avait des intermédiaires. Le clavier était relié à un '''contrôleur de clavier''', qui faisait l'interface entre le connecteur du clavier et le bus ISA. Le contrôleur de clavier était appelé le ''Keyboard Controler'', abrévié en KB. Il recevait ce qui est tapé au clavier et traduisait cela en quelque chose de compréhensible par l'ordinateur. Les autres périphériques étaient connectés à un circuit intégré dédié : l''''Intel 82091AA'''. Il était connecté au lecteur de disquette, au port série et au port parallèle. Il servait d'intermédiaire entre ces périphériques et le bus ISA. Vous pouvez le voir comme une sorte de répartiteur, mais qui ne serait pas connecté sur le processeur et la RAM Enfin, il ne faut pas oublier les autres composants présents sur l'IBM PC originel. Le BIOS est toujours là, de même que les ''timers'' Intel 8253 PIT, le contrôleur d'interruption Intel 8259 et le contrôleur DMA Intel 8237. Les PC 16 bits ont aussi intégré une ''Real Time Clock'' (RTC). Pour rappel, c'est un composant qui permet au PC de mémoriser la date et l'heure courante, à la seconde près. Le tout est résumé dans le schéma ci-dessous. [[File:Architecture de l'IBM PC compatible.png|centre|vignette|upright=2.5|Architecture de l'IBM PC compatible]] Un point important est que le bus ISA allait à la même fréquence que le processeur, vu que c'était un bus système. Les processeurs de l'époque étaient des CPU 286 d'Intel, ou le 386 d'Intel. Les Intel 286 allaient de 4 MHz minimum, à 25 MHz maximum. Le 386, quant à lui, allait de 12 à 40 MHz. Le bus ISA devait aller à cette fréquence, il était synchrone avec le processeur. Par la suite, les processeurs ont gagné en performance, ce qui fait que le bus ISA est devenu trop lent pour le processeur. Une idée a alors été de conserver le bus ISA, pour des raisons de compatibilité, mais de le reléguer comme bus secondaire. L'ordinateur contient alors deux bus : un bus système, et un bus ISA secondaire. Le lien entre les deux est réalisé par un '''pont ISA''', ''ISA Bridge'' en anglais. Le bus ISA fonctionnait alors sa fréquence usuelle, alors que le bus système était beaucoup plus rapide. Le bus système fonctionnait à une fréquence bien plus élevée, ce qui fait que le processeur pouvait communiquer à pleine vitesse, notamment avec la RAM. Le processeur n'était alors plus forcé à aller à la même fréquence que le bus ISA [[File:Architecture de l'IBM PC compatible avec bridge ISA.png|centre|vignette|upright=2.5|Architecture de l'IBM PC compatible avec bridge ISA]] Les PC de l'époque intégraient donc plusieurs bus séparés. Vous avez bien lu : plusieurs bus ! Ici, il s'agit de ce que j'appelle des '''bus en cascade''', à savoir qu'un bus est connecté à un autre bus par un intermédiaire. Au passage, si j'aborde ces exemples, car c'est pareil sur les ordinateurs modernes. Le pont ISA a été remplacé par des circuits différents, mais qui ont un rôle assez similaire. Le ''chipset'' de votre carte mère n'est qu'un lointain descendant du pont ISA, qui s'interface avec des bus différents. ===L'arrivée des standards AT et IDE pour les disques durs=== Initialement, les disques durs étaient placés dans l'ordinateur et étaient connectés sur le bus ISA, via une carte d'extension ISA. En clair, il fallait connecter le disque dur sur une carte d'extension, et non sur la carte mère. Les cartes d'extension en question permettaient de connecter un ou plusieurs disques durs, parfois des lecteurs de disquette supplémentaires. Les cartes ISA de ce type faisaient juste l'interface entre le bus ISA et les disques durs, rien de plus. L'interface en question a été standardisée, ce qui a donné le standard ''AT Bus Attachment'', qui a été abrévié en ATA. Et ce n'était pas que pour les disques durs, de nombreux composants étaient dans ce cas. Une carte d'extension servait d'intermédiaire entre eux et la carte mère. Les cartes d'extension en question étaient appelées des ''Host bus adapter''. [[File:Acculogic sIDE-4 Controller ISA.jpg|centre|vignette|upright=2|Carte ISA d'interface disque dur, de marque Acculogic.]] Mais les choses ont rapidement évoluées, que ce soit du côté des cartes mères que du côté des disques durs. Le '''standard IDE''' a permis de brancher un disque dur directement sur la carte mère, sans passer par une carte d'interface ISA. Pour cela, la carte mère réservait un connecteur ISA pour le disque dur, renommé '''connecteur ATA'''. Pour que cela soit possible, il a fallu rajouter des circuits sur la carte mère. Tout ce qui était sur les cartes d'interface ISA s'est retrouvé sur la carte mère. [[File:Ajout des ports IDE sur la carte mère.png|centre|vignette|upright=2|Ajout des ports IDE sur la carte mère]] En réalité, les connecteurs ATA étaient des connecteurs ISA simplifiés. Un connecteur ISA avait en tout 98 broches, alors qu'un connecteur ATA n'en contient que 40. Les broches qui étaient inutiles pour les disques durs ont simplement été enlevées. Et qui dit connecteur spécialisé, dit câble spécialisé. Les disques durs étaient branchés sur le connecteur AT grâce à un câble ATA, sur lequel on pouvait connecter deux disques durs. [[File:ATA Plug.svg|centre|vignette|upright=2|Connecteur ATA.]] [[File:ATA cables.jpg|centre|vignette|upright=2|Cable ATA.]] Il était donc possible de connecter deux disques durs sur un seul connecteur ATA. Et cette possibilité est devenue d'autant plus utile par la suite. A partir de la version 2, ATA supportait aussi les lecteurs de disquettes, les lecteurs de CD/DVD, et bien d'autres supports de stockage. Il était alors possible de connecter un lecteur CD et un disque dur sur un seul connecteur. Les cartes mères avaient généralement deux connecteurs ATA, et n'avaient pas besoin de plus. C'était suffisant pour connecter un disque dur, un lecteur de disquette et un lecteur CD, configuration courante entre les années 90 et 2000. Un câble est donc connecté à deux supports de stockage. Pour distinguer les deux, le standard ATA ajoute une possibilité de configuration. Sur un câble, il doit y avoir un support de stockage "maitre" et un support "esclave". C'était la terminologie de l'époque, que je reproduis ici, même si elle est fortement trompeuse. N'allez pas croire que cela implique que l'un ait des avantages sur l'autre. Le support 'maitre" n'a pas droit à plus de bande passante, il n'a pas la priorité sur l'autre, rien du tout. Il s'agit juste d'un nombre qui permet de savoir avec qui le processeur communique, qui vaut 0 pour le premier support, 1 pour l'autre. Une sorte d'adresse de 1 bit, si l'on veut. [[File:ATA-Konfiguration02.png|centre|vignette|upright=2|Configuration ATA.]] Pour configurer un support de stockage en mode "maitre" ou "esclave", le support de stockage avait quelques pins dédiés. Il suffisait de placer un détrompeur en plastique sur les pins adéquats. Les pins se trouvaient à l'arrière du disque dur ou du lecteur de CD/DVD/Disquette/autre. [[File:HDD Master and Slave Description.jpg|centre|vignette|upright=2|Configuration ''Master/Slave''.]] ===L'architecture d'un PC avec un processeur Intel 486=== Maintenant, passons aux ordinateurs 32 bits, avec l'exemple d'un PC avec un processeur 486 d'Intel. A cette époque, le bus ISA était devenu trop limité et était en place d'être remplacé par le bus PCI, qui avait la même fonction. De nombreuses cartes d'extension utilisaient déjà ce standard et étaient branchées sur des connecteurs PCI dédiés, différents des connecteurs ISA. Intuitivement, on se dit que le bus PCI remplaçait le bus ISA, mais les choses étaient plus compliquées. Les disques durs gardaient leur connecteur ATA, et ne passaient pas par le bus PCI. Ils avaient un bus IDE séparé, qui était un bus ISA modifié. Là encore, les processeurs étaient devenus beaucoup plus rapides que le bus PCI. Les deux allaient à des fréquences assez différentes, ce qui fait que le bus PCI était séparé du bus système. Il y avait alors deux implémentations possibles. * La première utilise un répartiteur unique, relié au processeur, à la RAM, au bus PCI, et au bus IDE. * La seconde utilise un bus système séparé du bus PCI, avec un '''pont PCI''' pour faire l'interface entre les deux. Le '''''System Controler''''' était un circuit intégré, placé sur la carte mère, qui peut servir soit de pont PCI, soit de répartiteur. Le répartiteur PCI sert d'intermédiaire avec le bus PCI, mais aussi avec le bus IDE, utilisé pour les disques durs, aussi appelé le bus ''Parallel ATA''. Il peut aussi être connecté au processeur, à la mémoire RAM, ainsi qu'à la mémoire cache, mais cela ne sert que quand il est utilisé comme répartiteur. [[File:Architecture d'un PC utilisant un bus PCI, implémentation avec un répartiteur.png|centre|vignette|upright=2|Architecture d'un PC utilisant un bus PCI, implémentation avec un répartiteur]] Pour des raisons de compatibilité, le bus ISA avait été conservé, aux côtés du bus PCI. Il y avait un pont ISA en plus du pont/répartiteur PCI. Une implémentation possible aurait été de connecter les deux ponts ISA et PCI à un bus système unique. Mais cette solution n'a pas été retenue. La raison est que le bus PCI et le bus ISA ont des performances très différentes. Le bus PCI est très rapide, le bus ISA beaucoup plus lent. La différence est d'un ordre de grandeur, environ. Dans ces conditions, il est possible de faire passer les communications ISA à travers le bus PCI. Pour cela, le pont ISA est directement connecté sur le pont PCI, comme illustré ci-dessous. Et il en est de même pour le bus dédié aux disques durs. En effet, les disques durs étaient autrefois reliés au bus ISA, mais cela a changé depuis. Ils disposent maintenant de leur propre bus dédié, le '''bus IDE''', qui est un bus ISA simplifié. Et ce bus ISA simplifié était connecté directement sur le pont PCI. [[File:Architecture de l'IBM PC compatible avec pont PCI.png|centre|vignette|upright=2|Architecture de l'IBM PC compatible avec pont PCI]] Dans ce qui va suivre, nous allons étudier un exemple qui utilise un bus système séparé, avec un pont PCI, sans répartiteur. Voilà pour les grandes lignes, mais le schéma ci-dessous montre que tout est plus complexe. Vous remarquerez des connexions optionnelles entre le pont PCI et la mémoire RAM et la mémoire cache. La raison est que le pont PCI peut aussi servir de répartiteur en remplacement du bus système. Concrètement, on peut alors retirer le bus système. La mémoire, le bus PCI, le bus ISA, le bus IDE, le processeur et la RAM sont alors connectés au répartiteur PCI, qui sert d'intermédiaire central entre tous ces composants. Mais ce n'est pas la solution qui a été retenue dans notre exemple. [[File:Intel486-Typ PCI System.png|centre|vignette|upright=2|PC IBM compatible avec un 486, un bus PCI et un bus ISA. Le ''host bus'' est le bus système.]] Le pont ISA sert ici d'intermédiaire entre le bus système et le bus ISA. De plus, il a été amélioré sur de nombreux points. Il inclut notamment des circuits qui étaient autrefois sur la carte mère, à savoir le contrôleur DMA 82C87 et le contrôleur d'interruption 82C59, ainsi que les ''timers'' Intel 82C54. Les composants restants sont eux reliés sur un quatrième bus : le bus X, l'ancêtre du bus ''Low Pin Count''. Le bus X était celui du BIOS, du contrôleur de clavier, de la ''Real Time Clock'', et du contrôleur de périphérique 82091AA d'Intel. [[File:ISA Bridge schematic.png|centre|vignette|upright=2|ISA Bridge.]] ===L'architecture des PC des années 90-2000=== Par la suite, les ponts PCI et ISA ont évolué avec l'évolution des bus de l'ordinateur. Le bus ISA a progressivement été remplacé par d'autres bus, comme le bus ''Low Pin Count'', le bus PCI a été remplacé par le PCI Express, d'autres bus ont été ajoutés, etc. Mais la séparation du ''chipset'' en deux a été conservée. [[File:Chipset schematic.svg|vignette|upright=1.0|Chipset séparé en northbridge et southbridge.]] Le pont PCI et le pont ISA ont été remplacés respectivement par le '''pont nord''' et le '''pont sud''', plus connus par leurs noms anglais de ''northbridge'' et de ''southbridge''. Le pont nord servait d'interface entre le processeur, la mémoire et la carte graphique et est connecté à chacun par un bus dédié. Il intégrait aussi le contrôleur mémoire. Le pont sud est le répartiteur pour les composants lents, à savoir l'USB, l'Ethernet, etc. Le bus qui relie le processeur au pont nord était appelé le '''''Front Side Bus''''', abrévié en FSB. [[File:IMac Chipset.png|centre|vignette|upright=2|Chipset séparé en northbridge et southbridge.]] Un point important est que le bus PCI est devenu un bus assez lent, ce qui fait qu'il a finit par être connecté au pont sud. Le pont PCI est donc devenu le pont sud, dans le courant des années 2000. Durant un moment, un équivalent du pont ISA a subsisté dans un circuit de '''''Super IO'''''. Concrètement, il s'occupait du lecteur de disquette, du port parallèle, du port série, et des ports PS/2 pour le clavier et la souris. Mais il ne gérait pas le bus ISA, mais son remplaçant, le bus ''Low Pin Count''. [[File:Motherboard diagram fr.svg|centre|vignette|upright=1.5|Carte mère avec circuit Super IO.]] ===L'architecture des PC depuis les années 2000=== Depuis la sortie du processeur AMD Athlon 64, le pont nord a été fusionné dans le processeur. La fusion ne s'est pas faite en une fois, des fonctionnalités ont progressivement été progressivement intégrées dans le processeur. Le pont sud est resté, mais il a alors été progressivement connecté directement au processeur. La raison derrière cette intégration est que les processeurs avaient de plus en plus de transistors à leur disposition. Ils en ont profité pour intégrer le pont nord. Et cela permettait de simplifier le câblage des cartes mères, sans pour autant rendre vraiment plus complexe la fabrication du processeur. Les industriels y trouvent leur compte. La première étape a été l'intégration du contrôleur mémoire a été intégré au processeur. Concrètement, le résultat était que la mémoire RAM n'était plus connectée au pont nord, mais était connectée directement au processeur ! Il y a donc eu un retour d'un bus mémoire, mais spécialisé pour la mémoire RAM. En théorie, une telle intégration permet diverses optimisations quant aux transferts avec la mémoire RAM. Les transferts ne passent pas par un répartiteur, ce qui réduit le temps d'accès à la mémoire RAM. Ajoutons de sombres histoires de prefetching, d'optimisation des commandes, et j'en passe. Toujours est-il que le pont nord ne servait alors d'intermédiaire que pour les ports PCI Express, et le pont sud. [[File:X58 Block Diagram.png|centre|vignette|upright=2|Chipset X58 d'Intel.]] Par la suite, la carte graphique fût aussi connectée directement sur le processeur. Le processeur incorpore pour cela des contrôleurs PCI-Express. Le pont nord a alors disparu complétement, son intégration dans le processeur était complète. Sur les cartes mères Intel récentes, le pont sdud subsiste, il est appelé le ''Platform Controler Hub'', ou PCH. L'organisation des bus sur la carte mère qui résulte de cette connexion du processeur à la carte graphique, est illustrée ci-dessous, avec l'exemple du PCH. [[File:Intel 5 Series architecture.png|centre|vignette|upright=2|Intel 5 Series architecture]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=L'interface électrique entre circuits intégrés et bus | prevText=L'interface électrique entre circuits intégrés et bus | next=La hiérarchie mémoire | nextText=La hiérarchie mémoire }} </noinclude> mgpawxt4lpqiptu22mnxiwsshvw0n8s 762859 762847 2026-04-03T19:43:49Z Mewtow 31375 /* L'architecture de la Megadrive et de la néo-géo */ 762859 wikitext text/x-wiki Dans les chapitres précédents, nous avons vu comment représenter de l'information, la traiter et la mémoriser avec des circuits. Mais un ordinateur n'est pas qu'un amoncellement de circuits et est organisé d'une manière bien précise. Il est structuré autour de trois circuits principaux : * un '''processeur''', qui manipule l'information et donne un résultat ; * une '''mémoire''' qui mémorise les données à manipuler ; * les '''entrées/sorties''', qui permettent à l'ordinateur de communiquer avec l'extérieur. [[File:Architecture Von Neumann.png|centre|vignette|upright=2|Architecture d'un système à mémoire.]] Pour faire simple, le processeur est un circuit qui s'occupe de faire des calculs. Rien d'étonnant à cela. Je rappelle que tout est codé par des nombres dans un ordinateur, ce qui fait que manipuler des nombres revient simplement à faire des calculs. Un ordinateur n'est donc qu'une grosse calculatrice améliorée, et le processeur est le composant qui fait les calculs. La mémoire s'occupe purement de la mémorisation des données, des nombres sur lesquelles faire des calculs. Pour être plus précis, il y a deux mémoires : une pour les données proprement dites, une autre pour le programme à exécuter. La première est la '''mémoire RAM''', la seconde est la '''mémoire ROM'''. Nous détaillerons ce que sont ces deux mémoires dans la suite du chapitre, mais sachez que nous avions déjà rencontré ces deux types de mémoires dans les chapitres sur les registres et les mémoires adressables. Les entrées-sorties permettent au processeur et à la mémoire de communiquer avec l'extérieur et d'échanger des informations avec des périphériques. Les '''périphériques''' regroupent, pour rappel, tout ce est branché sur un ordinateur, mais n'est pas à l'intérieur de celui-ci. Le processeur, les mémoires et les entrées-sorties communiquent ensemble via un '''réseau d'interconnexions'''. Le terme est assez barbare, mais rien de compliqué sur le principe. C'est juste un ensemble de fils électriques qui relie les différents éléments d'un ordinateur. Les interconnexions sont souvent appelées le bus de communication, mais le terme est un abus de langage, comme on le verra plus bas. Afin de simplifier les explications, on va supposer que le réseau d'interconnexion est le suivant. Tout est connecté au processeur. Il y a des interconnexions entre le processeur et la mémoire RAM, d'autres interconnexions entre processeur et mémoire ROM, et d'autres entre le processeur et les entrées-sorties. Nous verrons que d'autres réseaux d'interconnexions fusionnent certaines interconnexions, pour les partager entre la ROM et la RAM, par exemple. Mais pour le moment, gardez le schéma ci-dessous en tête. [[File:Réseau d'interconnexion avec un processeur au centre.png|centre|vignette|upright=2|Réseau d'interconnexion avec un processeur au centre]] ==Les mémoires RAM et ROM== La mémoire est le composant qui mémorise des informations, des données. Dans la majorité des cas, la mémoire est composée de plusieurs '''cases mémoire''', chacune mémorisant plusieurs bits, le nombre de bits étant identique pour toutes les cases mémoire. Dans le cas le plus simple, une case mémoire mémorise un '''octet''', un groupe de 8 bits. Mais les mémoires modernes mémorisent plusieurs octets par case mémoire : elles ont des cases mémoires de 16, 32 ou 64 bits, soit respectivement 2/4/8 octets. De rares mémoires assez anciennes utilisaient des cases mémoires contenant 1, 2, 3, 4, 5, 6 7, 13, 17, 23, 36 ou 48 bits. Mais ce n'était pas des mémoires électroniques, aussi nous allons les passer sous silence. Tout ce qu'il faut savoir est que la quasi-totalité des mémoires électronique a un ou plusieurs octets par case mémoire. Pour simplifier, vous pouvez imaginer qu'une mémoire RAM est un regroupement de registre, chacun étant une case mémoire. C'est une description pas trop mauvaise pour décrire les mémoires RAM, qu'on abordera dans ce qui suit. {|class="wikitable" |+ Contenu d'une mémoire, case mémoire de 16 bits (deux octets) |- ! Case mémoire N°1 | 0001 0110 1111 1110 |- ! Case mémoire N°2 | 1111 1110 0110 1111 |- ! Case mémoire N°3 | 0001 0000 0110 0001 |- ! Case mémoire N°4 | 1000 0110 0001 0000 |- ! Case mémoire N°5 | 1100 1010 0110 0001 |- ! ... | ... |- ! Case mémoire N°1023 | 0001 0110 0001 0110 |- ! Case mémoire N°1024 | 0001 0110 0001 0110 |} Dans ce cours, il nous arrivera de partir du principe qu'il y a un octet par case mémoire, par souci de simplification. Mais ce ne sera pas systématique. De plus, il nous arrivera d'utiliser le terme adresse pour parler en réalité de la case mémoire associée, par métonymie. ===La capacité mémoire=== Bien évidemment, une mémoire ne peut stocker qu'une quantité finie de données. Et à ce petit jeu, certaines mémoires s'en sortent mieux que d'autres et peuvent stocker beaucoup plus de données que les autres. La '''capacité''' d'une mémoire correspond à la quantité d'informations que celle-ci peut mémoriser. Plus précisément, il s'agit du nombre maximal de bits qu'une mémoire peut contenir. Elle est le produit entre le nombre de cases mémoire, et la taille en bit d'une case mémoire. Toutes les mémoires actuelles utilisant des cases mémoire d'un ou plusieurs octets, ce qui nous arrange pour compter la capacité d'une mémoire. Au lieu de compter cette capacité en bits, on préfère mesurer la capacité d'une mémoire avec le nombre d'octets qu'elle contient. Mais les mémoires des PC font plusieurs millions ou milliards d'octets. Pour se faciliter la tâche, on utilise des préfixes pour désigner les différentes capacités mémoires. Vous connaissez sûrement ces préfixes : kibioctets, mébioctets et gibioctets, notés respectivement Kio, Mio et Gio. {|class="wikitable" |- !Préfixe!!Capacité mémoire en octets!!Puissance de deux |- ||Kio||1024||2<sup>10</sup> octets |- ||Mio||1 048 576||2<sup>20</sup> octets |- ||Gio||1 073 741 824||2<sup>30</sup> octets |} On peut se demander pourquoi utiliser des puissances de 1024, et ne pas utiliser des puissances un peu plus communes ? Dans la majorité des situations, les électroniciens préfèrent manipuler des puissances de deux pour se faciliter la vie. Par convention, on utilise souvent des puissances de 1024, qui est la puissance de deux la plus proche de 1000. Or, dans le langage courant, kilo, méga et giga sont des multiples de 1000. Quand vous vous pesez sur votre balance et que celle-ci vous indique 58 kilogrammes, cela veut dire que vous pesez 58 000 grammes. De même, un kilomètre est égal à 1000 mètres, et non 1024 mètres. Autrefois, on utilisait les termes kilo, méga et giga à la place de nos kibi, mebi et gibi, par abus de langage. Mais peu de personnes sont au courant de l'existence de ces nouvelles unités, et celles-ci sont rarement utilisées. Et cette confusion permet aux fabricants de disques durs de nous « arnaquer » : Ceux-ci donnent la capacité des disques durs qu'ils vendent en kilo, méga ou giga octets : l’acheteur croit implicitement avoir une capacité exprimée en kibi, mébi ou gibi octets, et se retrouve avec un disque dur qui contient moins de mémoire que prévu. ===Lecture et écriture : mémoires ROM et RWM=== Pour simplifier grandement, on peut grossièrement classer les mémoires en deux types : les ''Read Only Memory'' et les ''Read Write Memory'', aussi appelées mémoires ROM et mémoires RWM. Pour les '''mémoires ROM''', on ne peut pas modifier leur contenu. On peut y récupérer une donnée ou une instruction : on dit qu'on y accède en lecture. Mais on ne peut pas modifier les données qu'elles contiennent. Quant aux '''mémoires RWM''', on peut y accéder en lecture (récupérer une donnée stockée en mémoire), mais aussi en écriture : on peut stocker une donnée dans la mémoire, ou modifier une donnée existante. Tout ordinateur contient au minimum une ROM et une RWM (souvent une mémoire RAM), les deux n'ont pas exactement le même rôle. Pour simplifier, la mémoire ROM mémorise le programme à exécuter, la mémoire RWM stocke des données. Il a existé des ordinateurs où la mémoire RWM était une mémoire magnétique, voire acoustique, mais ce n'est plus le cas de nos jours. Pour les ordinateurs modernes, la mémoire RWM est une mémoire électronique. Pour faire la différence avec ces anciennes mémoires RWM, elle est appelée la '''mémoire RAM'''. Il s'agit d'une mémoire qui stocke temporairement des données que le processeur doit manipuler (on dit qu'elle est volatile). Elle s'efface complètement quand on coupe l'alimentation de l'ordinateur. Outre le programme à exécuter, la mémoire ROM peut mémoriser des constantes, des données qui ne changent pas. Elles ne sont jamais modifiées et gardent la même valeur quoi qu'il se passe lors de l'exécution du programme. En conséquence, elles ne sont jamais accédées en écriture durant l'exécution du programme, ce qui fait que leur place est dans une mémoire ROM. La mémoire RWM est alors destinée aux données temporaires, qui changent ou sont modifiées lors de l'exécution du programme, et qui sont donc manipulées aussi bien en lecture et en écriture. La mémoire RWM mémorise alors les variables du programme à exécuter, qui sont des données que le programme va manipuler. Pour les systèmes les plus simples, la mémoire RWM ne sert à rien de plus. [[File:Espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=2.5|Espaces d'adressage sur une archi harvard modifiée]] Pour donner un exemple de données stockées en ROM, on peut prendre l'exemple des anciennes consoles de jeu 8 et 16 bits. Les jeux vidéos sur ces consoles étaient placés dans des cartouches de jeu, précisément dans une mémoire ROM à l'intérieur de la cartouche de jeu. La ROM mémorisait non seulement le code du jeu, le programme du jeu vidéo, mais aussi les niveaux et les ''sprites'' et autres données graphiques. Une conséquence est que les consoles 8/16 bits n'avaient pas besoin de beaucoup de RAM, comparé aux ordinateurs de l'époque, vu qu'une grande partie des données utiles étaient dans une ROM directement accessible par le processeur. À l'opposé, les micro-ordinateurs devaient copier les données d'un jeu depuis une disquette dans la mémoire RAM, ce qui demandait d'avoir plus de RAM. Le passage au support CD sur les consoles 32 bits a eu la même conséquence. Le processeur ne pouvant pas lire directement le CD à sa guise, il fallait copier les données du CD en RAM. D'où l'apparition de temps de chargement assez longs, inexistants sur support cartouche. ===L'adressage mémoire=== Sur une mémoire RAM ou ROM, on ne peut lire ou écrire qu'une case mémoire, qu'un registre à la fois : une lecture ou écriture ne peut lire ou modifier qu'une seule case mémoire. Techniquement, le processeur doit préciser à quel case mémoire il veut accéder à chaque lecture/écriture. Pour cela, chaque case mémoire se voit attribuer un nombre binaire unique, l''''adresse''', qui va permettre de le sélectionner et de l'identifier celle-ci parmi toutes les autres. En fait, on peut comparer une adresse à un numéro de téléphone (ou à une adresse d'appartement) : chacun de vos correspondants a un numéro de téléphone et vous savez que pour appeler telle personne, vous devez composer tel numéro. Les adresses mémoires en sont l'équivalent pour les cases mémoire. [[File:Adressage mémoire.png|centre|vignette|upright=2|Exemple : on demande à la mémoire de sélectionner la case mémoire d'adresse 1002 et on récupère son contenu (ici, 17).]] L'adresse mémoire est générée par le processeur. Le processeur peut parfaitement calculer des adresses, en extraire du programme qu'il exécute, et bien d'autres choses. Nous détaillerons d'ailleurs les mécanismes pour dans les chapitres portant sur les modes d'adressage du processeur. Mais pour le moment, nous avons juste besoin de savoir que c'est le processeur qui envoie des adresses aux mémoires RAM et ROM. Les adresses générées par le processeur sont alors envoyées à la RAM ou la ROM via une connexion dédiée, un ensemble de fils qui connecte le processeur à la mémoire : le '''bus d'adresse mémoire'''. L'adresse sélectionne une case mémoire, le processeur peut alors récupérer la donnée dedans pour une lecture, écrire une donnée pour l'écriture. Pour cela, un second ensemble de fil connecte le processeur à la RAM/ROM, mais cette fois-ci pour échanger des données. Il s'agit du '''bus de données mémoire'''. Les deux sont souvent regroupés sous le terme de '''bus mémoire'''. Un ordinateur contient toujours une RAM et une ROM, ce qui demande aux bus mémoire de s'adapter à la présence de deux mémoires. Il y a alors deux solutions, illustrées dans les deux schémas ci-dessous. Avec la première, il y a un seul bus mémoire partagé entre la RAM et la ROM, comme illustré ci-dessous. Une autre solution utilise deux bus séparés : un pour la RAM et un autre pour la ROM. Nous verrons les différences pratiques entre les deux à la fin du chapitre. Pour le moment, nous allons partir du principe qu'il y a un bus pour la mémoire ROM, et un autre bus pour la RAM. [[File:CPT-System-Architecture-gapfill1-ANS.svg|centre|vignette|upright=2|Architecture avec une ROM et une RAM.]] [[File:Réseau d'interconnexion avec un processeur au centre.png|centre|vignette|upright=2|Réseau d'interconnexion avec un processeur au centre]] ===L'alignement mémoire : introduction=== Plus haut, nous avions dit qu'il y a une adresse par case mémoire, chaque case mémoire contenant un ou plusieurs octets. Mais les processeurs modernes partent du principe que la mémoire a un octet par adresse, pas plus. Et ce même si la mémoire reliée au processeur utilise des cases mémoires de 2, 3, 4 octets ou plus. D'ailleurs, la majorité des mémoires RAM actuelle a des cases mémoires de 64 bits, soit 8 octets par case mémoire. Les raisons à cela sont multiple, mais nous les verrons en détail dans le chapitre sur l'alignement mémoire. Toujours est-il qu'il faut distinguer les '''adresses mémoire''' et les '''adresses d'octet''' gérées par le processeur. Le processeur génère des adresses d'octet, qui permettent de sélectionner un octet bien précis. L'adresse d'octet permet de sélectionner un octet parmi tous les autres. Mais la mémoire ne comprend pas directement cette adresse d'octet. Heureusement, l'octet en question est dans une case mémoire bien précise, qui a elle-même une adresse mémoire bien précise. L'adresse d'octet est alors convertie en une adresse mémoire, qui sélectionne la case mémoire adéquate, celle qui contient l'octet voulu. La case mémoire entière est lue, puis le processeur ne récupère que les données adéquates. Pour cela, des circuits d'alignement mémoire se chargent de faire la conversion entre adresses du processeur et adresse mémoire. Nous verrons cela dans le détail dans le chapitre sur l'alignement mémoire. Il existe des mémoires qui n'utilisent pas d'adresses mémoire, mais passons : ce sera pour la suite du cours. ==Le processeur== Dans les ordinateurs, l'unité de traitement porte le nom de '''processeur''', ou encore de '''''Central Processing Unit''''', abrévié en CPU. Le rôle principal du processeur est de faire des calculs. La plupart des processeurs actuels supportent au minimum l'addition, la soustraction et la multiplication. Quelques processeurs ne gèrent pas la division, qui est une opération très gourmande en circuit, peu utilisée, très lente. Il arrive que des processeurs très peu performants ne gèrent pas la multiplication, mais c'est assez rare. Un processeur ne fait pas que des calculs. Tout processeur est conçu pour effectuer un nombre limité d'opérations bien précises, comme des calculs, des échanges de données avec la mémoire, etc. Ces opérations sont appelées des '''instructions'''. Les plus intuitives sont les '''instructions arithmétiques''', qui font des calculs, comme l'addition, la soustraction, la multiplication, la division. Mais il y a aussi des '''instructions d'accès mémoire''', qui échangent des données entre la mémoire RAM et le processeur. Les autres instructions ne sont pas très intuitives, aussi passons-les sous silence pour le moment, tout deviendra plus clair dans les chapitres sur le processeur. ===Le processeur exécute un programme, une suite d'instructions=== Tout processeur est conçu pour exécuter une suite d'instructions dans l'ordre demandé, cette suite s'appelant un '''programme'''. Ce que fait le processeur est défini par la suite d'instructions qu'il exécute, par le programme qu'on lui demande de faire. Les instructions sont exécutées dans un ordre bien précis, les unes après les autres. L'ordre en question est décidé par le programmeur. La totalité des logiciels présents sur un ordinateur sont des programmes comme les autres. Le programme à exécuter est stockée dans la mémoire de l'ordinateur. C'est ainsi que l'ordinateur est rendu programmable : modifier le contenu de la mémoire permet de changer le programme exécuté. Mine de rien, cette idée de stocker le programme en mémoire est ce qui a fait que l’informatique est ce qu'elle est aujourd’hui. C'est la définition même d'ordinateur : appareil programmable qui stocke son programme dans une mémoire modifiable. Une instruction est codée comme les données : sous la forme de suites de bits. Telle suite de bit indique qu'il faut faire une addition, telle autre demande de faire une soustraction, etc. Pour simplifier, nous allons supposer qu'il y a une instruction par adresse mémoire. Sur la grosse majorité des ordinateurs, les instructions sont placées les unes à la suite des autres dans l'ordre où elles doivent être exécutées. Un programme informatique n'est donc qu'une vulgaire suite d'instructions stockée quelque part dans la mémoire de l'ordinateur. {|class="wikitable" |+ Exemple de programme informatique |- ! Adresse ! Instruction |- ! 0 | Copier le contenu de l'adresse 0F05 dans le registre numéro 5 |- ! 1 | Charger le contenu de l'adresse 0555 dans le registre numéro 4 |- ! 2 | Additionner ces deux nombres |- ! 3 | Charger le contenu de l'adresse 0555 |- ! 4 | Faire en XOR avec le résultat antérieur |- ! ... | ... |- ! 5464 | Instruction d'arrêt |} Pour exécuter une suite d'instructions dans le bon ordre, le processeur détermine à chaque cycle quelle est la prochaine instruction à exécuter. Pour cela, le processeur mémorise l'adresse de l'instruction en cours dans un registre : le '''Program Counter'''. Je rappelle que des instructions consécutives sont dans des adresses consécutives. Pour passer à la prochaine instruction, il suffit donc d'incrémenter le ''program counter''. : Si une instruction prend plusieurs octets, plusieurs adresses, il suffit de l'incrémenter du nombre d'octets/adresses. D'autres processeurs font autrement : chaque instruction précise l'adresse de la suivante, directement dans la suite de bit représentant l'instruction en mémoire. Ces processeurs n'ont pas besoin de calculer une adresse qui leur est fournie sur un plateau d'argent. Sur des processeurs aussi bizarres, pas besoin de stocker les instructions en mémoire dans l'ordre dans lesquelles elles sont censées être exécutées. Mais ces processeurs sont très très rares et peuvent être considérés comme des exceptions à la règle. Nous venons de voir qu'un processeur contient un registre appelé le ''program counter''. Mais il n'est pas le seul. Pour pouvoir fonctionner, tout processeur doit mémoriser un certain nombre d’informations nécessaires à son fonctionnement, qui sont mémorisées dans des '''registres de contrôle'''. La plupart ont des noms assez barbares (registre d'état, ''program counter'') et nous ne pouvons pas en parler à ce moment du cours. Nous les verrons en temps voulu, mais il est important de préciser qu'ils existent. ===L'intérieur d'un processeur=== Fort de ce que nous savons, nous pouvons expliquer ce qu'il y a à l'intérieur d'un processeur. Le premier point est qu'un processeur fait des calculs, ce qui implique qu'il contient des circuits de calcul. Ils sont regroupés dans une ou plusieurs '''unités de calcul'''. Nous avons déjà vu comment fabriquer une unité de calcul simple, dans un chapitre dédié, et c'est la même qui est présente dans un processeur. Du moins dans les grandes lignes, les circuits des processeurs modernes étant particulièrement optimisés. Il en est de même pour les autres circuits de calcul comme ceux pour les multiplications/division/autres. Si le processeur fait des calculs, qu'en est-il des opérandes ? Et où sont mémorisés les résultats des opérations ? Pour cela, le processeur incorpore des '''registres généraux'''. Les registres généraux servent à mémoriser les opérandes et résultats des instructions. Ils mémorisent des données, contrairement aux registres de contrôle mentionnés plus haut. Le nombre de registres généraux dépend grandement du processeur. Les tout premiers processeurs se débrouillaient avec un seul registre, mais les processeurs actuels utilisent plusieurs registres, pour mémoriser plusieurs opérandes/résultats. Mais la présence de registres est source de pas mal de petites complications. Par exemple, il faut échanger les données entre la RAM et les registres, il faut gérer l'adressage des registres, etc. Il s'agit là de détails que nous expliquerons dans les chapitres sur le processeur. Le processeur contient enfin un circuit pour interpréter les instructions, appelé l''''unité de contrôle'''. Elle lit les instructions depuis la mémoire, interprète la suite de bit associée, et commande le reste du processeur pour qu'il exécute l'instruction. Ses fonctions sont assez variées, mais nous allons simplifier en disant qu'elle configure l'unité de calcul et les registres pour faire le bon calcul. Son rôle est d'analyser la suite de bit qui constitue l'instruction, et d'en déduire quelle opération effectuer. Elle gère aussi l'accès à la mémoire RAM, et notamment ce qui est envoyé sur son bus d'adresse. : Dans ce qui suit, on suppose que le ''program counter'' fait partie de l'unité de contrôle. Pour résumer, un processeur contient une unité de calcul, des registres et une interface avec la mémoire RAM. Le tout est interconnecté, afin de pouvoir échanger des données. L’ensemble forme le '''chemin de données''', nom qui trahit le fait que c'est là que les données se déplacent et sont traitées. Il faut aussi ajouter l'unité de contrôle pour commander le tout. Elle lit les instructions en mémoire, puis commande le chemin de données pour que l'instruction soit exécutée correctement. [[File:Microarchitecture d'un processeur.png|centre|vignette|upright=2|Microarchitecture d'un processeur]] Un processeur parait donc assez simple expliqué comme ça, mais il y a de nombreuses subtilités. L'une d'entre elle est liée aux registres, et notamment à la manière dont on les utilise. Pour expliquer ces subtilités, nous allons voir comment les premiers processeurs fonctionnaient, avant de passer aux processeurs un peu plus modernes. Les tout premiers processeurs n'utilisaient qu'un seul registre, ce qui fait que l'utilisation des registres était très simple. Le passage à plusieurs registres a complexifié le tout. ===D'où viennent les adresses ?=== Il est maintenant temps de répondre à une question qui s'était posée dans la section sur l'adressage : d'où viennent les adresses envoyées à la mémoire ? Pour ce qui est des adresses des instructions, on connait déjà la réponse : c'est le ''program counter'' qui gère tout. Mais pour les données, il y a deux possibilités, qui correspondent à deux types de données : les données statiques et les données dynamiques. Les '''données statiques''' sont les plus simples : elles existent durant toute la durée de vie du programme. Tant que celui-ci s'exécute, il aura besoin de ces données. En conséquence, il leur réserve une place en mémoire, qui est toujours la même. La donnée se situe donc à une adresse bien précise, qui ne change jamais. Attention cependant : les données peuvent être modifiées, changer de valeur. Le programme écrit dans les donnée statiques, c'est même assez fréquent. Ce sont ne sont pas forcément des données constantes ! Pour les données statiques, elles sont toujours placées à la même adresse mémoire. Pour le dire autrement, l'adresse mémoire est une constante, qui ne change pas. L'adresse est connue avant d’exécuter le programme, le programme a été codé pour utiliser cette adresse pour telle donnée, on a réservé une adresse pour la donnée voulue. Et même si vous quittez le programme et vous le relancez plusieurs jours après, l'adresse mémoire sera la même avant et après. Et cela permet d'utiliser l''''adressage direct'''. L'idée est que les instructions précisent donc l'adresse à lire ou écrire. Pour cela, l'adresse est intégrée dans l’instruction elle-même. Pour rappel, l'instruction est codée par une suite de bit en mémoire RAM/ROM, dont certains précisent l'opération à faire, les autres servant à autre chose. L'idée est que certains bits précisent l'adresse mémoire de la donnée à lire. Les instructions sont donc du genre : * ''LOAD 56'' - lit l'adresse numéro 56 et copie son contenu dans un registre; * ''STORE R5 , adress 99'', copie le registre R5 dans l'adresse 99 ; * ''ADD R5 , adress 209'' : additionne le registre R5 avec le contenu de l'adresse 209. S'il existe des données statiques, c'est signe qu'il existe des '''données dynamiques'''. Ces dernières sont des données qui sont créées ou détruites selon les besoins. Pour comprendre d'où viennent ces données dynamiques, prenons le cas d'une personne qui écrit du texte sur un ordinateur. Le texte qu'il écrit est mémorisé dans la RAM de l’ordinateur, puis est sauvegardé sur le disque dur quand il sauvegarde son document. Au fur et à mesure qu'il écrit du texte, la RAM utilisée par ce texte augmente. Donc, ce texte est une donnée dynamique, dont la taille varie dans le temps. Pour gérer des données dynamiques, la plupart des systèmes d'exploitation incorporent des fonctionnalités d''''allocation mémoire'''. Derrière ce nom barbare, se cache quelque chose de simple : les programmes peuvent réclamer de la mémoire au système d'exploitation, pour y placer les données qu'ils souhaitent. Les langages de programmation bas niveau supportent des fonctions comme malloc(), qui permettent de demander un bloc de mémoire de N octets à l'OS, qui doit alors accommoder la demande. De même, un programme peut libérer de la mémoire qu'il n'utilise plus avec des fonctions comme free(). Avec l'allocation mémoire, les données n'ont pas de place fixe en mémoire. Leur adresse mémoire peut varier d'une exécution du programme à l'autre. Pire que ça : les données peuvent être déplacées dans la mémoire RAM, si besoin. En clair : l'adresse est déterminée lors de l'exécution du programme. L'adresse est alors soit calculée, soit lue depuis la mémoire RAM, soit déterminée autrement. Toujours est-il qu'elle se retrouve dans un registre général. Pour gérer ces adresse variables, les processeurs utilisent l''''adressage indirect'''. L'adressage indirect permet d'utiliser une adresse qui est dans un registre de données. L'adresse en question peut être envoyée à la mémoire RAM sans problème, le processeur peut automatiquement connecter le registre adéquat sur le bus d'adresse. Au tout début de l'informatique, les processeurs ne supportaient que l'adressage direct, pas plus. L'adressage indirect n'était tout simplement pas possible. Avec l'adressage direct, l'adresse à lire est extraite directement des instructions, par l'unité de contrôle. Le bus d'adresse de la RAM est alors relié directement à l'unité de contrôle, le bus de données est relié aux registres. [[File:Architecture Harvard avec adressage direct uniquement.png|centre|vignette|upright=2|Architecture Harvard avec adressage direct uniquement]] Mais dès les années 70, l'adressage indirect est apparu, rendant la programmation bien plus simple. Mais cela a demandé quelques adaptation au niveau du processeur. Avec l'adressage indirect, le bus d'adresse est connecté aux registres. Il a donc fallu rajouter un multiplexeur pour que le processeur décide de relier le bus d'adresse soit aux registres, soit à l'unité de contrôle. ===Un ordinateur peut avoir plusieurs processeurs=== La plupart des ordinateurs n'ont qu'un seul processeur, ce qui fait qu'on désigne avec le terme d''''ordinateurs mono-processeur'''. Mais il a existé (et existe encore) des '''ordinateurs multi-processeurs''', avec plusieurs processeurs sur la même carte mère. L'idée était de gagner en performance : deux processeurs permettent de faire deux fois plus de calcul qu'un seul, quatre permettent d'en faire quatre fois plus, etc. C'est très courant sur les supercalculateurs, des ordinateurs très puissants conçus pour du calcul industriel ou scientifique, mais aussi sur les serveurs ! Dans le cas le plus courant, ils utilisent plusieurs processeurs identiques : on utilise deux processeurs Core i3 de même modèle, ou quatre Pentium 3, etc. Pour utiliser plusieurs processeurs, les programmes doivent être adaptés. Pour cela, il y a plusieurs possibilités : * Une première possibilité, assez intuitive, est d’exécuter des programmes différents sur des processeurs différents. Par exemple, on exécute le navigateur web sur un processeur, le lecteur vidéo sur un autre, etc. * La seconde option est de créer des programmes spéciaux, qui utilisent plusieurs processeurs. Ils répartissent les calculs à faire sur les différents processeurs. Un exemple est la lecture d'une vidéo sur le web : un processeur peut télécharger la vidéo pendant le visionnage et bufferiser celle-ci, un autre processeur peut décoder la vidéo, un autre décoder l'audio. De tels programmes restent des suites d'instructions, mais ils sont plus complexes que les programmes normaux, aussi nous les passons sous silence. * La troisième option est d’exécuter le même programme sur les différents processeurs, mais chaque processeur traite son propre ensemble de données. Par exemple, pour un programme de rendu 3D, quatre processeurs peuvent s'occuper chacun d'une portion de l'image. [[File:Architecture de Von Neumann Princeton multi processeurs.svg|centre|vignette|upright=2|Architecture de Von Neumann Princeton multi processeurs]] De nos jours, les ordinateurs grand public les plus utilisés sont dans un cas intermédiaire, ils ne sont ni mono-, ni multi-processeur. Ils n'ont qu'un seul processeur, dans le sens où si on ouvre l'ordinateur et qu'on regarde la carte mère, il n'y a qu'un seul processeur. Mais ce processeur est en réalité assez similaire à un regroupement de plusieurs processeurs dans le même boitier. Il s'agit de '''processeurs multicœurs''', qui contiennent plusieurs cœurs, chaque cœur pouvant exécuter un programme tout seul. La différence entre cœur et processeur est assez difficile à saisir, mais pour simplifier : un cœur est l'ensemble des circuits nécessaires pour exécuter un programme. Chaque cœur dispose de toute la machinerie électronique pour exécuter un programme, à savoir des circuits aux noms barbares comme : un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits d'un processeur ne sont présents qu'en un seul exemplaire dans un processeur multicœur, comme les circuits de communication avec la mémoire ou les circuits d’interfaçage avec la carte mère. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Un processeur double-cœur est équivalent à avoir deux processeurs dans l'ordinateur, un processeur quadruple-cœur est équivalent à avoir quatre processeurs dans l'ordinateur, etc. Ces processeurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. ===Les coprocesseurs=== Quelques ordinateurs assez anciens disposaient de '''coprocesseurs''', des processeurs qui complémentaient un processeur principal. Les ordinateurs de ce type avaient un processeur principal, le '''CPU''', qui était secondé par un ou plusieurs coprocesseurs. Sauf exception, le CPU et le coprocesseur exécutent des programmes différents, ils travaillent en parallèle. Les coprocesseurs les plus connus sont les '''coprocesseurs pour le rendu 2D/3D''' et les '''coprocesseurs sonores'''. Ils ont eu leur heure de gloire sur les anciennes consoles de jeux vidéo, comme La Nintendo 64, la Playstation et autres consoles de cette génération ou antérieure. Ils s'occupaient respectivement de calculer les graphismes des jeux vidéos, et de calculer tout ce qui a trait au son. Pour donner un exemple, on peut citer la console Neo-géo, qui disposait de deux processeurs travaillant en parallèle : un processeur principal, et un co-processeur sonore. Le processeur principal était un Motorola 68000, alors que le co-processeur sonore était un processeur Z80. L'accès aux périphériques est quelque chose sur lequel nous passerons plusieurs chapitres dans ce cours. Mais sachez que l'accès aux périphériques peut demander pas mal de puissance de calculs. Le CPU principal peut faire ce genre de calculs par lui-même, mais il n'est pas rare qu'un '''coprocesseur d'IO''' soit dédié à l'accès aux périphériques. Un exemple assez récent est celui de la console de jeu Nintendo 3DS. Elle disposait d'un processeur principal de type ARM9, d'un coprocesseur pour les divisions qu'on abordera plus bas, et d'un second processeur ARM7. L'ARM 7 était utilisé comme coprocesseur d'I/O, ainsi que pour l'émulation de la console GBA. [[File:Asmp 2.gif|centre|vignette|upright=2|Co-processeur pour l'accès aux entrées-sorties.]] Les '''coprocesseurs arithmétiques''' sont un peu à part des autres. Ils permettent de faire certains calculs que le processeur ne peut pas faire. Les plus connus d'entre eux étaient utilisés pour implémenter les calculs en virgule flottante, à une époque où les CPU de l'époque ne géraient que des calculs entiers (en binaire ou en BCD). Un exemple est le coprocesseur flottant x87, complémentaire des premiers processeurs Intel x86. Il y a eu la même chose sur les processeurs Motorola 68000, avec deux coprocesseurs flottants appelés les Motorola 68881 et les Motorola 68882. Les coprocesseurs arithmétiques étaient optionnels et il était parfaitement possible de monter un PC qui n'en avait pas. En conséquence, les programmeurs devaient coder des programmes qui peuvent fonctionner avec et sans co-processeur. La solution la plus simple était de fournir deux versions du logiciel : une sans usage du coprocesseur, et une autre qui en fait usage, plus rapide. Sans ces coprocesseurs, les calculs flottants étaient émulés en logiciel, par des fonctions et libraires spécialisées, très lentes. Certaines applications conçues pour le coprocesseur étaient capables d'en tirer profit : des logiciels de conception assistée par ordinateur, par exemple. Ils sont aujourd'hui tombés en désuétude, depuis que les CPU sont devenus capables de faire des calculs sur des nombres flottants. Un exemple récent de coprocesseur est celui utilisé sur la console de jeu Nintendo DS. La console utilisait deux processeurs, un ARM9 et un ARM7, qui ne pouvaient pas faire de division entière. Il s'agit pourtant d'opérations importantes dans le cas du rendu 3D, ce qui fait que les concepteurs de la console ont rajouté un coprocesseur spécialisé dans les divisions entières et les racines carrées. Le coprocesseur était adressable directement par le processeur, comme peuvent l'être la RAM ou les périphériques. Les co-processeurs arithmétiques se distinguent des autres car ils fonctionnent en tandem avec le processeur principal, pas en parallèle. Les co-processeurs précédents sont autonomes, à savoir qu'ils exécutent un programme différent de celui exécuté par le CPU. Mais les co-processeurs arithmétiques ne sont pas dans ce cas. Il n'y a qu'un seul programme à exécuter, qui contient des instructions à destination du CPU, d'autres à destination du co-processeur. Les instructions sont exécutées soit par le CPU, soit par le co-processeur, une par une. En clair, le CPU et le co-processeur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''. ==Les entrées-sorties== Tous les circuits vus précédemment traitent des données codées en binaire. Ceci dit, les données ne sortent pas de n'importe où : l'ordinateur contient des composants électroniques qui traduisent des informations venant de l’extérieur en nombres. Ces composants sont ce qu'on appelle des '''entrées'''. Par exemple, le clavier est une entrée : l'électronique du clavier attribue un nombre entier (''scancode'') à une touche, nombre qui sera communiqué à l’ordinateur lors de l'appui d'une touche. Pareil pour la souris : quand vous bougez la souris, celle-ci envoie des informations sur la position ou le mouvement du curseur, informations qui sont codées sous la forme de nombres. La carte son évoquée il y a quelques chapitres est bien sûr une entrée : elle est capable d'enregistrer un son, et de le restituer sous la forme de nombres. S’il y a des entrées, on trouve aussi des '''sorties''', des composants électroniques qui transforment des nombres présents dans l'ordinateur en quelque chose d'utile. Ces sorties effectuent la traduction inverse de celle faite par les entrées : si les entrées convertissent une information en nombre, les sorties font l'inverse : là où les entrées encodent, les sorties décodent. Par exemple, un écran LCD est un circuit de sortie : il reçoit des informations, et les transforme en image affichée à l'écran. Même chose pour une imprimante : elle reçoit des documents texte encodés sous forme de nombres, et permet de les imprimer sur du papier. Et la carte son est aussi une sortie, vu qu'elle transforme les sons d'un fichier audio en tensions destinées à un haut-parleur : c'est à la fois une entrée, et une sortie. Les '''entrées-sorties''' incluent toutes les entrées et sorties, et même certains composants qui sont les deux à la fois. Il s'agit d'un terme générique, qui regroupe des composants forts différents. Dans ce qui va suivre, nous allons parfois parler de périphériques au lieu d'entrées-sorties, mais les deux termes ne sont pas équivalents. Dans le détail, les entrées-sorties regroupent : * Les '''périphériques''' sont les composants connectés sur l'unité centrale. Exemple : les claviers, souris, webcam, imprimantes, écrans, clés USB, disques durs externes, la Box internet, etc. * Les '''cartes d'extension''', qui se connectent sur la carte mère via un connecteur, comme les cartes son ou les cartes graphiques. * D'autres composants sont soudés à la carte mère mais sont techniquement des entrées-sorties : les cartes sons soudées sur les cartes mères actuelles, par exemple. ===L'interface avec le reste de l'ordinateur=== Les entrées-sorties sont très diverses, fonctionnent très différemment les unes des autres. Mais du point de vue du reste de l'ordinateur, les choses sont relativement standardisées. Du point de vue du processeur, les entrées-sorties sont juste des paquets de registres ! Tous les périphériques, toutes les entrées-sorties contiennent des '''registres d’interfaçage''', qui permettent de faire l'intermédiaire entre l'entrée/sortie et le reste de l'ordinateur. L'entrée/sortie est conçu pour réagir automatiquement quand on écrit dans ces registres. [[File:Registres d'interfaçage.png|centre|vignette|upright=2|Registres d'interfaçage.]] Les registres d’interfaçage sont assez variés. Les plus évidents sont les '''registres de données''', qui permettent l'échange de données entre le processeur et les périphériques. Pour échanger des données avec l'entrée/sortie, le processeur a juste à lire ou écrire dans ces registres de données. On trouve généralement un registre de lecture et un registre d'écriture, mais il se peut que les deux soient fusionnés en un seul registre d’interfaçage de données. Si le processeur veut envoyer une donnée à une entrée/sortie, il a juste à écrire dans ces registres. Inversement, s'il veut lire une donnée, il a juste à lire le registre adéquat. Mais le processeur ne fait pas que transmettre des données à l'entrée/sortie. Le processeur lui envoie aussi des « commandes », des valeurs numériques auxquelles l'entrée/sortie répond en effectuant un ensemble d'actions préprogrammées. En clair, ce sont l'équivalent des instructions du processeur, mais pour l'entrée/sortie. Par exemple, les commandes envoyées à une carte graphique peuvent être : affiche l'image présente à cette adresse mémoire, calcule le rendu 3D à partir des données présentes dans ta mémoire, etc. Pour recevoir les commandes, l'entrée/sortie contient des ''registres de commande'' qui mémorisent les commandes envoyées par le processeur. Quand le processeur veut envoyer une commande à l'entrée/sortie, il écrit la commande en question dans ce ou ces registres. Enfin, beaucoup d'entrée/sortie ont un ''registre d'état'', lisible par le processeur, qui contient des informations sur l'état de l'entrée/sortie. Ils servent notamment à indiquer au processeur que l'entrée/sortie est disponible, qu'il est en train d’exécuter une commande, qu'il est occupé, qu'il y a un problème, qu'il y a une erreur de configuration, etc. ===Les adresses des registres d’interfaçage=== Les registres des périphériques sont identifiés par des adresses mémoires. Et les adresses sont conçues de façon à ce que les adresses des différentes entrées/sorties ne se marchent pas sur les pieds. Chaque entrée/sortie, chaque registre, chaque contrôleur a sa propre adresse. D'ordinaire, certains bits de l'adresse indiquent quel est le destinataire. Certains indiquent quel est l'entrée/sortie voulue, les restants indiquant le registre de destination. Il existe deux organisations possibles pour les adresses des registres d’interfaçages. La première possibilité est de séparer les adresses pour les registres d’interfaçage et les adresses pour la mémoire. Le processeur doit avoir des instructions séparées pour gérer les périphériques et adresser la mémoire. Il a des instructions de lecture/écriture pour lire/écrire en mémoire, et d'autres pour lire/écrire les registres d’interfaçage. Sans cela, le processeur ne saurait pas si une adresse est destinée à un périphérique ou à la mémoire. [[File:Espaces d'adressages séparés entre mémoire et périphérique.png|centre|vignette|upright=2.5|Espaces d'adressages séparés entre mémoire et périphérique]] L'autre méthode mélange les adresses mémoire et des entrées-sorties. Si on prend par exemple un processeur de 16 bits, où les adresses font 16 bits, alors les 65536 adresses possibles seront découpées en deux portions : une partie ira adresser la RAM/ROM, l'autre les périphériques. On parle alors d''''entrées-sorties mappées en mémoire'''. L'avantage est que le processeur n'a pas besoin d'avoir des instructions séparées pour les deux. [[File:IO mappées en mémoire.png|centre|vignette|upright=2.0|IO mappées en mémoire]] Pour résumer, communiquer avec une entrée/sortie est similaire à ce qu'on a avec les mémoires. Il suffit de lire ou écrire dans des registres d’interfaçage, qui ont chacun une adresse mémoire. Le problème est que le système d'exploitation ne connaît pas toujours le fonctionnement d'une entrée/sortie : il faut installer un programme qui va s'exécuter quand on souhaite communiquer avec l'entrée/sortie, et qui s'occupera de tout ce qui est nécessaire pour le transfert des données, l'adressage du périphérique, etc. Ce petit programme est appelé un driver ou '''pilote de périphérique'''. La « programmation » périphérique est très simple : il suffit de savoir quoi mettre dans les registres, et c'est le pilote qui s'en charge. ==Les architectures Harvard et Von Neumann== Après avoir vu le processeur, les mémoires et les entrées-sorties, voyons voir comment le tout est interconnecté. Tous les ordinateurs ne sont pas organisés de la même manière, pour ce qui est de leurs bus. Mais pour comprendre pourquoi, nous devons regarder qui communique avec qui, dans un ordinateur. Pour rappel, les données sont placées en mémoire RAM, alors que les instructions sont placées en mémoire ROM. Le processeur lit des instructions dans la mémoire ROM, il lit et écrit dans la mémoire RAM, et accède aux registres d’interfaçage des entrées-sorties. Il y a donc besoins de trois interconnexions : CPU-ROM, CPU-RAM et CPU-IO. [[File:Réseau d'interconnexion avec un processeur au centre.png|centre|vignette|upright=2|Réseau d'interconnexion avec un processeur au centre]] Il parait intéressant d'utiliser trois interconnexions, au minimum CPU-ROM, CPU-RAM et CPU-IO. Néanmoins, faire ainsi a de nombreux désavantages. Déjà, il faut pouvoir brancher tout ça sur le processeur. Et celui-ci n'a pas forcément assez de broches pour. Aussi, il est parfois préférable de mutualiser des bus, à savoir de connecter plusieurs composants sur un même bus. Par exemple, on peut mutualiser le bus pour la mémoire RAM et pour la mémoire ROM. Il faut dire que les deux bus sont des bus mémoire, avec un bus d'adresse, un bus de données, et surtout : des bus de commande similaires. Les mutualiser est alors très simple, et permet d'économiser pas mal de broches. [[File:Réseau d'interconnexion avec un processeur au centre et une architecture Harvard.png|centre|vignette|upright=2|Réseau d'interconnexion avec un processeur au centre et une architecture Harvard]] Cette mutualisation nous amène naturellement à parler de la distinction entre les architectures Harvard d'un côté et les architectures Von Neumann de l'autre. Elle est très liée au fait d'utiliser soit un bus mémoire unique, soit des bus séparés pour la ROM et la RAM. Voyons cela en détail. ===Les architectures Harvard et Von Neumann : des bus séparés ou unifiés=== Avec l''''architecture Harvard''', la mémoire ROM et la mémoire RAM sont reliées au processeur par deux bus séparés. Il y a un bus RAM pour la mémoire RAM, un bus ROM pour la mémoire ROM. L'avantage de cette architecture est qu'elle permet de charger une instruction et une donnée simultanément : une instruction chargée sur le bus relié à la mémoire programme, et une donnée chargée sur le bus relié à la mémoire de données. Et cela simplifie fortement la conception du processeur. [[File:Harvard Architecture.png|centre|vignette|upright=2|Architecture Harvard, avec une ROM et une RAM séparées.]] Avec l''''architecture Von Neumann''', mémoire ROM et mémoire RAM sont reliées au processeur par un bus unique. Le bus unique qui relie processeur, RAM et ROM, s'appelle le '''bus mémoire'''. Un défaut de ces architecture est qu'elles ne peuvent pas charger une instruction et une donnée en même temps. Et cela pose quelques problèmes pour la conception du processeur. Par contre, nous verrons dans ce qui suit qu'utiliser un bus mémoire partagé est bien plus flexible et permet des choses que les architectures Harvard ne peuvent pas faire. [[File:Architecture Von Neumann, avec deux bus séparés.png|centre|vignette|upright=2|Architecture Von Neumann, avec deux bus séparés.]] ===Les architectures Harvard et Von Neumann : des espaces d'adressage séparés ou unifiés=== La distinction précédente se base sur les connexions entre RAM, ROM et processeur. Mais il existe une autre distinction, très liée, qui est souvent utilisée comme seconde définition des architectures Harvard/Von Neumann. Elle est liée aux adresses mémoire que le processeur peut gérer. Prenons un processeur 16 bits, par exemple, qui gère naturellement des adresses de 16 bits. Il peut gérer 2^16 adresses, soit 64 kibioctets de mémoire. L'ensemble de ces adresses est appelé un '''espace d'adressage'''. Mais comment cet espace d'adressage est utilisé pour adresser une RAM et une ROM ? Sur les architectures Harvard, le processeur voit deux mémoires séparées avec leur lot d'adresses distinctes. Une même adresse peut donc correspondre soit à la mémoire ROM, soit à la mémoire RAM, suivant le bus utilisé. L'espace d'adressage est donc doublé, dupliqué, avec un pour la ROM, un autre pour la RAM. Rien d'étonnant à cela : il y a deux bus d'adresses, chacun correspondant à un espace d'adressage. [[File:Vision de la mémoire par un processeur sur une architecture Harvard.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Harvard.]] Avec l'architecture Von Neumann, la RAM et la ROM doivent se partager les adresses mémoires disponibles. Il n'y a qu'un seul espace d'adressage qui est coupé en deux, avec une partie pour la ROM et une autre pour la RAM. Une adresse correspond soit à la mémoire RAM, soit à la mémoire ROM, mais pas aux deux. Typiquement, la mémoire ROM occupe une partie des adresses, la mémoire RAM utilise le reste. La répartition des adresses est réalisée par les circuits de décodage d'adresse mentionnés plus haut. [[File:Vision de la mémoire par un processeur sur une architecture Von Neumann.png|centre|vignette|upright=2|Vision de la mémoire par un processeur sur une architecture Von Neumann.]] Les '''architectures Harvard modifiées''' sont des intermédiaires entre architectures Harvard et architectures Von Neumann, bien qu'elles penchent bien plus du côté des architectures Harvard. Précisons que la terminologie n'est pas claire, beaucoup d'auteurs mettent des définitions différentes derrière ces deux termes. Mais dans ce cours, nous utiliserons une définition très stricte de ce qu'est une architecture Harvard modifiée. Une architecture Harvard modifiée est une architecture Harvard, où le processeur peut lire des données constantes depuis la mémoire ROM. Nous avions vu plus haut que les mémoires ROM peuvent mémoriser, en plus d'un programme exécutable, des données constantes, qui ne varient pas. Les architectures Harvard pures ne permettent pas de lire des données de ce genre depuis la mémoire ROM, alors que les architectures Harvard modifiées le permettent. Une architecture Harvard modifiée dispose d'une instruction pour lire les données en mémoire RWM, et d'une instruction pour lire des données en mémoire ROM. Il y a donc deux versions de l'instruction LOAD, qui copient la donnée dans un registre général, mais dont la source de la donnée est différente. Une autre possibilité, plus rare, est que une instruction de copie, qui copie une constante depuis la mémoire ROM vers la mémoire RAM. Le cas le plus commun est l'utilisation de deux instructions LOAD séparées. [[File:Espaces d'adressage sur une archi harvard modifiée.png|centre|vignette|upright=2.5|Espaces d'adressage sur une archi harvard modifiée]] Ceci étant dit, revenons à la distinction entre architecture Harvard et Von Neumann. Il faut noter que la RAM et la ROM n'ont pas forcément la même taille. Et ce que ce soit sur une architecture Harvard que sur une architecture Von Neumann, mais c'est plus facile à expliquer sur une architecture Harvard. On peut par exemple imaginer une architecture Harvard qui utilise des adresses de 16 bits pour la ROM, et seulement 8 bits pour la RAM. Le résultat est qu'il peut adresser 64 kibioctets de ROM, mais seulement 256 octets de RAM. Les deux bus d'adresse sont alors de taille différente, l'un faisant 8 bits, l'autre 16. Quelques processeurs 8 bits étaient dans ce cas, comme on le verra dans le chapitre sur les CPU 8bits. Mais d'autres processeurs utilisent des valeurs différentes, avec par exemple des adresses de 16 bits pour la RAM, mais de 20 bits pour la ROM, ou inversement. Sur une architecture Von Neumann, tout dépend de comment les adresses sont réparties. La solution la plus simple découpe l'espace d'adressage en deux parties égales, avec la RAM qui est dans la moitié basse (qui part de l'adresse 0 jusqu'à l'adresse au milieu), alors que la ROM est dans la moitié haute (entre l'adresse du milieu et l'adresse maximale). Mais ce n'est pas la seule possibilité, la limite entre RAM et ROM peut être mise n'importe où. Prenons par exemple un processeur 32 bits, capable de gérer 4 milliards d'adresse. Il est parfaitement possible de réserver 128 mébioctets de poids fort à la mémoire ROM, et de laisser le reste à la mémoire RAM. ===Le décodage d'adresse sur les architectures Von Neumann=== Pour résumer, les architectures Harvard et Von Neumann se distinguent sur deux points : * L'accès à la RAM et à la ROM se font par des bus séparés sur l'architecture Harvard, sur le même bus avec l'architecture Von Neumann. * Les adresses pour la mémoire ROM et la mémoire RAM sont séparées sur les architectures Harvard, partagées sur l’architecture Von Neumann. Les architectures Von Neumann utilisent donc un seul bus pour connecter la RAM et la ROM au processeur. Mais cela ne parait pas intuitif : comment deux composants peuvent se connecter aux mêmes fils ? Parce que c'est ce qu'implique le fait de partager un bus. Si je prends une mémoire RAM et une mémoire ROM, toutes deux de 8 bits, elles seront connectées à un bus mémoire de 8 bits. Intuitivement, on se dit qu'il y aura des conflits, du genre : la RAM et la ROM vont accéder au bus en même temps, comment savoir si une adresse est destinée à la RAM ou la ROM, etc ? Tous ces problèmes sont résolus avec une solution très simple : à chaque instant, seule une mémoire est connectée au bus. L'idée est que les mémoires sont connectées ou déconnectées du bus selon les besoins. Si le processeur veut envoyer lire une donnée en mémoire RAM, il déconnecte la mémoire ROM du bus. Et inversement, s'il veut lire une instruction, il déconnecte la RAM et connecte la ROM. Pour cela, les mémoires RAM et ROM possèdent une entrée ''Chip Select'' ou ''Output Enable'', qui agit comme une sorte de bouton ON/OFF. Lorsqu'on met un 1 sur cette entrée, la mémoire se connectera au bus. Ses entrées et sorties fonctionneront normalement, elle pourra recevoir des adresses, envoyer ou recevoir des données, tout sera normal. Par contre, si on met un 0 sur cette entrée, la mémoire se "désactive", ses entrée-sorties ne répondent plus aux sollicitations extérieures. Pire que ça : elles sont électriquement déconnectées. Au total, tout cela demande de gérer deux bit ''Chip Select''/''Output Enable'' : un pour la RAM, un pour la ROM. Et ces deux bits sont configurés pour chaque accès mémoire, pour chaque lecture ou écriture. Pour cela, un circuit de '''décodage d'adresse''' prend en entrée l'adresse mémoire à lire/écrire, et active/désactive les mémoires RAM/ROM selon les besoins. Il prend l'adresse et configure les bits ''Chip Select''/''Output Enable''. [[File:Décodage d'adresse sur une architecture Von Neumann.png|centre|vignette|upright=2|Décodage d'adresse sur une architecture Von Neumann.]] L'implémentation la plus simple réserve la moitié des adresses pour la RAM, l'autre moitié pour la ROM. Typiquement, la ROM prend la moitié basse, la RAM la moitié haute. Dans ce cas, activer/désactiver la RAM et la ROM se fait avec seulement le bit de poids fort de l'adresse. Si le bit de poids fort est à 1, alors on accède à la RAM et la ROM doit être désactivée. Mais si ce bit est à 0, alors on accède à la moitié basse et il faut désactiver la RAM. Une remarque intéressante : le fait de séparer la mémoire en deux parts égales permet de simuler une architecture Harvard à partir d'une architecture Von Neumann. Par exemple, le tout premier processeur d'Intel, le 4004, était l'un de ceux là. La RAM et la ROM sont reliés au même bus, et il y a donc un unique espace d'adressage, qui est séparé en deux parties égales. Le truc est que le processeur traite les deux parties égales comme deux espaces d'adressage séparés. Le processeur se débrouille pour cacher le fait qu'il y a un espace d'adressage unique coupé en deux, ce qui fait que les programmeurs voient bien deux espaces d'adressages distincts. [[File:Décodage d'adresse sur une architecture Von Neumann basique.png|centre|vignette|upright=2|Décodage d'adresse sur une architecture Von Neumann basique.]] Pour résumer, quand une adresse est envoyée sur le bus, les deux mémoires vont la recevoir mais une seule va répondre et se connecter au bus. Le décodage d'adresse garantit que seule la mémoire adéquate réponde à un accès mémoire. Le décodage d'adresse est réalisé par la carte mère, par un composant dédié. Le mécanisme peut être utilisé pour combiner plusieurs RAM en une seule, idem avec les ROM. Pour comprendre l'idée, je vais prendre l'exemple de l'IBM PC, un des tout premier PC existant. Nous étudierons ce PC dans une section dédiée, à la fin du chapitre, aussi je vais passer rapidement dessus. Tout ce que je vais faire est vous présenter la carte mère du PC, et vous demander de faire est de compter les mémoires ROM et mémoires RAM sur la carte mère : [[File:IBM 5150 Motherboard.svg|centre|vignette|upright=3|Carte mère de l'IBM 5150, un modèle de l'IBM PC.]] Si vous remarquerez qu'il y a 5 mémoires ROM et 8 à 32 mémoires RAM. Le fait est que le processeur voit les différentes mémoires ROM comme une seule mémoire ROM. Idem avec les mémoires RAM : elle font chacune 2 kibioctets, et l'ensemble est vu par le processeur comme une seule RAM de 16 à 64 kibioctets. Et cela grâce aux circuits de décodage d'adresse, qui sont situés en haut à droite de la carte mère. Pour comprendre l'idée, prenons l'exemple d'un processeur 16 bits, capable de gérer 64 kibioctets de mémoire. L'espace d'adressage est découpé en quatre portions, de 16 kibioctets chacune. Une portion est réservée à une ROM de 16 kibioctet, les autres sont chacune réservée à une RAM de 16 kibioctet. Le décodage d'adresse sélectionne alors la mémoire adéquate en utilisant les deux bits de poids fort de l'adresse. * S'ils valent 00, alors c'est la mémoire ROM qui est activée, connectée au bus. * S'ils valent 01, alors c'est la première mémoire RAM qui est connectée au bus. * S'ils valent 10, alors c'est la seconde mémoire RAM qui est connectée au bus. * S'ils valent 11, alors c'est la troisième mémoire RAM qui est connectée au bus. [[File:Décodage d'adresse sur une architecture Von Neumann, utilisant plusieurs RAM et une ROM.png|centre|vignette|upright=3|Décodage d'adresse sur une architecture Von Neumann, utilisant plusieurs RAM et une ROM]] ===L'impact sur la conception du processeur=== Plus haut, j'ai parlé d'un des avantages des architectures Harvard : elles peuvent lire une instruction en même temps qu'elles accèdent à une donnée. La donnée est lue/écrite en RAM, alors que l'instruction est lue en ROM. Et cela permet de simplifier l'intérieur du processeur. Pas de beaucoup, mais c'est déjà ça de pris. Voyons maintenant comment cela impacte l'intérieur du processeur. Tout ce dont vous avez à vous rappeler est la séparation entre chemin de données et unité de contrôle, et que les registres généraux sont dans le premier, le ''program counter'' dans la seconde. Avec une architecture Harvard, les instructions et les données passent par des bus différent : bus ROM pour les instructions, bus RAM pour les données. L'intuition nous dit que le bus pour la mémoire ROM est connecté à l'unité de contrôle, alors que le bus pour la RAM est connecté au chemin de données. Et dans les grandes lignes, c'est vrai. La logique est imparable pour ce qui est des bus de données. Mais il y a une petite subtilité pour les bus d'adresse. Pour comprendre comment le processeur exploite ces deux bus, voyons ce qui transite dessus. Pour la mémoire ROM, elle reçoit l'adresse de l'instruction à lire, elle renvoie l'instruction adéquate. Pour cela, le ''program counter'' est envoyé sur le bus d'adresse, l'instruction sur le bus de données. Pour la mémoire RAM, elle échange des données avec les registres généraux, les registres pour les données. Les adresses utilisées pour la RAM viennent elles soit du chemin de données, soit de l'unité de contrôle, tout dépend du mode d'adressage. Mais le ''program counter'' n'est pas impliqué. [[File:Architecture Harvard - échanges de données.png|centre|vignette|upright=2|Architecture Harvard - échanges de données]] Les architectures Harvard modifiées doivent cependant rajouter une connexion entre le bus ROM et les registres généraux. C'est nécessaire pour charger une donnée constante depuis la mémoire ROM. Rappelons que la donnée constante est copiée dans un registre général, donc dans le chemin de données. [[File:Architecture Harvard modifiée - implémentation du processeur.png|centre|vignette|upright=2|Architecture Harvard modifiée - implémentation du processeur]] Avec les architectures Von Neumann, il y a un seul bus qui est relié à la fois au chemin de données et à l'unité de contrôle. Si le processeur lit une instruction, le bus doit être relié à l'unité de contrôle. Par contre, s'il accède à une donnée, il doit être relié au chemin de données (le bus d'adresse peut éventuellement être connecté au séquenceur, si celui-ci fournit l'adresse à lire). Il faut donc utiliser un paquet de multiplexeurs et de démultiplexeurs pour faire la connexion au bon endroit. [[File:Architecture Von Neumann - implémentation du processeur.png|centre|vignette|upright=2|Architecture Von Neumann - implémentation du processeur]] Une instruction se fait en deux temps : on charge l'instruction depuis la mémoire ROM, puis on l'exécute. Avec une architecture Harvard, tout cela se fait en un seul cycle d'horloge, vu que charger la ROM et accéder aux données peut se faire en même temps. Pas avec les architectures Von Neumann, qui doivent libérer le bus mémoire après avoir chargé une instruction. Elles n'ont pas le choix : elles chargent l'instruction lors d'un premier cycle d'horloge, puis l'exécutent lors du second. Pour cela, ils incorporent un registre appelé le '''registre d'instruction''', qui mémorise l'instruction chargée. L'instruction est copiée dans ce registre lors du premier cycle, puis est utilisée lors du second cycle. Le registre permet de ne pas oublier l’instruction entre les deux cycles. Le registre d'instruction est obligatoire sur les architectures Von Neumann. En comparaison, il est facultatif sur les architectures Harvard. Elles peuvent en avoir un, pour des raisons techniques, mais ce n'est pas obligatoire. [[File:Registre d'instruction.png|centre|vignette|upright=2|Registre d'instruction.]] ===Les architectures Von Neumann sont plus flexibles=== Sur les architectures Harvard, le processeur sait faire la distinction entre programme et données. Les données sont stockées dans la mémoire RAM, le programme est stocké dans la mémoire ROM. Les deux sont séparés, accédés par le processeur sur des bus séparés, et c'est ce qui permet de faire la différence entre les deux. Il est impossible que le processeur exécute des données ou modifie le programme. Du moins, tant que la mémoire qui stocke le programme est bien une ROM. Par contre, sur les architectures Von Neumann, il est impossible de distinguer programme et données, sauf en ajoutant des techniques de protection mémoire avancées. La raison est qu'il est impossible de faire la différence entre donnée et instruction, vu que rien ne ressemble plus à une suite de bits qu'une autre suite de bits. Et c'est à l'origine d'un des avantages majeur de l'architecture Von Neumann : il est possible que des programmes soient copiés dans la mémoire RWM et exécutés dans celle-ci. Un cas d'utilisation familier est celui de votre ordinateur personnel. Le système d'exploitation et les autres logiciels sont copiés en mémoire RAM à chaque fois que vous les lancez. Mais cet exemple implique un disque dur, ce qui rend les choses plus compliquées que prévu. Un autre exemple serait la compilation de code à la volée, mais il ne sera pas très parlant. Un exemple plus adapté serait celui où la ROM mémorise un programme compressée dans la mémoire ROM, qui est décompressé pour être exécuté en mémoire RAM. Le programme de décompression est stocké en mémoire ROM et est exécuté au lancement de l’ordinateur. Cette méthode permet d'utiliser une mémoire ROM très petite et très lente, tout en ayant un programme rapide (si la mémoire RWM est rapide). Il est aussi possible de créer des programmes qui modifient leurs propres instructions : cela s'appelle du '''code auto-modifiant'''. Ce genre de choses servait autrefois sur des ordinateurs rudimentaires, au tout début de l'informatique. A l'époque, les adresses à lire/écrire devaient être écrites en dur dans le programme, dans les instructions exécutées. Pour gérer certaines fonctionnalités des langages de programmation qui ont besoin d'adresses modifiables, comme les tableaux, on devait corriger les adresses au besoin avec du code auto-modifiant. De nos jours, le code automodifiant est utilisée occasionnellement pour rendre un programme indétectable dans la mémoire (les virus informatiques utilisent beaucoup ce genre de procédés). L'impossibilité de séparer données et instructions est à l'origine de problèmes assez fâcheux. Il est parfaitement possible que le processeur charge et exécute des données, qu'il prend par erreur pour des instructions. C'est le cas quand des pirates informatiques arrivent à exploiter des bugs. Il arrive que des pirates informatiques vous fournissent des données corrompues, qui contiennent un virus ou un programme malveillant est caché dans les données. Les bugs en question permettent d'exécuter ces données, donc virus. Pour éviter cela, le système d'exploitation peut marquer certaines zones de la mémoire comme non-exécutable, c’est-à-dire que le système d'exploitation interdit d’exécution de quoi que ce soit qui est dans cette zone. Mais ce n'est pas parfait. Toujours est-il que tout cela est impossible sur les architectures Harvard. Et ce serait très limitant. Imaginez : pas possible de lancer un programme depuis le disque dur ou une clé USB, le programme doit impérativement être dans une mémoire ROM, pas de compilation à la volée, etc. Que des techniques très utilisées dans l'informatique moderne. Malgré ses défauts, les architectures Von Neumann ne sont pas les plus utilisées pour rien. Les architectures Harvard sont concrètement utilisées uniquement dans l'informatique embarquée, sur des microcontrôleurs très spécifiques. ==Le bus de communication avec les entrées-sorties== Le processeur, la mémoire et les entrées-sorties sont connectées par un ou plusieurs '''bus de communication'''. Ce bus n'est rien d'autre qu'un ensemble de fils électriques sur lesquels on envoie des zéros ou des uns. Pour communiquer avec la mémoire, il y a trois prérequis qu'un bus doit respecter : pouvoir sélectionner la case mémoire (ou l'entrée-sortie) dont on a besoin, préciser à la mémoire s'il s'agit d'une lecture ou d'une écriture, et enfin pouvoir transférer la donnée. Pour cela, on doit donc avoir trois bus spécialisés, bien distincts, qu'on nommera le bus de commande, le bus d'adresse, et le bus de donnée. * Le '''bus de données''', sur lequel s'échangent les données entre les composants. * Le '''bus de commande''' pour configurer la mémoire et les entrées-sorties. * Le '''bus d'adresse''', facultatif, permet de préciser quelle adresse mémoire il faut lire/écrire. Chaque composant possède des entrées séparées pour le bus d'adresse, le bus de commande et le bus de données. Par exemple, une mémoire RAM possédera des entrées sur lesquelles brancher le bus d'adresse, d'autres sur lesquelles brancher le bus de commande, et des broches d'entrée-sortie pour le bus de données. Précisons cependant que le bus de commande n'est pas exactement le même entre des mémoires RAM/ROM et des entrées-sorties. [[File:Bus general schematic.svg|centre|vignette|upright=2|Contenu d'un bus, généralités.]] ===Le réseau d'interconnexion : généralités=== Reprenons où nous nous étions arrêté. Avant de voir les architectures Harvard et Von Neumann, nous avions dit que le processeur, les mémoires et les entrées-sorties sont reliées entre eux par un réseau d'interconnexion. Nous venons de voir qu'il est possible de mutualiser certains bus, notamment celui de la mémoire RAM et celui de la mémoire ROM. Mais il est possible de faire la même chose pour les entrées-sorties. Là encore, il est possible de regrouper le bus mémoire avec les bus pour les entrées-sorties. Voyons ce que cela implique. {| |[[File:Réseau d'interconnexion avec un processeur au centre.png|centre|vignette|upright=2|Réseau d'interconnexion avec une architecture Harvard.]] |[[File:Réseau d'interconnexion avec un processeur au centre et une architecture Harvard.png|centre|vignette|upright=2|Interconnexions d'une architecture Von Neumann.]] |} Avant de poursuivre, nous devons préciser quelque chose d'important. Sur les ordinateurs modernes, les entrées-sorties peuvent accéder à la mémoire RAM. Les ordinateurs modernes intègrent des techniques de '''''Direct Memory Access''''' (DMA) qui permettent aux entrées-sorties de lire ou d'écrire en mémoire RAM. Les transferts DMA se font sans intervention du processeur. Ils permettent de copier un bloc de plusieurs octets, dans deux sens : de la mémoire RAM vers une entrée-sortie, ou inversement. Le DMA demande d'ajouter un circuit dédié sur la carte mère : le contrôleur DMA. Il effectue la copie d'un paquet d'octets de la RAM vers l'entrée-sortie ou dans l'autre sens. [[File:Réseau d'interconnexion avec un processeur au centre, et direct memory access.png|centre|vignette|upright=2|Réseau d'interconnexion avec un processeur au centre, et direct memory access]] ===Les bus systèmes=== La première solution utilise un bus unique, celui-ci est appelé le '''bus système''', aussi appelé ''backplane bus''. Le bus système est connecté à la mémoire RAM, la mémoire ROM, au processeur, et aux entrées-sorties. Tous les composants présents dans l'ordinateur sont connectés à ce bus, sans exception. De tels bus avaient pour avantage la simplicité. Le processeur n'est connecté qu'à un seul bus, ce qui utilise peu de broches et économise des fils. La mutualisation des bus est totale, le câblage est plus simple, la fabrication aussi. [[File:Architecture minimale d'un ordinateur.png|centre|vignette|upright=2|Architecture minimale d'un ordinateur.]] Un bus système contient un bus d'adresse, de données et de commande. Un bus système se marie bien avec des entrées-sorties mappées en mémoire. La conséquence est que le bus d'adresse ne sert pas que pour l'accès à la mémoire RAM/ROM, mais aussi pour l'accès aux entrées-sorties. Il y a moyen d'implémenter un système d'adresse séparés avec, mais c'est pas l'idéal. [[File:Architecture Von Neumann avec les bus.png|centre|vignette|upright=2|Architecture Von Neumann avec les bus.]] Un bus système n'a pas de limitations quant aux échanges de données. Le processeur peut communiquer directement avec les mémoires et les entrées-sorties, les entrées-sorties peuvent communiquer avec la mémoire RAM, etc. Notamment, un bus système peut implémenter le ''Direct Memory Access''. Il suffit juste de connecter un contrôleur DMA sur le bus système. Le contrôleur DMA est considéré comme une entrée-sortie, ses registres sont mappés en mémoire et sont donc accessibles directement par le processeur. [[File:Bus système avec controleur DMA.png|centre|vignette|upright=2|Bus système avec contrôleur DMA.]] Si on suit la définition à la lettre, un bus système est systématiquement une architecture Von Neumann, vu que la mémoire ROM et la mémoire RAM sont reliées sur le bus système. La conséquence est que les circuits de décodage d'adresse sont présents. Ils sont toujours sur la carte mère, et sont plus ou moins à côté du bus système. Cependant, le décodage d'adresse est parfois étendu pour tenir compte des entrées-sorties. Les entrées-sorties soudées sur la carte mère ont elles aussi des entrées ''Chip Select'' ou quelque chose de similaire. Le décodage d'adresse peut alors les activer ou les désactiver suivant l'adresse envoyée sur le bus d'adresse. C'est ce qui arrive quand le processeur écrit dans un registre d’interfaçage : il envoie l'adresse de ce registre sur le bus d'adresse, le circuit de décodage d'adresse active seulement l'entrée-sortie associée. Il faut noter que ce n'est pas systématique, il existe des techniques pour se passer de décodage d'adresse. Mais nous en reparlerons dans le chapitre sur les bus de communication. [[File:Chipselectfr.png|centre|vignette|upright=1.5|Exemple détaillé.]] Les bus systèmes sont certes très simples, mais ils ont aussi des désavantages. Par exemple, il faut éviter que le processeur et les entrées-sorties se marchent sur les pieds, ils ne peuvent pas utiliser le bus en même temps. De tels conflits d'accès au bus système sont fréquents et ils réduisent la performance, comme on le verra dans le chapitre sur les bus. De plus, un bus système a le fâcheux désavantage de relier des composants allant à des vitesses très différentes : il arrivait fréquemment qu'un composant rapide doive attendre qu'un composant lent libère le bus. Le processeur était le composant le plus touché par ces temps d'attente. Elle était utilisée sur les tout premiers ordinateurs, pour sa simplicité. Elle était parfaitement adaptée aux anciens composants, qui allaient tous à la même vitesse. De nos jours, les ordinateurs à haute performance ne l'utilisent plus trop, mais elle est encore utilisée sur certains systèmes embarqués, en informatique industrielle dans des systèmes très peu puissants. ===Les bus d'entrées-sorties=== Les bus systèmes ont de nombreux problèmes, ce qui fait que d'anciens ordinateurs faisaient autrement. A la place d'un bus système unique, ils utilisent un bus séparé pour les mémoires, et un autre séparé pour les entrées-sorties. Le bus spécialisé pour la mémoire est appelé le '''bus mémoire''', l'autre bus est appelé le '''bus d'entrées-sorties'''. Le bus mémoire est généralement relié à la fois à la mémoire RAM et à la mémoire ROM, les exceptions ne sont pas rares, cependant. [[File:Bus mémoire séparé du bus pour les IO.png|centre|vignette|upright=2|Bus mémoire séparé du bus pour les IO]] Les bus d'entrée-sorties peuvent être spécialisés et simplifiés. Par exemple, ils peuvent avoir un bus de commande différent de celui de la mémoire, qui utilise nettement moins de fils. Le bus d'adresse peut aussi être réduit, et utiliser des adresses plus courtes que celles du bus mémoire. Les bus de données peuvent aussi être de taille différentes. Il est ainsi possible d'avoir un bus mémoire capable de lire/écrire 64 bits à la fois, alors que la communication avec les entrées-sorties se fait octet par octet ! En général, les bus d'entrée-sortie sont assez petits, ils ont une taille de 8 ou 16 bits, même si le bus mémoire est plus grand. Cela permet de ne pas gaspiller trop de broches. Ajouter un bus d'entrée-sortie n'est donc pas très gourmand en broches et en fils. : Il est en théorie possible d'avoir une fréquence différente pour les deux bus, avec un bus mémoire ultra-rapide et un bus pour les entrées-sorties est un bus moins rapide. Mais il faut que le processeur soit prévu pour, et c'est très rare. Niveau performances, le processeur peut théoriquement accéder à la mémoire en attendant qu'une entrée/sortie réponde, mais il faut que le processeur soit prévu pour, et ce n'est pas de la tarte. Par contre, cela implique d'avoir des adresses séparées pour les registres d’interfaçage et la mémoire. En clair : pas d'entrée-sortie mappée en mémoire ! Un autre problème est que les entrées-sorties ne peuvent pas communiquer avec la mémoire directement, elles doivent passer par l'intermédiaire du processeur. En clair : pas de ''Direct Memory Access'' ! Les deux sont des défauts rédhibitoires pour les programmeurs système, notamment pour ceux qui codent les pilotes de périphériques. Pour résumer, les défauts sont assez problématiques : pas d'entrées-sorties mappées en mémoire, pas de ''Direct Memory Access'', économie de broches limitée. Les deux premiers sont des défauts majeurs, qui font que de tels bus ne sont pas utilisés dans les ordinateurs modernes. A la place, ils utilisent une troisième solution, distincte des bus systèmes et des bus d'entrée-sorties. ===Les bus avec répartiteur=== Il existe une méthode intermédiaire, qui garde deux bus séparés pour la mémoire et les entrées-sorties, mais élimine les problèmes de brochage sur le processeur. L'idée est d'intercaler, entre le processeur et les deux bus, un '''circuit répartiteur'''. Il récupère tous les accès et distribue ceux-ci soit sur le bus mémoire, soit sur le bus des périphériques. Le ou les répartiteurs s'appellent aussi le '''''chipset''''' de la carte mère. C'était ce qui était fait à l'époque des premiers Pentium. À l'époque, la puce de gestion du bus PCI faisait office de répartiteur. Elle mémorisait des plages mémoires entières, certaines étant attribuées à la RAM, les autres aux périphériques mappés en mémoire. Elles utilisaient ces plages pour faire la répartition. [[File:IO mappées en mémoire avec séparation des bus.png|centre|vignette|upright=2|IO mappées en mémoire avec séparation des bus]] Niveau adresses des registres d'interfacage, il est possible d'avoir soit des adresses unifiées avec les adresses mémoire, soit des adresses séparées. L'usage d'un répartiteur ne pose pas de problèmes particuliers pour implémenter le DMA. La seule contrainte est que le contrôleur DMA soit intégré dans le répartiteur. Les échanges entre IO et mémoire passent par le répartiteur, qui fait le pont entre bus mémoire et bus des IO. [[File:Implémentation du DMA avec un répartiteur.png|centre|vignette|upright=2|Implémentation du DMA avec un répartiteur]] ==Les microcontrôleurs et ''system on chip''== Parfois, on décide de regrouper la mémoire, les bus, le CPU et les ports d'entrée-sortie dans un seul circuit intégré, un seul boitier. L'ensemble forme alors ce qu'on appelle un '''''System on Chip''''' (système sur une puce), abrévié en SoC. Le nom est assez explicite : un SoC comprend un système informatique complet sur une seule puce de silicium, microprocesseurs, mémoires et périphériques inclus. Ils incorporent aussi des ''timers'', des compteurs, et autres circuits très utiles. [[File:ARMSoCBlockDiagram.svg|centre|vignette|upright=2|SoC basé sur un processeur ARM, avec des entrées-sorties typiques de celles d'un µ-contrôleur. Le support du bus CAN, d'Ethernet, du bus SPI, d'un circuit de PWM (génération de signaux spécifiques), de convertisseurs analogique-digital et inverse, sont typiques des µ-contrôleurs.]] Le terme SoC regroupe des circuits imprimés assez variés, aux usages foncièrement différents et à la conception distincte. Les plus simples d’entre eux sont des microcontrôleurs, qui sont utilisés pour des applications à base performance. Les plus complexes sont utilisés pour des applications qui demandent plus de puissance, nous les appellerons SoC haute performance. La relation entre SoC et microcontrôleurs est assez compliquée à expliquer, la terminologie n'est pas clairement établie. Il existe quelques cours/livres qui séparent les deux, d'autres qui pensent que les deux sont très liés. Dans ce cours, nous allons partir du principe que tous les systèmes qui regroupent processeur, mémoire et quelques périphériques/entrées-sorties sont des SoC. Les microcontrôleurs sont donc un cas particulier de SoC, en suivant cette définition. ===Les microcontrôleurs=== Les '''microcontrôleurs''' sont des composants utilisés dans l'embarqué ou d'informatique industrielle. Leur nom trahit leur rôle. Ils sont utilisés pour contrôler de l'électroménager, des chaines de fabrication dans une usine, des applications robotiques, les alarmes domestiques, les voitures. De manière générale, on les trouve dans tous les systèmes dits embarqués et/ou temps réel. Ils ont besoin de s'interconnecter à un grand nombre de composants et intègrent pour cela un grand nombre d'entrée-sorties. Les microcontrôleurs sont généralement peu puissants, et doivent consommer peu d'énergie/électricité. [[File:Microcontroller 8051.gif|centre|vignette|upright=2.5|Microcontrôleur Intel 8051.]] Un microcontrôleur tend à intégrer des entrées-sorties assez spécifiques, qu'on ne retrouve pas dans les SoC destinés au grand public. Un microcontrôleur est typiquement relié à un paquet de senseurs et son rôle est de commander des moteurs ou d'autres composants. Et les entrées-sorties intégrées sont adaptées à cette tâche. Par exemple, ils tendent à intégrer de nombreux convertisseurs numériques-analogiques pour gérer des senseurs. Ils intègrent aussi des circuits de génération de signaux PWM spécialisés pour commander des moteurs, le processeur peut gérer des calculs trigonométriques (utiles pour commander la rotation d'un moteur), etc. Fait amusant, on en trouve dans certains périphériques informatiques. Par exemple, les anciens disques durs intégraient un microcontrôleur qui contrôlait plusieurs moteurs/ Les moteurs pour faire tourner les plateaux magnétiques et les moteurs pour déplacer les têtes de lecture/écriture étaient commandés par ce microcontrôleur. Comme autre exemple, les claviers d'ordinateurs intègrent un microcontrôleur connecté aux touches, qui détecte quand les touches sont appuyées et qui communique avec l'ordinateur. Nous détaillerons ces deux exemples dans les chapitres dédiés aux périphériques et aux disques durs, tout deviendra plus clair à ce moment là. La majorité des périphériques ou des composants internes à un ordinateur contiennent des microcontrôleurs. ===Les SoC haute performance=== Les SoC les plus performants sont actuellement utilisés dans les téléphones mobiles, tablettes, ''Netbook'', ''smartphones'', ou tout appareil informatique grand public qui ne doit pas prendre beaucoup de place. La petite taille de ces appareils fait qu'ils gagnent à regrouper toute leur électronique dans un circuit imprimé unique. Mais les contraintes font qu'ils doivent être assez puissants. Ils incorporent des processeurs assez puissants, surtout ceux des ''smartphones''. C'est absolument nécessaire pour faire tourner le système d'exploitation du téléphone et les applications installées dessus. Niveau entrées-sorties, ils incorporent souvent des interfaces WIFI et cellulaires (4G/5G), des ports USB, des ports audio, et même des cartes graphiques pour les plus puissants d'entre eux. Les SoC incorporent des cartes graphiques pour gérer tout ce qui a trait à l'écran LCD/OLED, mais aussi pour gérer la caméra, voire le visionnage de vidéo (avec des décodeurs/encodeurs matériel). Par exemple, les SoC Tegra de NVIDIA incorporent une carte graphique, avec des interfaces HDMI et VGA, avec des décodeurs vidéo matériel H.264 & VC-1 gérant le 720p. Pour résumer, les périphériques sont adaptés à leur utilisation et sont donc foncièrement différents de ceux des microcontrôleurs. [[File:Phone hardware.png|centre|vignette|upright=2|Hardware d'un téléphone. On voit qu'il est centré autour d'un SoC, complété par de la RAM, un disque dur de faible capacité, de quoi gérer les entrées utilisateurs (l'écran tactile, les boutons), et un modem pour les émissions téléphoniques/2G/3G/4G/5G.]] Un point important est que les processeurs d'un SoC haute performance sont... performants. Ils sont le plus souvent des processeurs de marque ARM, qui sont différents de ceux utilisés dans les PC fixe/portables grand public qui sont eux de type x86. Nous verrons dans quelques chapitres en quoi consistent ces différences, quand nous parlerons des jeux d'instruction du processeur. Autrefois réservé au monde des PCs, les processeurs multicœurs deviennent de plus en plus fréquents pour les SoC de haute performance. Il n'est pas rare qu'un SoC incorpore plusieurs cœurs. Il arrive même qu'ils soient foncièrement différents, avec plusieurs cœurs d'architecture différente. La frontière entre SoC haute performance et microcontrôleur est de plus en plus floue. De nombreux appareils du quotidien intègrent des SoC haute performance, d'autres des microcontrôleurs. Par exemple, les lecteurs CD/DVD/BR et certains trackers GPS intègrent un SoC ou des processeurs dont la performance est assez pêchue. À l'opposé, les systèmes domotiques intègrent souvent des microcontrôleurs simples. Malgré tout, les deux cas d'utilisation font que le SoC/microcontrôleur est connecté à un grand nombre d'entrées-sorties très divers, comme des capteurs, des écrans, des LEDs, etc. [[File:GPS tracker Hardware Architecture.png|centre|vignette|upright=2|Hardware d'un tracker GPS.]] ==Étude de quelques exemples d'architectures== Après avoir vu la théorie, nous allons voir des exemples réels d'ordinateurs. Dans ce qui suit, nous allons voir des ordinateurs assez anciens, pour une raison simple : ils collent assez bien à l''''architecture de base''' vue plus haut, avec un CPU, une RAM et une ROM, quelques entrées-sorties. Tous les ordinateurs modernes, mais aussi dans les smartphones, les consoles de jeu et autres, utilisent une architecture grandement modifiée et améliorée, avec un grand nombre de périphériques, disques durs/SSD, un grand nombre de mémoires différentes, etc. Il pourrait sembler pertinent d’étudier des microcontrôleurs ou des ''System On Chip'', en premier lieu. Mais nous éviterons soigneusement de tels systèmes pour le moment. La raison est qu'ils ont un grand nombre d'entrées-sorties, qui sont peu familières. Attendez-vous à avoir près d'une vingtaine ou centaine d'entrée-sorties différentes pour de tels systèmes. Le tout est très complexe, bien trop pour un premier exemple. A la place, nous allons voir précisément des exemples plus simples : les premiers PC, et des consoles de jeu 8 et 16 bits. Bien que ce soit des systèmes très simples, ils sont cependant plus complexes que l'architecture de base. Et leur avantages/désavantages sont un peu inverse l'un de l'autre. Si on devait résumer les différences, on aurait ceci : * Les PC ont plus d'entrées-sorties que les consoles, bien que nettement moins que pour les microcontrôleurs/SoC. * Les PC utilisent des disques durs, les consoles font avec soit des cartouches de jeu, soit des CD/DVD. * Les PC utilisent des cartes électroniques séparées pour le son et l'écran, les consoles utilisent des circuits soudés sur la carte mère, qui sont souvent des co-processeurs. * Les PC ont une mémoire ROM soudées sur la carte mère, les consoles 8 bits font sans. Les PC et micro-ordinateurs ont plus d'entrées-sorties que les consoles. Même si on mets de côté les périphériques, ils ont aussi beaucoup de composants soudées sur la carte mère. En comparaison, les consoles de jeu 8/16 bits se débrouillent avec : une cartouche de jeu et une manette en entrée, une sortie vidéo et une sortie son. Un autre point important est l'absence de disque dur ou de lecteur CD. La présence d'un disque dur ou d'un lecteur CD/DVD complexifie tout de suite l'architecture des PC. Il faut leur réserver un bus dédié ou les connecter à un bus système, cela demande d'ajouter des circuits sur la carte mère, etc. Et surtout, il faut expliquer comment l'ordinateur exécute des programmes, ce qui demande de parler de l'interaction avec le disque dur et la ROM du BIOS. Rien de tout cela sur les consoles de jeu 8 et 16 bits. Elles utilisent à la place des cartouches de jeu, qui intègrent une mémoire ROM, pour mémoriser les données du jeu, voire son code. Pas besoin de parler des mémoires de stockage, on est beaucoup plus proche de l'architecture de base avec une ROM unique. Par contre, n'allez pas croire que tout est rose avec les consoles 8/16 bits. Il y a quelques différences qui font qu'elles sont plus complexes qu'un PC sur certains points. Les PC utilisent des cartes électroniques à brancher sur la carte mère pour alimenter l'écran et les hauts-parleurs/casques, alors que les consoles de jeu utilisent des souvent co-processeurs dédiés pour le son et les graphismes. La différence parait mineure, mais elle avantage les consoles. Nous avons déjà expliqué ce que sont les co-processeurs plus haut, aussi les co-processeurs des consoles nous paraitrons familiers. On n'a pas à s’embêter à expliquer ce que sont les cartes d'extension, les bus associés et tout ce qui va avec, cela peut être retardé pour la section sur l'architecture des PC. La gestion de la cartouche de jeu est aussi un peu subtile à comprendre, bien que ce soit bien plus simple à comprendre qu'un système avec un disque dur. Les cartouches de jeu intègrent une mémoire ROM, pour mémoriser les données du jeu, voire son code. Et le processeur doit exécuter le code depuis cette mémoire ROM. La conséquence est que les consoles 8/16 bits utilisent une architecture Harvard, avec un bus relié à la cartouche pour lire les instructions. Mais si ce n'était que ça... Les cartouches mémorisent aussi les données pour les graphismes, ce qui fait que le co-processeur vidéo doit lui aussi lire la cartouche pour récupérer ces données... ===L'architecture de la TurboGraphX-16=== La console PC Engine, aussi appelée TurboGraphX, est une ancienne console 8 bits. Elle contient un processeur 65C02, 8 kibioctets de RAM, un port manettes, une carte son et une carte vidéo. La '''carte son''' est le composant qui s'occupe de commander les haut-parleurs et de gérer tout ce qui a rapport au son. La '''carte graphique''' est le composant qui est en charge de calculer les graphismes, tout ce qui s'affiche à l'écran. Sur cette console, les cartes son et graphique ne sont PAS des co-processeurs, ce sont des circuits électroniques dits fixes. C'est totalement différent de ce qu'on a sur les consoles modernes, aussi le préciser est important. Bien que la carte graphique ne soit pas un processeur, elle a 64 kibioctets de RAM rien que pour elle. La RAM en question est séparée de la RAM normale, c'est un circuit intégré séparé. Et c'est un cas très fréquent, qui reviendra par la suite. La majeure partie des cartes graphiques dispose de leur propre '''mémoire vidéo''', totalement réservée à la carte graphique. La RAM vidéo est connectée à la carte graphique via un bus séparé. Le processeur est souvent connecté à ce bus, afin de pouvoir écrire des données dedans, mais ce n'est pas le cas ici. [[File:Architecture de la PC Engine, aussi appelée TurboGrafx-16.png|centre|vignette|upright=2.5|Architecture de la PC Engine, aussi appelée TurboGrafx-16]] L'architecture de la console était particulièrement simple. Le processeur était le centre de l'architecture, tout était connecté dessus. Il y a un bus pour la cartouche de jeu, un autre pour la RAM, un autre pour les manettes, un autre pour carte son, et un dernier pour la carte graphique. Le fait d'avoir un bus par composant est assez rare et ce n'est le cas ici que parce des conditions particulières sont remplies. Déjà, il y a peu d'entrée-sorties. Ensuite, les bus font tous 8 bits, vu que le processeur est un CPU 8 bits. Avec 5 connexions de 8 bits, le tout utilise 40 broches, ce qui est beaucoup, mais totalement gérable. Par contre, les choses changerons pour les autres consoles. Au final, l'organisation des bus peut s'expliquer avec ce qu'on a vu dans la section sur les bus de communication. La console utilise une architecture Harvard, car la ROM et la RAM utilisent des bus différents. De plus, il y a des bus dédiés aux entrées-sorties, séparés des bus mémoire. Enfin, la carte graphique a droit à ses propres bus pour lire dans la cartouche et dans sa RAM vidéo dédiée. ===L'architecture de la console de jeu NES=== Maintenant, nous allons voir la console de Jeu Famicom, aussi appelée la NES en occident. Elle a une architecture centrée sur un processeur Ricoh 2A03, similaire au processeur 6502, un ancien processeur autrefois très utilisé et très populaire. Le processeur est associé à 2 KB de mémoire RAM. Sur certaines cartouches, on trouve une RAM utilisée pour les sauvegardes, qui est adressée par le processeur directement. Première variation par rapport à l'architecture de la console précédente : l'ajout de la RAM pour les sauvegardes dans les cartouches. Niveau carte graphique, une différence importante est que la carte graphique est connectée à la cartouche de jeu via un autre bus, afin de pouvoir lire les sprites et textures du jeu dans la cartouche. [[File:Architecture de la NES.png|centre|vignette|upright=2.5|Architecture de la NES]] La différence avec l'architecture précédente est que des bus ont été fusionnés. Comme dit plus haut, le système utilise une architecture Harvard, vu que la ROM est dans la cartouche, alors que la RAM est soudée à la carte mère. Par contre, la Famicon utilise un bus dédié aux entrées-sorties. Il est utilisé pour la carte son et la carte graphique, seules les manettes sont sur un bus à part. Ce qui fait qu'on devrait plutôt parler de bus de sorties, mais passons... L'essentiel est qu'on n'est plus tout à fait dans le cas de la console précédente, avec un bus par composant. ===L'architecture de la SNES=== L'architecture de la SNES est illustrée ci-dessous. Les changements pour le processeur et la RAM sont mineurs.La RAM a augmenté en taille et passe à 128 KB. Pareil pour la RAM de la carte vidéo, qui passe à 64 KB. Par contre, on remarque un changement complet au niveau des bus, de la carte graphique et de la carte son. [[File:Architecture de la SNES.png|centre|vignette|upright=2|Architecture de la SNES]] La console utilise un '''bus système unique''', sur lequel tout est connecté : ROM, RAM, entrées-sorties, etc. La seule exception est pour les manettes, qui sont encore connectées directement sur le processeur, via un bus séparé. La transition vers un bus système s'explique par le fait que la console est maintenant de 16 bits, ce qui fait que les bus doivent être plus larges. Le processeur adresse des mémoires RAM et ROM plus grandes, ce qui double la taille de leurs bus. De plus, les entrées-sorties aussi ont besoin d'un bus plus large. Le processeur n'ayant pas un nombre illimité de broches, la seule solution est de fusionner les bus en un seul bus système. Un autre changement est que la carte graphique est maintenant composée de deux circuits séparés. Encore une fois, il ne s'agit pas de coprocesseurs, mais de circuits non-programmables. Par contre, la carte son est remplacée par deux coprocesseurs audio ! De plus, les deux processeurs sont connectés à une mémoire RAM dédiée de 64 KB, comme pour la carte graphique. L'un est un processeur 8 bits (le DSP), l'autre est un processeur 16 bits. Un point très intéressant : certains jeux intégraient des coprocesseurs dans leurs cartouches de jeu ! Par exemple, les cartouches de Starfox et de Super Mario 2 contenait un coprocesseur Super FX, qui gérait des calculs de rendu 2D/3D. Le Cx4 faisait plus ou moins la même chose, il était spécialisé dans les calculs trigonométriques, et diverses opérations de rendu 2D/3D. En tout, il y a environ 16 coprocesseurs d'utiliser et on en trouve facilement la liste sur le net. La console était conçue pour, des pins sur les ports cartouches étaient prévues pour des fonctionnalités de cartouche annexes, dont ces coprocesseurs. Ces pins connectaient le coprocesseur au bus des entrées-sorties. Les coprocesseurs des cartouches de NES avaient souvent de la mémoire rien que pour eux, qui était intégrée dans la cartouche. ===L'architecture de la Megadrive et de la néo-géo=== Passons maintenant à la console de jeu Megadrive, une console 16 bits. Elle a une architecture similaire à celle de la néo-géo, une autre console bien plus puissante, sorti à peu près en même temps. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le Z80 et le Motorola 68000 étaient deux processeurs très populaires à l'époque. Le Z80 est une sorte de version améliorée de l'Intel 8088 utilisé sur les anciens PC et de nombreuses consoles utilisaient des Z80 comme processeur principal. Il était familier pour les programmeurs de l'époque, pour son cout réduit, sa bonne disponibilité, et bien d'autres avantages liés à sa production de masse. Le Z80 est utilisé comme co-processeur audio. Il commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Le Motorola 68000 était un processeur 16 bits, alors que le Z80 est un processeur 8 bits. Et cette différence fait que l'on ne peut pas connecter directement les deux sur le même bus, ou du moins pas facilement. La solution retenue est d'utiliser deux bus séparés : un bus de 16 bits connecté au 68000, un bus de 8 bits connecté au Z80. Le premier bus est un bus système sur lequel est connecté le 68000, 64 kibioctets de RAM, la cartouche de jeu, et la carte graphique. Le second bus est un bus de 8 bits, plus court, relié au Z80, à un synthétiseur sonore, et 8 kibioctets de RAM Les deux bus sont connectés à un '''''chipset''''', un circuit répartiteur, qui fait le pont entre les deux bus. Les manettes sont connectées sur le ''chipset''. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'un programme pré-déterminé sur le Z80. : Pour ceux qui savent ce qu'est une interruption, les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. [[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]] Cet exemple nous montre que les bus systèmes sont certes très simples, mais aussi inflexibles. Ils fonctionnent bien quand les composants branchés dessus sont tous des composants 8 bits, ou sont tous de 16 bits, ou tous 32 bits. Mais dès qu'on mélange composants 8, 16, 32 ou 64 bits, les choses deviennent plus compliquées. Il est alors préférable d'utiliser des bus séparés, avec des répartiteurs pour faire le pont entre les différents bus. Et nous verrons que le problème s'est posé lui aussi sur les PC. ===L'architecture des anciennes consoles Playstation : beaucoup de co-processeurs=== Les consoles que nous venons d'aborder étaient des consoles 8 ou 16 bits. A partir des consoles 32 bits, leur architecture s'est rapprochée de celle des PC, avec un usage plus complexes de répartiteurs. La XBOX était très semblable à un PC : le processeur était un Pentium 3 modifié, la carte graphique était une Geforce 3 modifiée, les 64 mébioctets de RAM était la même mémoire DDR que celle des PC, le répartiteur secondaire était un ''chipset'' nForce de NVIDIA, etc. Mais les Playstation 1, 2 et 3 se distinguent de leur contemporains. Elles disposent de très nombreux co-processeurs, qui sont en plus très variés. La Playstation 1 a été une des premières console à utiliser les CD-ROM comme support de stockage, en remplacement des cartouches. La conséquence est que la console contient une mémoire ROM, soudée à la carte mère, de 512 kibioctets. Elle contient aussi 2 mébioctets de RAM, une carte graphique avec 1 mébioctet de mémoire vidéo, un processeur, et de quoi gérer les périphériques. Il y a un co-processeur audio spécialisé, avec 512 kibioctets de RAM, ce qui nous est familier. Par contre, les autres co-processeurs ne le sont pas. Déjà, le lecteur de CD-ROM est associé à des circuits sur la carte mère, il y a tout un sous-système dédié au lecteur de CD. Il y a un contrôleur qui sert d'interface avec le lecteur proprement dit, mais aussi deux co-processeurs audio et 32 kibioctets de RAM. Les co-processeurs audio servent à lire des CD sans trop utiliser le second co-processeur audio, ils lui servent de complément. Ensuite, le processeur incorpore plusieurs cœurs, avec un cœur principal et plusieurs co-processeurs. Le premier est un co-processeur système, qui est utilisé pour gérer la mémoire cache intégrée au processeur, pour des fonctionnalités appelées interruptions et exceptions, ainsi que pour configurer le processeur. Le second est un co-processeur arithmétique spécialisé dans les calculs en virgule flottante, très importants pour le rendu 3D. Enfin, il y a un décodeur vidéo, qui n'est pas un co-processeur, mais un circuit non-programmable, spécialisé dans le décodage vidéo. De nos jours, ce circuit aurait été intégré dans la carte graphique, mais il était intégré dans le processeur sur la Playstation 2. Pour le reste, le processeur est la figure centrale de la console. Il est connecté à 4 bus : un pour la RAM, un pour la carte graphique, un pour les manettes, un autre pour le reste. Le dernier bus est connecté au système audio et au système pour le lecteur CD. Ce serait un bus d'entrée-sortie, s'il n'était pas connecté à la mémoire ROM. Vous avez bien lu : la mémoire ROM est reliée au bus d'entrée-sortie. [[File:Architecture de la Playstation.png|centre|vignette|upright=2.5|Architecture de la Playstation]] La Playstation 2 est composé d'un processeur, couplé à 32 Mébioctets de RAM, et d'un paquet de co-processeurs. Plus de co-processeurs que la PS1. Le processeur principal n'est pas la même que celui de la PS1, mais il a une architecture similaire. Il intègre un décodeur vidéo sur le même circuit intégré, ainsi que deux co-processeur. Les co-processeurs ne sont cependant pas les mêmes. Le co-processeur système disparait et est remplacé par un second co-processeur arithmétique. Les deux co-processeurs arithmétiques sont spécialisés dans les nombres flottants, avec quelques différences entre les deux. Par exemple, le second co-processeur gérait des calculs trigonométriques, des exponentielles, des logarithmes, et d'autres fonctions complexes du genre ; mais pas le premier co-processeur. Ils sont reliés à 4 kibioctets de RAM pour le premier, 16 kibioctets de RAM pour le second ; qui sont intégrées dans le processeur et non-représentés dans le diagramme ci-dessous. La PS2 intègre aussi un co-processeur d'entrées-sorties. Pour information, il s'agit du processeur principal de la Playstation 1, qui est ici utilisé différemment, suivant que l'on place un jeu PS1 ou PS1 dans la console. Si on met un jeu PS1, il est utilisé pour émuler la Playstation 1, afin de faire tourner le jeu PS1 sur la PS2. Si on met un jeu PS2, il est utilisé comme co-processeur d'entrée-sortie et fait l'interface entre CPU et entrées-sorties. Il est relié à 2 mébioctets de RAM, soit exactement la même quantité de mémoire que la Playstation 1. Tous les périphériques sont connectés au co-processeur d'entrées-sortie. Pour cela, le co-processeur d'entrées-sortie est relié à deux bus dédiés aux périphériques. Le premier bus est relié aux manettes, aux ports USB et aux ports pour cartes mémoires. Le second bus est relié à la carte son, la carte réseau, le lecteur DVD, et un port PCMIA. Notons que la carte son intègre un co-processeur audio, qui n'est pas représenté dans le diagramme ci-dessous. [[File:Playstation 2 architecture.png|centre|vignette|upright=2.5|Playstation 2 architecture]] ==L'architecture des PC et son évolution== Après avoir vu les consoles, nous allons maintenant voir les anciens PC, des années 80 ou 90. Le tout premier PC était techniquement l''''IBM PC'''. Par la suite, de nombreux ordinateurs ont tenté de reproduire l'IBM PC originel, avec parfois quelques modifications mineures. De tels ordinateurs ''IBM PC compatibles'', ont été très nombreux, pour des raisons diverses. Le fait d'utiliser des composants banalisés, facilement disponibles, ainsi qu'une bonne documentation de l'IBM PC originel, a grandement aidé. Les IBM PC compatibles ont progressivement évolué pour donner les PC actuels. L'IBM PC compatible a donné naissance à de nombreux standards divers. ===L'IBM PC originel et l'IBM PC XT=== [[File:IBM PC XT 02.jpg|vignette|IBM PC XT.]] Nous allons commencer par voir l'IBM PC originel, et son successeur : l'IBM Personal Computer XT. Nous les appelerons tous deux l'IBM PC. L'IBM PC utilisait un processeur Intel 8088, qui était un processeur 8 bits. Ils utilisaient un bus système unique, appelé le '''bus XT'''. Le bus système allait à 4.77 MHz, soit la même fréquence que le processeur. C'était un bus de 8 bits, ce qui collait parfaitement avec les processeurs 8 bits commercialisés par Intel à l'époque. L'IBM PC comprenait une mémoire ROM avec de quoi faire fonctionner le PC. La ROM en question contenait un programme minimal, appelé le '''BIOS''', sans lequel le PC ne fonctionnait pas du tout. Il servait de base pour le système d'exploitation et MS-DOS ne fonctionnait pas sans elle. De nos jours, son rôle est plus limité : sans elle, le PC ne démarre pas. Mais nous détaillerons cela dans le prochain chapitre. En plus de la ROM pour le BIOS, l'IBM PC avait quatre mémoires ROM dédiée au langage de programmation BASIC. Lorsque le PC démarrait, il ne bootait pas un système d'exploitation, mais lançait l'interpréteur pour le langage BASIC. De nos jours, ce serait l'équivalent d'un ordinateur qui boote directement sur du Python, à savoir la console Python que vous avez peut-être déjà utilisé si vous avez testé Python. Ceux qui ont déjà touché à un ordinateur de l'époque savent ce que ca veut dire, mais c'est malheureusement très difficile à expliquer sans ce genre d'expérience. Toujours est-il que c'était une sorte de norme à l'époque : les ordinateurs bootaient généralement sur un interpréteur BASIC. [[File:XT Bus pins.svg|vignette|Connecteur du bus XT.]] Les PC étaient conçus pour qu'on branche des '''cartes d'extension''', à savoir des cartes électroniques qu'on branchait sur la carte mère, à l'intérieur du PC. Les cartes d'extension de l'époque étaient surtout des cartes son ou des cartes graphiques, mais aussi des cartes pour brancher des péripéhriques. par exemple, on pouvait ajouter deux cartes graphiques dans l'IBM PC originel : l'''IBM Monochrome Display Adapter'' et/ou la ''IBM Color Graphics Adapter''. De nos jours, les cartes son sont intégrées à la carte mère, mais les cartes graphiques sont restées des cartes d'extension. Les cartes d'extension étaient branchées sur un '''connecteur XT''', qui était directement relié au bus XT. Le connecteur XT est illustré ci-contre, mais ne vous en souciez pas trop pour le moment. La carte mère de l'IBM PC avait 5 connecteurs de ce type, qu'on pouvait peupler avec autant de cartes d'extension. L'IBM Personal Computer XT est passé à 8 connecteurs XT, soit trois de plus. Pour ce qui est des périphériques, l'IBM PC avait plusieurs connecteurs : un port série, un port parallèle, un port pour le clavier, et un port pour un lecteur cassette. Le clavier et le lecteur cassette étaient connectés directement sur la carte mère, qui contenait quelques circuits pour gérer le clavier. Par contre, les deux premiers n'étaient pas connectés à la carte mère. Le port série était en réalité une carte d'extension, branchée sur un connecteur XT. Et il en est de même pour le port parallèle. Pour ce qui est des supports de stockage, l'IBM PC originel n'avait pas de disque dur et n'avait que des lecteurs de disquette. De plus, le lecteur de disquette n'était pas connecté directement sur la carte mère, mais était connecté à une carte d'extension, branchée sur un connecteur XT. La carte d'extension avait deux connecteurs, un par lecteur de disquette, ce qui fait que les deux lecteurs de disquettes pouvaient être branchés sur une seule carte d'extension. L'IBM Personal Computer XT a ajouté un disque dur, sauf sur quelques sous-modèles spécifiques. Le PC avait aussi un petit haut-parleur capable de faire des bips. Pour résumer, l'IBM PC originel se reposait beaucoup sur les cartes d'extension, sa carte mère contenait peu de choses. Enfin, peu de choses... Il y avait un processeur Intel 8088, éventuellement un coprocesseur flottant 8087, de la RAM, de la ROM, et des circuits intégrés assez divers. En voici la liste, certains vous seront familiers, d'autres vous seront inconnus à ce stade du cours : * les circuits de décodage d'adresse ; * un contrôleur DMA intel 8273 ; * un contrôleur d'interruption 8259 ; * un contrôleur de bus Intel 8288 pour gérer le bus XT ; * un générateur d'horloge Intel 8284 et un diviseur de fréquence ; * un ''timer'' Intel 8253, le même que celui étudié dans le chapitre sur les ''timers'' ; * un contrôleur parallèle 8255. Les multiplexeurs, registres et portes logiques, sont des circuits de décodage d'adresse, qui permettent de combiner plusieurs RAM en une seule, idem avec la mémoire ROM. Si vous verrez qu'il y a 5 mémoires ROM : une ROM pour le BIOS, et quatre autres ROM pour le BASIC. Les 4 ROM du BASIC sont combinées en une seule mémoire ROM. Pour les RAM, il y en a 8 à 32, qui sont combinées en une seule RAM de 16 à 64 kibioctets. [[File:IBM 5150 Motherboard.svg|centre|vignette|upright=3|Carte mère de l'IBM 5150, un modèle de l'IBM PC.]] ===L'architecture d'un IBM PC compatible 16 bits=== Les PC suivants sont passés à des processeurs 16 bits, mais c'était toujours des processeurs x86 d'Intel, à savoir des Intel 286 et 386. La RAM a grossi, quelques entrées-sorties ont été ajoutées, mais l'architecture globale est plus moins resté le même. C'est surtout au niveau du bus et des périphériques que les changements majeurs ont eu lieu. [[File:ISA Bus pins.svg|vignette|Connecteur ISA.]] Les PC 16 bits utilisaient un bus système unique, sur lequel tout était connecté : le processeur, la RAM, la ROM, les cartes d'extension et tout le reste. Le bus en question s'appelait le '''bus AT''', mais il a rapidement été renommé en '''bus ISA''' (''Industry Standard Architecture''). Le bus ISA était prévu pour avoir une compatibilité avec le bus 8 bits de l'IBM PC originel. D'ailleurs, cela se ressent jusque dans le connecteur utilisé : le connecteur ISA est un connecteur XT qu'on a fusionné avec un second connecteur pour l'étendre de 8 à 16 bits. Les PC 16 bits avaient toujours un port série, un port parallèle, un clavier, un lecteur de disquette et des cartes d'extension. Des disques durs pouvaient être ajoutés, aussi. Mais pour ces périphériques, un changement majeur a eu lieu comparé à l'IBM PC originel. L'IBM PC originel utilisait des cartes d'extension pour tout, sauf le clavier. Mais maintenant, les périphériques ne sont plus connectés à une carte d'extension. A la place, les circuits de la carte d'extension sont déplacés sur la carte mère. Mais n'allez pas croire qu'ils étaient connectés directement au bus ISA, il y avait des intermédiaires. Le clavier était relié à un '''contrôleur de clavier''', qui faisait l'interface entre le connecteur du clavier et le bus ISA. Le contrôleur de clavier était appelé le ''Keyboard Controler'', abrévié en KB. Il recevait ce qui est tapé au clavier et traduisait cela en quelque chose de compréhensible par l'ordinateur. Les autres périphériques étaient connectés à un circuit intégré dédié : l''''Intel 82091AA'''. Il était connecté au lecteur de disquette, au port série et au port parallèle. Il servait d'intermédiaire entre ces périphériques et le bus ISA. Vous pouvez le voir comme une sorte de répartiteur, mais qui ne serait pas connecté sur le processeur et la RAM Enfin, il ne faut pas oublier les autres composants présents sur l'IBM PC originel. Le BIOS est toujours là, de même que les ''timers'' Intel 8253 PIT, le contrôleur d'interruption Intel 8259 et le contrôleur DMA Intel 8237. Les PC 16 bits ont aussi intégré une ''Real Time Clock'' (RTC). Pour rappel, c'est un composant qui permet au PC de mémoriser la date et l'heure courante, à la seconde près. Le tout est résumé dans le schéma ci-dessous. [[File:Architecture de l'IBM PC compatible.png|centre|vignette|upright=2.5|Architecture de l'IBM PC compatible]] Un point important est que le bus ISA allait à la même fréquence que le processeur, vu que c'était un bus système. Les processeurs de l'époque étaient des CPU 286 d'Intel, ou le 386 d'Intel. Les Intel 286 allaient de 4 MHz minimum, à 25 MHz maximum. Le 386, quant à lui, allait de 12 à 40 MHz. Le bus ISA devait aller à cette fréquence, il était synchrone avec le processeur. Par la suite, les processeurs ont gagné en performance, ce qui fait que le bus ISA est devenu trop lent pour le processeur. Une idée a alors été de conserver le bus ISA, pour des raisons de compatibilité, mais de le reléguer comme bus secondaire. L'ordinateur contient alors deux bus : un bus système, et un bus ISA secondaire. Le lien entre les deux est réalisé par un '''pont ISA''', ''ISA Bridge'' en anglais. Le bus ISA fonctionnait alors sa fréquence usuelle, alors que le bus système était beaucoup plus rapide. Le bus système fonctionnait à une fréquence bien plus élevée, ce qui fait que le processeur pouvait communiquer à pleine vitesse, notamment avec la RAM. Le processeur n'était alors plus forcé à aller à la même fréquence que le bus ISA [[File:Architecture de l'IBM PC compatible avec bridge ISA.png|centre|vignette|upright=2.5|Architecture de l'IBM PC compatible avec bridge ISA]] Les PC de l'époque intégraient donc plusieurs bus séparés. Vous avez bien lu : plusieurs bus ! Ici, il s'agit de ce que j'appelle des '''bus en cascade''', à savoir qu'un bus est connecté à un autre bus par un intermédiaire. Au passage, si j'aborde ces exemples, car c'est pareil sur les ordinateurs modernes. Le pont ISA a été remplacé par des circuits différents, mais qui ont un rôle assez similaire. Le ''chipset'' de votre carte mère n'est qu'un lointain descendant du pont ISA, qui s'interface avec des bus différents. ===L'arrivée des standards AT et IDE pour les disques durs=== Initialement, les disques durs étaient placés dans l'ordinateur et étaient connectés sur le bus ISA, via une carte d'extension ISA. En clair, il fallait connecter le disque dur sur une carte d'extension, et non sur la carte mère. Les cartes d'extension en question permettaient de connecter un ou plusieurs disques durs, parfois des lecteurs de disquette supplémentaires. Les cartes ISA de ce type faisaient juste l'interface entre le bus ISA et les disques durs, rien de plus. L'interface en question a été standardisée, ce qui a donné le standard ''AT Bus Attachment'', qui a été abrévié en ATA. Et ce n'était pas que pour les disques durs, de nombreux composants étaient dans ce cas. Une carte d'extension servait d'intermédiaire entre eux et la carte mère. Les cartes d'extension en question étaient appelées des ''Host bus adapter''. [[File:Acculogic sIDE-4 Controller ISA.jpg|centre|vignette|upright=2|Carte ISA d'interface disque dur, de marque Acculogic.]] Mais les choses ont rapidement évoluées, que ce soit du côté des cartes mères que du côté des disques durs. Le '''standard IDE''' a permis de brancher un disque dur directement sur la carte mère, sans passer par une carte d'interface ISA. Pour cela, la carte mère réservait un connecteur ISA pour le disque dur, renommé '''connecteur ATA'''. Pour que cela soit possible, il a fallu rajouter des circuits sur la carte mère. Tout ce qui était sur les cartes d'interface ISA s'est retrouvé sur la carte mère. [[File:Ajout des ports IDE sur la carte mère.png|centre|vignette|upright=2|Ajout des ports IDE sur la carte mère]] En réalité, les connecteurs ATA étaient des connecteurs ISA simplifiés. Un connecteur ISA avait en tout 98 broches, alors qu'un connecteur ATA n'en contient que 40. Les broches qui étaient inutiles pour les disques durs ont simplement été enlevées. Et qui dit connecteur spécialisé, dit câble spécialisé. Les disques durs étaient branchés sur le connecteur AT grâce à un câble ATA, sur lequel on pouvait connecter deux disques durs. [[File:ATA Plug.svg|centre|vignette|upright=2|Connecteur ATA.]] [[File:ATA cables.jpg|centre|vignette|upright=2|Cable ATA.]] Il était donc possible de connecter deux disques durs sur un seul connecteur ATA. Et cette possibilité est devenue d'autant plus utile par la suite. A partir de la version 2, ATA supportait aussi les lecteurs de disquettes, les lecteurs de CD/DVD, et bien d'autres supports de stockage. Il était alors possible de connecter un lecteur CD et un disque dur sur un seul connecteur. Les cartes mères avaient généralement deux connecteurs ATA, et n'avaient pas besoin de plus. C'était suffisant pour connecter un disque dur, un lecteur de disquette et un lecteur CD, configuration courante entre les années 90 et 2000. Un câble est donc connecté à deux supports de stockage. Pour distinguer les deux, le standard ATA ajoute une possibilité de configuration. Sur un câble, il doit y avoir un support de stockage "maitre" et un support "esclave". C'était la terminologie de l'époque, que je reproduis ici, même si elle est fortement trompeuse. N'allez pas croire que cela implique que l'un ait des avantages sur l'autre. Le support 'maitre" n'a pas droit à plus de bande passante, il n'a pas la priorité sur l'autre, rien du tout. Il s'agit juste d'un nombre qui permet de savoir avec qui le processeur communique, qui vaut 0 pour le premier support, 1 pour l'autre. Une sorte d'adresse de 1 bit, si l'on veut. [[File:ATA-Konfiguration02.png|centre|vignette|upright=2|Configuration ATA.]] Pour configurer un support de stockage en mode "maitre" ou "esclave", le support de stockage avait quelques pins dédiés. Il suffisait de placer un détrompeur en plastique sur les pins adéquats. Les pins se trouvaient à l'arrière du disque dur ou du lecteur de CD/DVD/Disquette/autre. [[File:HDD Master and Slave Description.jpg|centre|vignette|upright=2|Configuration ''Master/Slave''.]] ===L'architecture d'un PC avec un processeur Intel 486=== Maintenant, passons aux ordinateurs 32 bits, avec l'exemple d'un PC avec un processeur 486 d'Intel. A cette époque, le bus ISA était devenu trop limité et était en place d'être remplacé par le bus PCI, qui avait la même fonction. De nombreuses cartes d'extension utilisaient déjà ce standard et étaient branchées sur des connecteurs PCI dédiés, différents des connecteurs ISA. Intuitivement, on se dit que le bus PCI remplaçait le bus ISA, mais les choses étaient plus compliquées. Les disques durs gardaient leur connecteur ATA, et ne passaient pas par le bus PCI. Ils avaient un bus IDE séparé, qui était un bus ISA modifié. Là encore, les processeurs étaient devenus beaucoup plus rapides que le bus PCI. Les deux allaient à des fréquences assez différentes, ce qui fait que le bus PCI était séparé du bus système. Il y avait alors deux implémentations possibles. * La première utilise un répartiteur unique, relié au processeur, à la RAM, au bus PCI, et au bus IDE. * La seconde utilise un bus système séparé du bus PCI, avec un '''pont PCI''' pour faire l'interface entre les deux. Le '''''System Controler''''' était un circuit intégré, placé sur la carte mère, qui peut servir soit de pont PCI, soit de répartiteur. Le répartiteur PCI sert d'intermédiaire avec le bus PCI, mais aussi avec le bus IDE, utilisé pour les disques durs, aussi appelé le bus ''Parallel ATA''. Il peut aussi être connecté au processeur, à la mémoire RAM, ainsi qu'à la mémoire cache, mais cela ne sert que quand il est utilisé comme répartiteur. [[File:Architecture d'un PC utilisant un bus PCI, implémentation avec un répartiteur.png|centre|vignette|upright=2|Architecture d'un PC utilisant un bus PCI, implémentation avec un répartiteur]] Pour des raisons de compatibilité, le bus ISA avait été conservé, aux côtés du bus PCI. Il y avait un pont ISA en plus du pont/répartiteur PCI. Une implémentation possible aurait été de connecter les deux ponts ISA et PCI à un bus système unique. Mais cette solution n'a pas été retenue. La raison est que le bus PCI et le bus ISA ont des performances très différentes. Le bus PCI est très rapide, le bus ISA beaucoup plus lent. La différence est d'un ordre de grandeur, environ. Dans ces conditions, il est possible de faire passer les communications ISA à travers le bus PCI. Pour cela, le pont ISA est directement connecté sur le pont PCI, comme illustré ci-dessous. Et il en est de même pour le bus dédié aux disques durs. En effet, les disques durs étaient autrefois reliés au bus ISA, mais cela a changé depuis. Ils disposent maintenant de leur propre bus dédié, le '''bus IDE''', qui est un bus ISA simplifié. Et ce bus ISA simplifié était connecté directement sur le pont PCI. [[File:Architecture de l'IBM PC compatible avec pont PCI.png|centre|vignette|upright=2|Architecture de l'IBM PC compatible avec pont PCI]] Dans ce qui va suivre, nous allons étudier un exemple qui utilise un bus système séparé, avec un pont PCI, sans répartiteur. Voilà pour les grandes lignes, mais le schéma ci-dessous montre que tout est plus complexe. Vous remarquerez des connexions optionnelles entre le pont PCI et la mémoire RAM et la mémoire cache. La raison est que le pont PCI peut aussi servir de répartiteur en remplacement du bus système. Concrètement, on peut alors retirer le bus système. La mémoire, le bus PCI, le bus ISA, le bus IDE, le processeur et la RAM sont alors connectés au répartiteur PCI, qui sert d'intermédiaire central entre tous ces composants. Mais ce n'est pas la solution qui a été retenue dans notre exemple. [[File:Intel486-Typ PCI System.png|centre|vignette|upright=2|PC IBM compatible avec un 486, un bus PCI et un bus ISA. Le ''host bus'' est le bus système.]] Le pont ISA sert ici d'intermédiaire entre le bus système et le bus ISA. De plus, il a été amélioré sur de nombreux points. Il inclut notamment des circuits qui étaient autrefois sur la carte mère, à savoir le contrôleur DMA 82C87 et le contrôleur d'interruption 82C59, ainsi que les ''timers'' Intel 82C54. Les composants restants sont eux reliés sur un quatrième bus : le bus X, l'ancêtre du bus ''Low Pin Count''. Le bus X était celui du BIOS, du contrôleur de clavier, de la ''Real Time Clock'', et du contrôleur de périphérique 82091AA d'Intel. [[File:ISA Bridge schematic.png|centre|vignette|upright=2|ISA Bridge.]] ===L'architecture des PC des années 90-2000=== Par la suite, les ponts PCI et ISA ont évolué avec l'évolution des bus de l'ordinateur. Le bus ISA a progressivement été remplacé par d'autres bus, comme le bus ''Low Pin Count'', le bus PCI a été remplacé par le PCI Express, d'autres bus ont été ajoutés, etc. Mais la séparation du ''chipset'' en deux a été conservée. [[File:Chipset schematic.svg|vignette|upright=1.0|Chipset séparé en northbridge et southbridge.]] Le pont PCI et le pont ISA ont été remplacés respectivement par le '''pont nord''' et le '''pont sud''', plus connus par leurs noms anglais de ''northbridge'' et de ''southbridge''. Le pont nord servait d'interface entre le processeur, la mémoire et la carte graphique et est connecté à chacun par un bus dédié. Il intégrait aussi le contrôleur mémoire. Le pont sud est le répartiteur pour les composants lents, à savoir l'USB, l'Ethernet, etc. Le bus qui relie le processeur au pont nord était appelé le '''''Front Side Bus''''', abrévié en FSB. [[File:IMac Chipset.png|centre|vignette|upright=2|Chipset séparé en northbridge et southbridge.]] Un point important est que le bus PCI est devenu un bus assez lent, ce qui fait qu'il a finit par être connecté au pont sud. Le pont PCI est donc devenu le pont sud, dans le courant des années 2000. Durant un moment, un équivalent du pont ISA a subsisté dans un circuit de '''''Super IO'''''. Concrètement, il s'occupait du lecteur de disquette, du port parallèle, du port série, et des ports PS/2 pour le clavier et la souris. Mais il ne gérait pas le bus ISA, mais son remplaçant, le bus ''Low Pin Count''. [[File:Motherboard diagram fr.svg|centre|vignette|upright=1.5|Carte mère avec circuit Super IO.]] ===L'architecture des PC depuis les années 2000=== Depuis la sortie du processeur AMD Athlon 64, le pont nord a été fusionné dans le processeur. La fusion ne s'est pas faite en une fois, des fonctionnalités ont progressivement été progressivement intégrées dans le processeur. Le pont sud est resté, mais il a alors été progressivement connecté directement au processeur. La raison derrière cette intégration est que les processeurs avaient de plus en plus de transistors à leur disposition. Ils en ont profité pour intégrer le pont nord. Et cela permettait de simplifier le câblage des cartes mères, sans pour autant rendre vraiment plus complexe la fabrication du processeur. Les industriels y trouvent leur compte. La première étape a été l'intégration du contrôleur mémoire a été intégré au processeur. Concrètement, le résultat était que la mémoire RAM n'était plus connectée au pont nord, mais était connectée directement au processeur ! Il y a donc eu un retour d'un bus mémoire, mais spécialisé pour la mémoire RAM. En théorie, une telle intégration permet diverses optimisations quant aux transferts avec la mémoire RAM. Les transferts ne passent pas par un répartiteur, ce qui réduit le temps d'accès à la mémoire RAM. Ajoutons de sombres histoires de prefetching, d'optimisation des commandes, et j'en passe. Toujours est-il que le pont nord ne servait alors d'intermédiaire que pour les ports PCI Express, et le pont sud. [[File:X58 Block Diagram.png|centre|vignette|upright=2|Chipset X58 d'Intel.]] Par la suite, la carte graphique fût aussi connectée directement sur le processeur. Le processeur incorpore pour cela des contrôleurs PCI-Express. Le pont nord a alors disparu complétement, son intégration dans le processeur était complète. Sur les cartes mères Intel récentes, le pont sdud subsiste, il est appelé le ''Platform Controler Hub'', ou PCH. L'organisation des bus sur la carte mère qui résulte de cette connexion du processeur à la carte graphique, est illustrée ci-dessous, avec l'exemple du PCH. [[File:Intel 5 Series architecture.png|centre|vignette|upright=2|Intel 5 Series architecture]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=L'interface électrique entre circuits intégrés et bus | prevText=L'interface électrique entre circuits intégrés et bus | next=La hiérarchie mémoire | nextText=La hiérarchie mémoire }} </noinclude> 13domncigrdlrcg4gme53xxt1yoqy94 Fonctionnement d'un ordinateur/Architectures multiprocesseurs et multicœurs 0 65962 762842 762439 2026-04-03T18:30:51Z Mewtow 31375 /* Les processeurs multicœurs */ 762842 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs ordinateurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Si utiliser plusieurs ordinateurs connectés par un réseau local est une solution simple et facile à implémenter, elle est cependant assez onéreuse et demande des logiciels adaptés qui permettent de profiter d'une telle architecture matérielle. D'autres solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. ==Les systèmes multiprocesseur== Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, la demande pour des ordinateurs multiprocesseurs était limitée à quelques professionnels et ne justifiait pas un investissement majeur dans ces technologies. Au final, le gain était assez faible en terme de performance, peu de logiciels grand public profitaient de l'usage de plusieurs processeurs, la technologie est restée confidentielle. Du moins, avant l'arrivée des processeurs multicœur, après 2005. Nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. ===Le réseau d'interconnexion inter-processeur=== Les cartes mères multi-processeurs incorporent un réseau d'interconnexion pour connecter les processeurs à la mémoire RAM, au reste de l'ordinateur, et aussi pour les connecter entre eux. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les processeurs sont tous connectés en utilisant deux solutions : soit avec un bus, soit avec un réseau d'interconnexion très complexe. Une solution simple relie les processeurs/cœurs entre eux grâce au bus mémoire. Le réseau d'interconnexion est donc un bus, partagé avec tous les processeurs. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. Les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] De nos jours, les cartes mères pour serveur n'utilisent plus de bus partagé, mais utilisent un réseau d'interconnexion plus élaboré. Le réseau en question est un vrai réseau, qui peut être un réseau en anneau, un réseau crossbar, un réseau routé, etc. Le réseau d'interconnexion relie entre eux les cœurs, l'interface avec la mémoire RAM, le cache partagé L3/L4, mais aussi le bus PCI-Express, le GPU et quelques autres circuits séparés. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] Les systèmes multi-processeurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils remplacent le fameux ''front-side bus'' des tout premiers processeurs multicœurs. ===Les systèmes multiprocesseur symétriques et asymétriques=== Les systèmes multiprocesseur utilisent souvent des processeurs identiques. C'est la la solution la plus simple et la plus pratique, si on veut gagner en performance. Cependant, il existe des systèmes multiprocesseurs sur lequels les deux processeurs utilisés ne sont pas identiques. La différence est suffisamment importante pour qu'on ait mis un nom dessus. On parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. Le multiprocesseur asymétrique peut sembler assez contreintuitif. Mais pourtant, nous en avons rencontré précédemment dans ce cours ! Rappelez-vous quand nous avions parlé des co-processeurs. Il s'agissait formellement de multiprocesseur asymétrique. Pour rappel, les co-processeurs sont des processeurs utilisés en complément du processeur principal, afin de le décharger de certains calculs. Les plus connus sont certainement les co-processeurs arithmétiques, qui étaient des FPU externalisées, utilisées à une époque où les CPU n'intégraient pas de FPU. Mais nous allons laisser ceux-ci de côté pour une raison simple : ils ne permettaient pas d'exécution en parallèle ! Avec eux, le CPU principal se met en pause en attendant que le coprocesseur finisse son travail. Les deux processeurs se passent la main pour exécuter un programme unique. On parle alors de '''coprocesseurs fortement couplés'''. Pour le dire autrement, il y a deux manières d'utiliser un co-processeur : soit le CPU et le co-processeur se passent à la main à tour de rôle, soit ils travaillent en parallèle. Si le CPU et le coprocesseur travaillent en parallèle, ils exécutent des programmes différents. On a un programme qui s’exécute sur le coprocesseur, un autre qui s’exécute sur le CPU. On parle alors de '''coprocesseurs faiblement couplés'''. C'est le cas pour tous les coprocesseurs, à l'exception des co-processeurs arithmétiques. Et ce sont eux qui nous intéressent ici. ===Les interruptions inter-processeurs=== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Un exemple d'implémentation très simple est celui de la console Néo-géo, qui contenait deux processeurs. Le premier est un Motorola 68000 qui sert de processeur principal, l'autre est un Z80 qui sert de processeur dédié à l'audio. Le Z80 commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des interruptions au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un '''''IO arbiter chip''''', un circuit sur la carte mère connecté au bus, qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80. Le code de 8 bits est envoyé au processeur Z80, en parallèle de l'interruption. : Il est possible de voir ce ''IO arbiter chip'' comme un contrôleur d'interruptions spécialisé dans les interruptions inter-processeur. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Les processeurs multicœurs== Puis, avec les progrès en matière de miniaturisation des processeurs, les fabricants ont eu l'idée d'utiliser les transistors qu'ils avaient pour fabriquer des '''processeurs multicœurs''', un terme que vous avez peut-être déjà entendu sans vraiment comprendre ce qu'il signifiait réellement. Les processeurs multicœurs peuvent être vus comme un regroupement de plusieurs processeurs sur la même puce de silicium. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Chaque cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits d'un processeur ne sont présents qu'en un seul exemplaire dans un processeur multicœurs, comme les circuits de communication avec la mémoire ou les circuits d’interfaçage avec la carte mère. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. ===Le multicœurs symétrique ou asymétrique=== Dans la grosse majorité des cas, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation et on peut très bien mettre plusieurs cœurs assez différents sur la même puce. On peut par exemple utiliser un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Un exemple est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 coeurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] ===Les architectures à cœurs conjoints=== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les CPU multicœurs connectent plusieurs cœurs entre eux, tout comme les systèmes multi-processeurs connectent plusieurs processeurs entre eux. Mais les transferts de données entre cœurs se font par l'intermédiaire des caches, ce qui fait que l'implémentation est différente de celle utilisée sur les systèmes multi-processeurs. Les standards pour les interconnexions entre coeurs sont assez proches de ceux utilisés pour interconnecter des processeurs, mais les standards utilisés ne sont pas les mêmes en pratique. Là encore, les cœurs sont connectés entre eux soit par un bus, soit par un réseau d'interconnexion. ===Le bus partagé entre plusieurs cœurs=== Une solution simple relie les cœurs entre eux grâce à un '''bus partagé'''. Le cas le plus simple est celui où chaque cœur dispose de caches dédiés, qui sont alors tous reliés au bus mémoire, qui sert d'intermédiaire. Mais l'idée doit être adaptée sur les processeurs multicœurs avec des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts dont nous parlerons en détail du bus partagé dans le chapitre sur la cohérence des caches. ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> 457tt41uigdzv1athnr7xfg6233q91n 762843 762842 2026-04-03T19:01:17Z Mewtow 31375 /* Les processeurs multicœurs */ 762843 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs ordinateurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Si utiliser plusieurs ordinateurs connectés par un réseau local est une solution simple et facile à implémenter, elle est cependant assez onéreuse et demande des logiciels adaptés qui permettent de profiter d'une telle architecture matérielle. D'autres solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. ==Les systèmes multiprocesseur== Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, la demande pour des ordinateurs multiprocesseurs était limitée à quelques professionnels et ne justifiait pas un investissement majeur dans ces technologies. Au final, le gain était assez faible en terme de performance, peu de logiciels grand public profitaient de l'usage de plusieurs processeurs, la technologie est restée confidentielle. Du moins, avant l'arrivée des processeurs multicœur, après 2005. Nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. ===Le réseau d'interconnexion inter-processeur=== Les cartes mères multi-processeurs incorporent un réseau d'interconnexion pour connecter les processeurs à la mémoire RAM, au reste de l'ordinateur, et aussi pour les connecter entre eux. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les processeurs sont tous connectés en utilisant deux solutions : soit avec un bus, soit avec un réseau d'interconnexion très complexe. Une solution simple relie les processeurs/cœurs entre eux grâce au bus mémoire. Le réseau d'interconnexion est donc un bus, partagé avec tous les processeurs. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. Les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] De nos jours, les cartes mères pour serveur n'utilisent plus de bus partagé, mais utilisent un réseau d'interconnexion plus élaboré. Le réseau en question est un vrai réseau, qui peut être un réseau en anneau, un réseau crossbar, un réseau routé, etc. Le réseau d'interconnexion relie entre eux les cœurs, l'interface avec la mémoire RAM, le cache partagé L3/L4, mais aussi le bus PCI-Express, le GPU et quelques autres circuits séparés. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] Les systèmes multi-processeurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils remplacent le fameux ''front-side bus'' des tout premiers processeurs multicœurs. ===Les systèmes multiprocesseur symétriques et asymétriques=== Les systèmes multiprocesseur utilisent souvent des processeurs identiques. C'est la la solution la plus simple et la plus pratique, si on veut gagner en performance. Cependant, il existe des systèmes multiprocesseurs sur lequels les deux processeurs utilisés ne sont pas identiques. La différence est suffisamment importante pour qu'on ait mis un nom dessus. On parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. Le multiprocesseur asymétrique peut sembler assez contreintuitif. Mais pourtant, nous en avons rencontré précédemment dans ce cours ! Rappelez-vous quand nous avions parlé des co-processeurs. Il s'agissait formellement de multiprocesseur asymétrique. Pour rappel, les co-processeurs sont des processeurs utilisés en complément du processeur principal, afin de le décharger de certains calculs. Les plus connus sont certainement les co-processeurs arithmétiques, qui étaient des FPU externalisées, utilisées à une époque où les CPU n'intégraient pas de FPU. Mais nous allons laisser ceux-ci de côté pour une raison simple : ils ne permettaient pas d'exécution en parallèle ! Avec eux, le CPU principal se met en pause en attendant que le coprocesseur finisse son travail. Les deux processeurs se passent la main pour exécuter un programme unique. On parle alors de '''coprocesseurs fortement couplés'''. Pour le dire autrement, il y a deux manières d'utiliser un co-processeur : soit le CPU et le co-processeur se passent à la main à tour de rôle, soit ils travaillent en parallèle. Si le CPU et le coprocesseur travaillent en parallèle, ils exécutent des programmes différents. On a un programme qui s’exécute sur le coprocesseur, un autre qui s’exécute sur le CPU. On parle alors de '''coprocesseurs faiblement couplés'''. C'est le cas pour tous les coprocesseurs, à l'exception des co-processeurs arithmétiques. Et ce sont eux qui nous intéressent ici. ===Les interruptions inter-processeurs=== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Un exemple d'implémentation très simple est celui de la console Néo-géo, qui contenait deux processeurs. Le premier est un Motorola 68000 qui sert de processeur principal, l'autre est un Z80 qui sert de processeur dédié à l'audio. Le Z80 commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des interruptions au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un '''''IO arbiter chip''''', un circuit sur la carte mère connecté au bus, qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80. Le code de 8 bits est envoyé au processeur Z80, en parallèle de l'interruption. : Il est possible de voir ce ''IO arbiter chip'' comme un contrôleur d'interruptions spécialisé dans les interruptions inter-processeur. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Les processeurs multicœurs== Avec les progrès de la miniaturisation des transistors, les fabricants de CPU sont passés aux '''processeurs multicœurs''', qui peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. ===Le multicœurs symétrique ou asymétrique=== Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] ===Les architectures à cœurs conjoints=== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les CPU multicœurs connectent plusieurs cœurs entre eux, tout comme les systèmes multi-processeurs connectent plusieurs processeurs entre eux. Mais les transferts de données entre cœurs se font par l'intermédiaire des caches, ce qui fait que l'implémentation est différente de celle utilisée sur les systèmes multi-processeurs. Les standards pour les interconnexions entre coeurs sont assez proches de ceux utilisés pour interconnecter des processeurs, mais les standards utilisés ne sont pas les mêmes en pratique. Là encore, les cœurs sont connectés entre eux soit par un bus, soit par un réseau d'interconnexion. ===Le bus partagé entre plusieurs cœurs=== Une solution simple relie les cœurs entre eux grâce à un '''bus partagé'''. Le cas le plus simple est celui où chaque cœur dispose de caches dédiés, qui sont alors tous reliés au bus mémoire, qui sert d'intermédiaire. Mais l'idée doit être adaptée sur les processeurs multicœurs avec des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts dont nous parlerons en détail du bus partagé dans le chapitre sur la cohérence des caches. ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> 2nln18otcoqid7tcubgzmbj3mztlu1m 762844 762843 2026-04-03T19:02:10Z Mewtow 31375 762844 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs ordinateurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Si utiliser plusieurs ordinateurs connectés par un réseau local est une solution simple et facile à implémenter, elle est cependant assez onéreuse et demande des logiciels adaptés qui permettent de profiter d'une telle architecture matérielle. D'autres solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. ==Les systèmes multiprocesseur== Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, la demande pour des ordinateurs multiprocesseurs était limitée à quelques professionnels et ne justifiait pas un investissement majeur dans ces technologies. Au final, le gain était assez faible en terme de performance, peu de logiciels grand public profitaient de l'usage de plusieurs processeurs, la technologie est restée confidentielle. Du moins, avant l'arrivée des processeurs multicœur, après 2005. Nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. ===Le réseau d'interconnexion inter-processeur=== Les cartes mères multi-processeurs incorporent un réseau d'interconnexion pour connecter les processeurs à la mémoire RAM, au reste de l'ordinateur, et aussi pour les connecter entre eux. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les processeurs sont tous connectés en utilisant deux solutions : soit avec un bus, soit avec un réseau d'interconnexion très complexe. Une solution simple relie les processeurs/cœurs entre eux grâce au bus mémoire. Le réseau d'interconnexion est donc un bus, partagé avec tous les processeurs. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. Les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] De nos jours, les cartes mères pour serveur n'utilisent plus de bus partagé, mais utilisent un réseau d'interconnexion plus élaboré. Le réseau en question est un vrai réseau, qui peut être un réseau en anneau, un réseau crossbar, un réseau routé, etc. Le réseau d'interconnexion relie entre eux les cœurs, l'interface avec la mémoire RAM, le cache partagé L3/L4, mais aussi le bus PCI-Express, le GPU et quelques autres circuits séparés. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] Les systèmes multi-processeurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils remplacent le fameux ''front-side bus'' des tout premiers processeurs multicœurs. ===Les systèmes multiprocesseur symétriques et asymétriques=== Les systèmes multiprocesseur utilisent souvent des processeurs identiques. C'est la la solution la plus simple et la plus pratique, si on veut gagner en performance. Cependant, il existe des systèmes multiprocesseurs sur lequels les deux processeurs utilisés ne sont pas identiques. La différence est suffisamment importante pour qu'on ait mis un nom dessus. On parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. Le multiprocesseur asymétrique peut sembler assez contreintuitif. Mais pourtant, nous en avons rencontré précédemment dans ce cours ! Rappelez-vous quand nous avions parlé des co-processeurs. Il s'agissait formellement de multiprocesseur asymétrique. Pour rappel, les co-processeurs sont des processeurs utilisés en complément du processeur principal, afin de le décharger de certains calculs. Les plus connus sont certainement les co-processeurs arithmétiques, qui étaient des FPU externalisées, utilisées à une époque où les CPU n'intégraient pas de FPU. Mais nous allons laisser ceux-ci de côté pour une raison simple : ils ne permettaient pas d'exécution en parallèle ! Avec eux, le CPU principal se met en pause en attendant que le coprocesseur finisse son travail. Les deux processeurs se passent la main pour exécuter un programme unique. On parle alors de '''coprocesseurs fortement couplés'''. Pour le dire autrement, il y a deux manières d'utiliser un co-processeur : soit le CPU et le co-processeur se passent à la main à tour de rôle, soit ils travaillent en parallèle. Si le CPU et le coprocesseur travaillent en parallèle, ils exécutent des programmes différents. On a un programme qui s’exécute sur le coprocesseur, un autre qui s’exécute sur le CPU. On parle alors de '''coprocesseurs faiblement couplés'''. C'est le cas pour tous les coprocesseurs, à l'exception des co-processeurs arithmétiques. Et ce sont eux qui nous intéressent ici. ===Les interruptions inter-processeurs=== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Un exemple d'implémentation très simple est celui de la console Néo-géo, qui contenait deux processeurs. Le premier est un Motorola 68000 qui sert de processeur principal, l'autre est un Z80 qui sert de processeur dédié à l'audio. Le Z80 commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des interruptions au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un '''''IO arbiter chip''''', un circuit sur la carte mère connecté au bus, qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80. Le code de 8 bits est envoyé au processeur Z80, en parallèle de l'interruption. : Il est possible de voir ce ''IO arbiter chip'' comme un contrôleur d'interruptions spécialisé dans les interruptions inter-processeur. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Les processeurs multicœurs== Avec les progrès de la miniaturisation des transistors, les fabricants de CPU sont passés aux '''processeurs multicœurs''', qui peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les CPU multicœurs connectent plusieurs cœurs entre eux, tout comme les systèmes multi-processeurs connectent plusieurs processeurs entre eux. Mais les transferts de données entre cœurs se font par l'intermédiaire des caches, ce qui fait que l'implémentation est différente de celle utilisée sur les systèmes multi-processeurs. Les standards pour les interconnexions entre coeurs sont assez proches de ceux utilisés pour interconnecter des processeurs, mais les standards utilisés ne sont pas les mêmes en pratique. Là encore, les cœurs sont connectés entre eux soit par un bus, soit par un réseau d'interconnexion. ===Le bus partagé entre plusieurs cœurs=== Une solution simple relie les cœurs entre eux grâce à un '''bus partagé'''. Le cas le plus simple est celui où chaque cœur dispose de caches dédiés, qui sont alors tous reliés au bus mémoire, qui sert d'intermédiaire. Mais l'idée doit être adaptée sur les processeurs multicœurs avec des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts dont nous parlerons en détail du bus partagé dans le chapitre sur la cohérence des caches. ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> 4a1u9sqgqz4sr9dbmfjfavk4o784i7h 762845 762844 2026-04-03T19:15:01Z Mewtow 31375 /* Les processeurs multicœurs */ 762845 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs ordinateurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Si utiliser plusieurs ordinateurs connectés par un réseau local est une solution simple et facile à implémenter, elle est cependant assez onéreuse et demande des logiciels adaptés qui permettent de profiter d'une telle architecture matérielle. D'autres solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. ==Les systèmes multiprocesseur== Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, la demande pour des ordinateurs multiprocesseurs était limitée à quelques professionnels et ne justifiait pas un investissement majeur dans ces technologies. Au final, le gain était assez faible en terme de performance, peu de logiciels grand public profitaient de l'usage de plusieurs processeurs, la technologie est restée confidentielle. Du moins, avant l'arrivée des processeurs multicœur, après 2005. Nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. ===Le réseau d'interconnexion inter-processeur=== Les cartes mères multi-processeurs incorporent un réseau d'interconnexion pour connecter les processeurs à la mémoire RAM, au reste de l'ordinateur, et aussi pour les connecter entre eux. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les processeurs sont tous connectés en utilisant deux solutions : soit avec un bus, soit avec un réseau d'interconnexion très complexe. Une solution simple relie les processeurs/cœurs entre eux grâce au bus mémoire. Le réseau d'interconnexion est donc un bus, partagé avec tous les processeurs. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. Les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] De nos jours, les cartes mères pour serveur n'utilisent plus de bus partagé, mais utilisent un réseau d'interconnexion plus élaboré. Le réseau en question est un vrai réseau, qui peut être un réseau en anneau, un réseau crossbar, un réseau routé, etc. Le réseau d'interconnexion relie entre eux les cœurs, l'interface avec la mémoire RAM, le cache partagé L3/L4, mais aussi le bus PCI-Express, le GPU et quelques autres circuits séparés. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] Les systèmes multi-processeurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils remplacent le fameux ''front-side bus'' des tout premiers processeurs multicœurs. ===Les systèmes multiprocesseur symétriques et asymétriques=== Les systèmes multiprocesseur utilisent souvent des processeurs identiques. C'est la la solution la plus simple et la plus pratique, si on veut gagner en performance. Cependant, il existe des systèmes multiprocesseurs sur lequels les deux processeurs utilisés ne sont pas identiques. La différence est suffisamment importante pour qu'on ait mis un nom dessus. On parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. Le multiprocesseur asymétrique peut sembler assez contreintuitif. Mais pourtant, nous en avons rencontré précédemment dans ce cours ! Rappelez-vous quand nous avions parlé des co-processeurs. Il s'agissait formellement de multiprocesseur asymétrique. Pour rappel, les co-processeurs sont des processeurs utilisés en complément du processeur principal, afin de le décharger de certains calculs. Les plus connus sont certainement les co-processeurs arithmétiques, qui étaient des FPU externalisées, utilisées à une époque où les CPU n'intégraient pas de FPU. Mais nous allons laisser ceux-ci de côté pour une raison simple : ils ne permettaient pas d'exécution en parallèle ! Avec eux, le CPU principal se met en pause en attendant que le coprocesseur finisse son travail. Les deux processeurs se passent la main pour exécuter un programme unique. On parle alors de '''coprocesseurs fortement couplés'''. Pour le dire autrement, il y a deux manières d'utiliser un co-processeur : soit le CPU et le co-processeur se passent à la main à tour de rôle, soit ils travaillent en parallèle. Si le CPU et le coprocesseur travaillent en parallèle, ils exécutent des programmes différents. On a un programme qui s’exécute sur le coprocesseur, un autre qui s’exécute sur le CPU. On parle alors de '''coprocesseurs faiblement couplés'''. C'est le cas pour tous les coprocesseurs, à l'exception des co-processeurs arithmétiques. Et ce sont eux qui nous intéressent ici. ===Les interruptions inter-processeurs=== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Un exemple d'implémentation très simple est celui de la console Néo-géo, qui contenait deux processeurs. Le premier est un Motorola 68000 qui sert de processeur principal, l'autre est un Z80 qui sert de processeur dédié à l'audio. Le Z80 commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des interruptions au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un '''''IO arbiter chip''''', un circuit sur la carte mère connecté au bus, qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80. Le code de 8 bits est envoyé au processeur Z80, en parallèle de l'interruption. : Il est possible de voir ce ''IO arbiter chip'' comme un contrôleur d'interruptions spécialisé dans les interruptions inter-processeur. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Les processeurs multicœurs== Avec les progrès de la miniaturisation des transistors, les fabricants de CPU sont passés aux '''processeurs multicœurs''', qui peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les CPU multicœurs connectent plusieurs cœurs entre eux, tout comme les systèmes multi-processeurs connectent plusieurs processeurs entre eux. Mais les transferts de données entre cœurs se font par l'intermédiaire des caches, ce qui fait que l'implémentation est différente de celle utilisée sur les systèmes multi-processeurs. Les standards pour les interconnexions entre coeurs sont assez proches de ceux utilisés pour interconnecter des processeurs, mais les standards utilisés ne sont pas les mêmes en pratique. Là encore, les cœurs sont connectés entre eux soit par un bus, soit par un réseau d'interconnexion. ===Le bus partagé entre plusieurs cœurs=== Une solution simple relie les cœurs entre eux grâce à un '''bus partagé'''. Le cas le plus simple est celui où chaque cœur dispose de caches dédiés, qui sont alors tous reliés au bus mémoire, qui sert d'intermédiaire. Mais l'idée doit être adaptée sur les processeurs multicœurs avec des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts dont nous parlerons en détail du bus partagé dans le chapitre sur la cohérence des caches. ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> e93ocbs4woti8v739se8rccns907dsp 762846 762845 2026-04-03T19:16:32Z Mewtow 31375 /* Les systèmes multiprocesseur */ 762846 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs ordinateurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Si utiliser plusieurs ordinateurs connectés par un réseau local est une solution simple et facile à implémenter, elle est cependant assez onéreuse et demande des logiciels adaptés qui permettent de profiter d'une telle architecture matérielle. D'autres solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. ==Les systèmes multiprocesseur== Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, la demande pour des ordinateurs multiprocesseurs était limitée à quelques professionnels et ne justifiait pas un investissement majeur dans ces technologies. Au final, le gain était assez faible en terme de performance, peu de logiciels grand public profitaient de l'usage de plusieurs processeurs, la technologie est restée confidentielle. Du moins, avant l'arrivée des processeurs multicœur, après 2005. Nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. Les systèmes multiprocesseur utilisent souvent des processeurs identiques. C'est la la solution la plus simple et la plus pratique, si on veut gagner en performance. Cependant, il existe des systèmes multiprocesseurs sur lequels les deux processeurs utilisés ne sont pas identiques. La différence est suffisamment importante pour qu'on ait mis un nom dessus. On parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. Le multiprocesseur asymétrique peut sembler assez contreintuitif, mais nous en avons rencontré précédemment dans ce cours ! Rappelez-vous quand nous avions parlé des co-processeurs, utilisés en complément du processeur principal, afin de le décharger de certains calculs. Il s'agissait formellement de multiprocesseur asymétrique. ===Le réseau d'interconnexion inter-processeur=== Les cartes mères multi-processeurs incorporent un réseau d'interconnexion pour connecter les processeurs à la mémoire RAM, au reste de l'ordinateur, et aussi pour les connecter entre eux. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les processeurs sont tous connectés en utilisant deux solutions : soit avec un bus, soit avec un réseau d'interconnexion très complexe. Une solution simple relie les processeurs/cœurs entre eux grâce au bus mémoire. Le réseau d'interconnexion est donc un bus, partagé avec tous les processeurs. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. Les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] De nos jours, les cartes mères pour serveur n'utilisent plus de bus partagé, mais utilisent un réseau d'interconnexion plus élaboré. Le réseau en question est un vrai réseau, qui peut être un réseau en anneau, un réseau crossbar, un réseau routé, etc. Le réseau d'interconnexion relie entre eux les cœurs, l'interface avec la mémoire RAM, le cache partagé L3/L4, mais aussi le bus PCI-Express, le GPU et quelques autres circuits séparés. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] Les systèmes multi-processeurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils remplacent le fameux ''front-side bus'' des tout premiers processeurs multicœurs. ===Les interruptions inter-processeurs=== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Un exemple d'implémentation très simple est celui de la console Néo-géo, qui contenait deux processeurs. Le premier est un Motorola 68000 qui sert de processeur principal, l'autre est un Z80 qui sert de processeur dédié à l'audio. Le Z80 commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des interruptions au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un '''''IO arbiter chip''''', un circuit sur la carte mère connecté au bus, qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80. Le code de 8 bits est envoyé au processeur Z80, en parallèle de l'interruption. : Il est possible de voir ce ''IO arbiter chip'' comme un contrôleur d'interruptions spécialisé dans les interruptions inter-processeur. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Les processeurs multicœurs== Avec les progrès de la miniaturisation des transistors, les fabricants de CPU sont passés aux '''processeurs multicœurs''', qui peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les CPU multicœurs connectent plusieurs cœurs entre eux, tout comme les systèmes multi-processeurs connectent plusieurs processeurs entre eux. Mais les transferts de données entre cœurs se font par l'intermédiaire des caches, ce qui fait que l'implémentation est différente de celle utilisée sur les systèmes multi-processeurs. Les standards pour les interconnexions entre coeurs sont assez proches de ceux utilisés pour interconnecter des processeurs, mais les standards utilisés ne sont pas les mêmes en pratique. Là encore, les cœurs sont connectés entre eux soit par un bus, soit par un réseau d'interconnexion. ===Le bus partagé entre plusieurs cœurs=== Une solution simple relie les cœurs entre eux grâce à un '''bus partagé'''. Le cas le plus simple est celui où chaque cœur dispose de caches dédiés, qui sont alors tous reliés au bus mémoire, qui sert d'intermédiaire. Mais l'idée doit être adaptée sur les processeurs multicœurs avec des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts dont nous parlerons en détail du bus partagé dans le chapitre sur la cohérence des caches. ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> svqvvgdz3rawzla0tvi61ou734niwxc 762848 762846 2026-04-03T19:19:22Z Mewtow 31375 /* Les systèmes multiprocesseur */ 762848 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs ordinateurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Si utiliser plusieurs ordinateurs connectés par un réseau local est une solution simple et facile à implémenter, elle est cependant assez onéreuse et demande des logiciels adaptés qui permettent de profiter d'une telle architecture matérielle. D'autres solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. ==Les systèmes multiprocesseur== Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Du moins, avant l'arrivée des processeurs multicœur, après 2005. Les systèmes multiprocesseur utilisent souvent des processeurs identiques. C'est la la solution la plus simple et la plus pratique, si on veut gagner en performance. Cependant, il existe des systèmes multiprocesseurs sur lequels les deux processeurs utilisés ne sont pas identiques. La différence est suffisamment importante pour qu'on ait mis un nom dessus. On parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. Le multiprocesseur asymétrique peut sembler assez contreintuitif, mais nous en avons rencontré précédemment dans ce cours ! Rappelez-vous quand nous avions parlé des co-processeurs, utilisés en complément du processeur principal, afin de le décharger de certains calculs. Il s'agissait formellement de multiprocesseur asymétrique. ===Le réseau d'interconnexion inter-processeur=== Les cartes mères multi-processeurs incorporent un réseau d'interconnexion pour connecter les processeurs à la mémoire RAM, au reste de l'ordinateur, et aussi pour les connecter entre eux. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les processeurs sont tous connectés en utilisant deux solutions : soit avec un bus, soit avec un réseau d'interconnexion très complexe. Une solution simple relie les processeurs/cœurs entre eux grâce au bus mémoire. Le réseau d'interconnexion est donc un bus, partagé avec tous les processeurs. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. Les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] De nos jours, les cartes mères pour serveur n'utilisent plus de bus partagé, mais utilisent un réseau d'interconnexion plus élaboré. Le réseau en question est un vrai réseau, qui peut être un réseau en anneau, un réseau crossbar, un réseau routé, etc. Le réseau d'interconnexion relie entre eux les cœurs, l'interface avec la mémoire RAM, le cache partagé L3/L4, mais aussi le bus PCI-Express, le GPU et quelques autres circuits séparés. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] Les systèmes multi-processeurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils remplacent le fameux ''front-side bus'' des tout premiers processeurs multicœurs. ===Les interruptions inter-processeurs=== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Un exemple d'implémentation très simple est celui de la console Néo-géo, qui contenait deux processeurs. Le premier est un Motorola 68000 qui sert de processeur principal, l'autre est un Z80 qui sert de processeur dédié à l'audio. Le Z80 commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des interruptions au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un '''''IO arbiter chip''''', un circuit sur la carte mère connecté au bus, qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80. Le code de 8 bits est envoyé au processeur Z80, en parallèle de l'interruption. : Il est possible de voir ce ''IO arbiter chip'' comme un contrôleur d'interruptions spécialisé dans les interruptions inter-processeur. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Les processeurs multicœurs== Avec les progrès de la miniaturisation des transistors, les fabricants de CPU sont passés aux '''processeurs multicœurs''', qui peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les CPU multicœurs connectent plusieurs cœurs entre eux, tout comme les systèmes multi-processeurs connectent plusieurs processeurs entre eux. Mais les transferts de données entre cœurs se font par l'intermédiaire des caches, ce qui fait que l'implémentation est différente de celle utilisée sur les systèmes multi-processeurs. Les standards pour les interconnexions entre coeurs sont assez proches de ceux utilisés pour interconnecter des processeurs, mais les standards utilisés ne sont pas les mêmes en pratique. Là encore, les cœurs sont connectés entre eux soit par un bus, soit par un réseau d'interconnexion. ===Le bus partagé entre plusieurs cœurs=== Une solution simple relie les cœurs entre eux grâce à un '''bus partagé'''. Le cas le plus simple est celui où chaque cœur dispose de caches dédiés, qui sont alors tous reliés au bus mémoire, qui sert d'intermédiaire. Mais l'idée doit être adaptée sur les processeurs multicœurs avec des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts dont nous parlerons en détail du bus partagé dans le chapitre sur la cohérence des caches. ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> 5oezz5q9fyek06g3lxnxhjp3yjdge54 762849 762848 2026-04-03T19:19:53Z Mewtow 31375 762849 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Si utiliser plusieurs ordinateurs connectés par un réseau local est une solution simple et facile à implémenter, elle est cependant assez onéreuse et demande des logiciels adaptés qui permettent de profiter d'une telle architecture matérielle. D'autres solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. ==Les systèmes multiprocesseur== Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Du moins, avant l'arrivée des processeurs multicœur, après 2005. Les systèmes multiprocesseur utilisent souvent des processeurs identiques. C'est la la solution la plus simple et la plus pratique, si on veut gagner en performance. Cependant, il existe des systèmes multiprocesseurs sur lequels les deux processeurs utilisés ne sont pas identiques. La différence est suffisamment importante pour qu'on ait mis un nom dessus. On parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. Le multiprocesseur asymétrique peut sembler assez contreintuitif, mais nous en avons rencontré précédemment dans ce cours ! Rappelez-vous quand nous avions parlé des co-processeurs, utilisés en complément du processeur principal, afin de le décharger de certains calculs. Il s'agissait formellement de multiprocesseur asymétrique. ===Le réseau d'interconnexion inter-processeur=== Les cartes mères multi-processeurs incorporent un réseau d'interconnexion pour connecter les processeurs à la mémoire RAM, au reste de l'ordinateur, et aussi pour les connecter entre eux. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les processeurs sont tous connectés en utilisant deux solutions : soit avec un bus, soit avec un réseau d'interconnexion très complexe. Une solution simple relie les processeurs/cœurs entre eux grâce au bus mémoire. Le réseau d'interconnexion est donc un bus, partagé avec tous les processeurs. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. Les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] De nos jours, les cartes mères pour serveur n'utilisent plus de bus partagé, mais utilisent un réseau d'interconnexion plus élaboré. Le réseau en question est un vrai réseau, qui peut être un réseau en anneau, un réseau crossbar, un réseau routé, etc. Le réseau d'interconnexion relie entre eux les cœurs, l'interface avec la mémoire RAM, le cache partagé L3/L4, mais aussi le bus PCI-Express, le GPU et quelques autres circuits séparés. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] Les systèmes multi-processeurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils remplacent le fameux ''front-side bus'' des tout premiers processeurs multicœurs. ===Les interruptions inter-processeurs=== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Un exemple d'implémentation très simple est celui de la console Néo-géo, qui contenait deux processeurs. Le premier est un Motorola 68000 qui sert de processeur principal, l'autre est un Z80 qui sert de processeur dédié à l'audio. Le Z80 commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des interruptions au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un '''''IO arbiter chip''''', un circuit sur la carte mère connecté au bus, qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80. Le code de 8 bits est envoyé au processeur Z80, en parallèle de l'interruption. : Il est possible de voir ce ''IO arbiter chip'' comme un contrôleur d'interruptions spécialisé dans les interruptions inter-processeur. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Les processeurs multicœurs== Avec les progrès de la miniaturisation des transistors, les fabricants de CPU sont passés aux '''processeurs multicœurs''', qui peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les CPU multicœurs connectent plusieurs cœurs entre eux, tout comme les systèmes multi-processeurs connectent plusieurs processeurs entre eux. Mais les transferts de données entre cœurs se font par l'intermédiaire des caches, ce qui fait que l'implémentation est différente de celle utilisée sur les systèmes multi-processeurs. Les standards pour les interconnexions entre coeurs sont assez proches de ceux utilisés pour interconnecter des processeurs, mais les standards utilisés ne sont pas les mêmes en pratique. Là encore, les cœurs sont connectés entre eux soit par un bus, soit par un réseau d'interconnexion. ===Le bus partagé entre plusieurs cœurs=== Une solution simple relie les cœurs entre eux grâce à un '''bus partagé'''. Le cas le plus simple est celui où chaque cœur dispose de caches dédiés, qui sont alors tous reliés au bus mémoire, qui sert d'intermédiaire. Mais l'idée doit être adaptée sur les processeurs multicœurs avec des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts dont nous parlerons en détail du bus partagé dans le chapitre sur la cohérence des caches. ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> immhdaiaq3fq3k2efa4vlyd9fmpmpaq 762850 762849 2026-04-03T19:21:04Z Mewtow 31375 762850 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. ==Les systèmes multiprocesseur== Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Du moins, avant l'arrivée des processeurs multicœur, après 2005. Les systèmes multiprocesseur utilisent souvent des processeurs identiques. C'est la la solution la plus simple et la plus pratique, si on veut gagner en performance. Cependant, il existe des systèmes multiprocesseurs sur lequels les deux processeurs utilisés ne sont pas identiques. La différence est suffisamment importante pour qu'on ait mis un nom dessus. On parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. Le multiprocesseur asymétrique peut sembler assez contreintuitif, mais nous en avons rencontré précédemment dans ce cours ! Rappelez-vous quand nous avions parlé des co-processeurs, utilisés en complément du processeur principal, afin de le décharger de certains calculs. Il s'agissait formellement de multiprocesseur asymétrique. ===Le réseau d'interconnexion inter-processeur=== Les cartes mères multi-processeurs incorporent un réseau d'interconnexion pour connecter les processeurs à la mémoire RAM, au reste de l'ordinateur, et aussi pour les connecter entre eux. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les processeurs sont tous connectés en utilisant deux solutions : soit avec un bus, soit avec un réseau d'interconnexion très complexe. Une solution simple relie les processeurs/cœurs entre eux grâce au bus mémoire. Le réseau d'interconnexion est donc un bus, partagé avec tous les processeurs. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. Les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] De nos jours, les cartes mères pour serveur n'utilisent plus de bus partagé, mais utilisent un réseau d'interconnexion plus élaboré. Le réseau en question est un vrai réseau, qui peut être un réseau en anneau, un réseau crossbar, un réseau routé, etc. Le réseau d'interconnexion relie entre eux les cœurs, l'interface avec la mémoire RAM, le cache partagé L3/L4, mais aussi le bus PCI-Express, le GPU et quelques autres circuits séparés. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] Les systèmes multi-processeurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils remplacent le fameux ''front-side bus'' des tout premiers processeurs multicœurs. ===Les interruptions inter-processeurs=== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Un exemple d'implémentation très simple est celui de la console Néo-géo, qui contenait deux processeurs. Le premier est un Motorola 68000 qui sert de processeur principal, l'autre est un Z80 qui sert de processeur dédié à l'audio. Le Z80 commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des interruptions au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un '''''IO arbiter chip''''', un circuit sur la carte mère connecté au bus, qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80. Le code de 8 bits est envoyé au processeur Z80, en parallèle de l'interruption. : Il est possible de voir ce ''IO arbiter chip'' comme un contrôleur d'interruptions spécialisé dans les interruptions inter-processeur. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Les processeurs multicœurs== Avec les progrès de la miniaturisation des transistors, les fabricants de CPU sont passés aux '''processeurs multicœurs''', qui peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les CPU multicœurs connectent plusieurs cœurs entre eux, tout comme les systèmes multi-processeurs connectent plusieurs processeurs entre eux. Mais les transferts de données entre cœurs se font par l'intermédiaire des caches, ce qui fait que l'implémentation est différente de celle utilisée sur les systèmes multi-processeurs. Les standards pour les interconnexions entre coeurs sont assez proches de ceux utilisés pour interconnecter des processeurs, mais les standards utilisés ne sont pas les mêmes en pratique. Là encore, les cœurs sont connectés entre eux soit par un bus, soit par un réseau d'interconnexion. ===Le bus partagé entre plusieurs cœurs=== Une solution simple relie les cœurs entre eux grâce à un '''bus partagé'''. Le cas le plus simple est celui où chaque cœur dispose de caches dédiés, qui sont alors tous reliés au bus mémoire, qui sert d'intermédiaire. Mais l'idée doit être adaptée sur les processeurs multicœurs avec des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts dont nous parlerons en détail du bus partagé dans le chapitre sur la cohérence des caches. ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> 0bx7alvchwy8vefa1kzrs3wnqjhh3i0 762851 762850 2026-04-03T19:26:13Z Mewtow 31375 /* Le réseau d'interconnexion inter-processeur */ 762851 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. ==Les systèmes multiprocesseur== Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Du moins, avant l'arrivée des processeurs multicœur, après 2005. Les systèmes multiprocesseur utilisent souvent des processeurs identiques. C'est la la solution la plus simple et la plus pratique, si on veut gagner en performance. Cependant, il existe des systèmes multiprocesseurs sur lequels les deux processeurs utilisés ne sont pas identiques. La différence est suffisamment importante pour qu'on ait mis un nom dessus. On parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. Le multiprocesseur asymétrique peut sembler assez contreintuitif, mais nous en avons rencontré précédemment dans ce cours ! Rappelez-vous quand nous avions parlé des co-processeurs, utilisés en complément du processeur principal, afin de le décharger de certains calculs. Il s'agissait formellement de multiprocesseur asymétrique. ===Le réseau d'interconnexion inter-processeur=== Les cartes mères multi-processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Le réseau d'interconnexion relie entre eux les cœurs, la mémoire RAM, et potentiellement d'autres circuits. Le réseau va d'un simple bus partagé à un réseau d'interconnexions très complexe. Les systèmes multi-processeurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] Pour un faible nombre de processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] ===Les interruptions inter-processeurs=== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Un exemple d'implémentation très simple est celui de la console Néo-géo, qui contenait deux processeurs. Le premier est un Motorola 68000 qui sert de processeur principal, l'autre est un Z80 qui sert de processeur dédié à l'audio. Le Z80 commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des interruptions au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un '''''IO arbiter chip''''', un circuit sur la carte mère connecté au bus, qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80. Le code de 8 bits est envoyé au processeur Z80, en parallèle de l'interruption. : Il est possible de voir ce ''IO arbiter chip'' comme un contrôleur d'interruptions spécialisé dans les interruptions inter-processeur. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Les processeurs multicœurs== Avec les progrès de la miniaturisation des transistors, les fabricants de CPU sont passés aux '''processeurs multicœurs''', qui peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les CPU multicœurs connectent plusieurs cœurs entre eux, tout comme les systèmes multi-processeurs connectent plusieurs processeurs entre eux. Mais les transferts de données entre cœurs se font par l'intermédiaire des caches, ce qui fait que l'implémentation est différente de celle utilisée sur les systèmes multi-processeurs. Les standards pour les interconnexions entre coeurs sont assez proches de ceux utilisés pour interconnecter des processeurs, mais les standards utilisés ne sont pas les mêmes en pratique. Là encore, les cœurs sont connectés entre eux soit par un bus, soit par un réseau d'interconnexion. ===Le bus partagé entre plusieurs cœurs=== Une solution simple relie les cœurs entre eux grâce à un '''bus partagé'''. Le cas le plus simple est celui où chaque cœur dispose de caches dédiés, qui sont alors tous reliés au bus mémoire, qui sert d'intermédiaire. Mais l'idée doit être adaptée sur les processeurs multicœurs avec des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts dont nous parlerons en détail du bus partagé dans le chapitre sur la cohérence des caches. ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> ljff8vuvda2xjvcvturfjccsmvc5qbc 762852 762851 2026-04-03T19:30:03Z Mewtow 31375 762852 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. ==Les systèmes multiprocesseur== Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Du moins, avant l'arrivée des processeurs multicœur, après 2005. ===Le réseau d'interconnexion inter-processeur=== Les cartes mères multi-processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Le réseau d'interconnexion relie entre eux les cœurs, la mémoire RAM, et potentiellement d'autres circuits. Le réseau va d'un simple bus partagé à un réseau d'interconnexions très complexe. Les systèmes multi-processeurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] Pour un faible nombre de processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] ===Les interruptions inter-processeurs=== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Un exemple d'implémentation très simple est celui de la console Néo-géo, qui contenait deux processeurs. Le premier est un Motorola 68000 qui sert de processeur principal, l'autre est un Z80 qui sert de processeur dédié à l'audio. Le Z80 commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des interruptions au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un '''''IO arbiter chip''''', un circuit sur la carte mère connecté au bus, qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80. Le code de 8 bits est envoyé au processeur Z80, en parallèle de l'interruption. : Il est possible de voir ce ''IO arbiter chip'' comme un contrôleur d'interruptions spécialisé dans les interruptions inter-processeur. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Les processeurs multicœurs== Avec les progrès de la miniaturisation des transistors, les fabricants de CPU sont passés aux '''processeurs multicœurs''', qui peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les CPU multicœurs connectent plusieurs cœurs entre eux, tout comme les systèmes multi-processeurs connectent plusieurs processeurs entre eux. Mais les transferts de données entre cœurs se font par l'intermédiaire des caches, ce qui fait que l'implémentation est différente de celle utilisée sur les systèmes multi-processeurs. Les standards pour les interconnexions entre coeurs sont assez proches de ceux utilisés pour interconnecter des processeurs, mais les standards utilisés ne sont pas les mêmes en pratique. Là encore, les cœurs sont connectés entre eux soit par un bus, soit par un réseau d'interconnexion. ===Le bus partagé entre plusieurs cœurs=== Une solution simple relie les cœurs entre eux grâce à un '''bus partagé'''. Le cas le plus simple est celui où chaque cœur dispose de caches dédiés, qui sont alors tous reliés au bus mémoire, qui sert d'intermédiaire. Mais l'idée doit être adaptée sur les processeurs multicœurs avec des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts dont nous parlerons en détail du bus partagé dans le chapitre sur la cohérence des caches. ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Le multiprocesseur/multicœur asymétrique : les co-processeur== Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. L'usage de multiprocesseur asymétrique peut sembler assez contreintuitif, mais nous en avons rencontré précédemment dans ce cours ! Rappelez-vous quand nous avions parlé des co-processeurs, utilisés en complément du processeur principal, afin de le décharger de certains calculs. Il s'agissait formellement de multiprocesseur asymétrique. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> 4aq0xdf6864otremo4sun1rw0qewrvm 762853 762852 2026-04-03T19:34:22Z Mewtow 31375 762853 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Puis, en 2005, les '''processeurs multicœurs''' sont arrivés. Ils peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. ==Les interruptions inter-processeurs== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Un exemple d'implémentation très simple est celui de la console Néo-géo, qui contenait deux processeurs. Le premier est un Motorola 68000 qui sert de processeur principal, l'autre est un Z80 qui sert de processeur dédié à l'audio. Le Z80 commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des interruptions au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un '''''IO arbiter chip''''', un circuit sur la carte mère connecté au bus, qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80. Le code de 8 bits est envoyé au processeur Z80, en parallèle de l'interruption. : Il est possible de voir ce ''IO arbiter chip'' comme un contrôleur d'interruptions spécialisé dans les interruptions inter-processeur. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les systèmes avec plusieurs processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les CPU multicœurs ont aussi un tel réseau d'interconnexion, pour relier les cœurs entre eux. La différence est que le réseau d'interconnexion est placé dans le processeur, pas sur la carte mère. Mais les transferts de données entre cœurs se font par l'intermédiaire des caches partagés, ce qui fait que l'implémentation est différente de celle utilisée sur les systèmes multi-processeurs. Les systèmes multi-cœurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils sont aussi utilisés pour faire communiquer entre eux plusieurs processeurs. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] ===Le bus partagé entre plusieurs cœurs=== Pour un faible nombre de coeurs/processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] Une solution simple relie les cœurs entre eux grâce à un '''bus partagé'''. Le cas le plus simple est celui où chaque cœur dispose de caches dédiés, qui sont alors tous reliés au bus mémoire, qui sert d'intermédiaire. Mais l'idée doit être adaptée sur les processeurs multicœurs avec des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts dont nous parlerons en détail du bus partagé dans le chapitre sur la cohérence des caches. ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Le multiprocesseur/multicœur asymétrique : les co-processeur== Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. L'usage de multiprocesseur asymétrique peut sembler assez contreintuitif, mais nous en avons rencontré précédemment dans ce cours ! Rappelez-vous quand nous avions parlé des co-processeurs, utilisés en complément du processeur principal, afin de le décharger de certains calculs. Il s'agissait formellement de multiprocesseur asymétrique. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> 2hh3t7cq4i28dde2jpe3h9fjw4b8ock 762854 762853 2026-04-03T19:34:51Z Mewtow 31375 /* Le bus partagé entre plusieurs cœurs */ 762854 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Puis, en 2005, les '''processeurs multicœurs''' sont arrivés. Ils peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. ==Les interruptions inter-processeurs== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Un exemple d'implémentation très simple est celui de la console Néo-géo, qui contenait deux processeurs. Le premier est un Motorola 68000 qui sert de processeur principal, l'autre est un Z80 qui sert de processeur dédié à l'audio. Le Z80 commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des interruptions au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un '''''IO arbiter chip''''', un circuit sur la carte mère connecté au bus, qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80. Le code de 8 bits est envoyé au processeur Z80, en parallèle de l'interruption. : Il est possible de voir ce ''IO arbiter chip'' comme un contrôleur d'interruptions spécialisé dans les interruptions inter-processeur. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les systèmes avec plusieurs processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les CPU multicœurs ont aussi un tel réseau d'interconnexion, pour relier les cœurs entre eux. La différence est que le réseau d'interconnexion est placé dans le processeur, pas sur la carte mère. Mais les transferts de données entre cœurs se font par l'intermédiaire des caches partagés, ce qui fait que l'implémentation est différente de celle utilisée sur les systèmes multi-processeurs. Les systèmes multi-cœurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils sont aussi utilisés pour faire communiquer entre eux plusieurs processeurs. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] ===Le bus partagé entre plusieurs cœurs=== Pour un faible nombre de coeurs/processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] Pour les systèmes multicoeurs, par présence de caches partagés modifie le réseau d'interconnexion. Le cas le plus simple est celui où chaque cœur dispose de caches dédiés, qui sont alors tous reliés au bus mémoire, qui sert d'intermédiaire. Mais l'idée doit être adaptée sur les processeurs multicœurs avec des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts dont nous parlerons en détail du bus partagé dans le chapitre sur la cohérence des caches. ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Le multiprocesseur/multicœur asymétrique : les co-processeur== Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. L'usage de multiprocesseur asymétrique peut sembler assez contreintuitif, mais nous en avons rencontré précédemment dans ce cours ! Rappelez-vous quand nous avions parlé des co-processeurs, utilisés en complément du processeur principal, afin de le décharger de certains calculs. Il s'agissait formellement de multiprocesseur asymétrique. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> 91r2i314iro74oxbhtw0v4c5dvn5k1g 762855 762854 2026-04-03T19:36:06Z Mewtow 31375 /* Le réseau d'interconnexion entre cœurs */ 762855 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Puis, en 2005, les '''processeurs multicœurs''' sont arrivés. Ils peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. ==Les interruptions inter-processeurs== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Un exemple d'implémentation très simple est celui de la console Néo-géo, qui contenait deux processeurs. Le premier est un Motorola 68000 qui sert de processeur principal, l'autre est un Z80 qui sert de processeur dédié à l'audio. Le Z80 commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des interruptions au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un '''''IO arbiter chip''''', un circuit sur la carte mère connecté au bus, qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80. Le code de 8 bits est envoyé au processeur Z80, en parallèle de l'interruption. : Il est possible de voir ce ''IO arbiter chip'' comme un contrôleur d'interruptions spécialisé dans les interruptions inter-processeur. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les systèmes avec plusieurs processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les CPU multicœurs ont aussi un tel réseau d'interconnexion, pour relier les cœurs entre eux. La différence est que le réseau d'interconnexion est placé dans le processeur, pas sur la carte mère. Les systèmes multi-cœurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils sont aussi utilisés pour faire communiquer entre eux plusieurs processeurs. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] ===Le bus partagé entre plusieurs cœurs=== Pour un faible nombre de coeurs/processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] Pour les systèmes multicœurs, l'usage d'un bus partagé doit être adaptée pour tenir compte des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts dont nous parlerons en détail du bus partagé dans le chapitre sur la cohérence des caches. ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Le multiprocesseur/multicœur asymétrique : les co-processeur== Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. L'usage de multiprocesseur asymétrique peut sembler assez contreintuitif, mais nous en avons rencontré précédemment dans ce cours ! Rappelez-vous quand nous avions parlé des co-processeurs, utilisés en complément du processeur principal, afin de le décharger de certains calculs. Il s'agissait formellement de multiprocesseur asymétrique. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> 0460p16kmlgldqizxesy30wwljkk6nq 762856 762855 2026-04-03T19:37:10Z Mewtow 31375 /* Le bus partagé entre plusieurs cœurs */ 762856 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Puis, en 2005, les '''processeurs multicœurs''' sont arrivés. Ils peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. ==Les interruptions inter-processeurs== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Un exemple d'implémentation très simple est celui de la console Néo-géo, qui contenait deux processeurs. Le premier est un Motorola 68000 qui sert de processeur principal, l'autre est un Z80 qui sert de processeur dédié à l'audio. Le Z80 commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des interruptions au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un '''''IO arbiter chip''''', un circuit sur la carte mère connecté au bus, qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80. Le code de 8 bits est envoyé au processeur Z80, en parallèle de l'interruption. : Il est possible de voir ce ''IO arbiter chip'' comme un contrôleur d'interruptions spécialisé dans les interruptions inter-processeur. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les systèmes avec plusieurs processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les CPU multicœurs ont aussi un tel réseau d'interconnexion, pour relier les cœurs entre eux. La différence est que le réseau d'interconnexion est placé dans le processeur, pas sur la carte mère. Les systèmes multi-cœurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils sont aussi utilisés pour faire communiquer entre eux plusieurs processeurs. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] ===Le bus partagé entre plusieurs cœurs=== Pour un faible nombre de coeurs/processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Pour les systèmes multicœurs, l'usage d'un bus partagé doit être adaptée pour tenir compte des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts. Par exemple, les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. D'autres défauts très importants seront abordés en détail dans le chapitre sur la cohérence des caches [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Le multiprocesseur/multicœur asymétrique : les co-processeur== Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. L'usage de multiprocesseur asymétrique peut sembler assez contreintuitif, mais nous en avons rencontré précédemment dans ce cours ! Rappelez-vous quand nous avions parlé des co-processeurs, utilisés en complément du processeur principal, afin de le décharger de certains calculs. Il s'agissait formellement de multiprocesseur asymétrique. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> 0rm78y8fqo2ve0rqtm0qb6n6l9g37xu 762857 762856 2026-04-03T19:38:20Z Mewtow 31375 762857 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Puis, en 2005, les '''processeurs multicœurs''' sont arrivés. Ils peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. : Dans ce qui suit, nous utiliserons les termes "processeurs" et "cœurs" comme s'ils étaient synonymes. Tout ce qui vaut pour les systèmes multiprocesseur vaut aussi pour les systèmes multicœurs. ==Les interruptions inter-processeurs== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Un exemple d'implémentation très simple est celui de la console Néo-géo, qui contenait deux processeurs. Le premier est un Motorola 68000 qui sert de processeur principal, l'autre est un Z80 qui sert de processeur dédié à l'audio. Le Z80 commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des interruptions au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un '''''IO arbiter chip''''', un circuit sur la carte mère connecté au bus, qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80. Le code de 8 bits est envoyé au processeur Z80, en parallèle de l'interruption. : Il est possible de voir ce ''IO arbiter chip'' comme un contrôleur d'interruptions spécialisé dans les interruptions inter-processeur. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les systèmes avec plusieurs processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les CPU multicœurs ont aussi un tel réseau d'interconnexion, pour relier les cœurs entre eux. La différence est que le réseau d'interconnexion est placé dans le processeur, pas sur la carte mère. Les systèmes multi-cœurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils sont aussi utilisés pour faire communiquer entre eux plusieurs processeurs. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] ===Le bus partagé entre plusieurs cœurs=== Pour un faible nombre de coeurs/processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Pour les systèmes multicœurs, l'usage d'un bus partagé doit être adaptée pour tenir compte des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts. Par exemple, les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. D'autres défauts très importants seront abordés en détail dans le chapitre sur la cohérence des caches [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Le multiprocesseur/multicœur asymétrique : les co-processeur== Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. L'usage de multiprocesseur asymétrique peut sembler assez contreintuitif, mais nous en avons rencontré précédemment dans ce cours ! Rappelez-vous quand nous avions parlé des co-processeurs, utilisés en complément du processeur principal, afin de le décharger de certains calculs. Il s'agissait formellement de multiprocesseur asymétrique. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> 1gei1z2mmmmw8r5413jv1n9dec60gvg 762858 762857 2026-04-03T19:42:40Z Mewtow 31375 /* Les interruptions inter-processeurs */ 762858 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Puis, en 2005, les '''processeurs multicœurs''' sont arrivés. Ils peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. : Dans ce qui suit, nous utiliserons les termes "processeurs" et "cœurs" comme s'ils étaient synonymes. Tout ce qui vaut pour les systèmes multiprocesseur vaut aussi pour les systèmes multicœurs. ==Les interruptions inter-processeurs== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les systèmes avec plusieurs processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les CPU multicœurs ont aussi un tel réseau d'interconnexion, pour relier les cœurs entre eux. La différence est que le réseau d'interconnexion est placé dans le processeur, pas sur la carte mère. Les systèmes multi-cœurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils sont aussi utilisés pour faire communiquer entre eux plusieurs processeurs. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] ===Le bus partagé entre plusieurs cœurs=== Pour un faible nombre de coeurs/processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Pour les systèmes multicœurs, l'usage d'un bus partagé doit être adaptée pour tenir compte des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts. Par exemple, les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. D'autres défauts très importants seront abordés en détail dans le chapitre sur la cohérence des caches [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Le multiprocesseur/multicœur asymétrique : les co-processeur== Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. L'usage de multiprocesseur asymétrique peut sembler assez contreintuitif, mais nous en avons rencontré précédemment dans ce cours ! Rappelez-vous quand nous avions parlé des co-processeurs, utilisés en complément du processeur principal, afin de le décharger de certains calculs. Il s'agissait formellement de multiprocesseur asymétrique. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> ismv630k40ix8ylmydrvn93bsws088j 762860 762858 2026-04-03T19:44:11Z Mewtow 31375 /* Le multiprocesseur/multicœur asymétrique : les co-processeur */ 762860 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Puis, en 2005, les '''processeurs multicœurs''' sont arrivés. Ils peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. : Dans ce qui suit, nous utiliserons les termes "processeurs" et "cœurs" comme s'ils étaient synonymes. Tout ce qui vaut pour les systèmes multiprocesseur vaut aussi pour les systèmes multicœurs. ==Les interruptions inter-processeurs== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les systèmes avec plusieurs processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les CPU multicœurs ont aussi un tel réseau d'interconnexion, pour relier les cœurs entre eux. La différence est que le réseau d'interconnexion est placé dans le processeur, pas sur la carte mère. Les systèmes multi-cœurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils sont aussi utilisés pour faire communiquer entre eux plusieurs processeurs. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] ===Le bus partagé entre plusieurs cœurs=== Pour un faible nombre de coeurs/processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Pour les systèmes multicœurs, l'usage d'un bus partagé doit être adaptée pour tenir compte des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts. Par exemple, les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. D'autres défauts très importants seront abordés en détail dans le chapitre sur la cohérence des caches [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Le multiprocesseur/multicœur asymétrique : les co-processeur== Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. L'usage de multiprocesseur asymétrique peut sembler assez contreintuitif, mais nous en avons rencontré précédemment dans ce cours ! Rappelez-vous quand nous avions parlé des co-processeurs, utilisés en complément du processeur principal, afin de le décharger de certains calculs. Il s'agissait formellement de multiprocesseur asymétrique. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> ioacgu8lik04e5fyvks54viinss6ixx 762861 762860 2026-04-03T19:45:49Z Mewtow 31375 /* Le multiprocesseur/multicœur asymétrique : les co-processeur */ 762861 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Puis, en 2005, les '''processeurs multicœurs''' sont arrivés. Ils peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. : Dans ce qui suit, nous utiliserons les termes "processeurs" et "cœurs" comme s'ils étaient synonymes. Tout ce qui vaut pour les systèmes multiprocesseur vaut aussi pour les systèmes multicœurs. ==Les interruptions inter-processeurs== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les systèmes avec plusieurs processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les CPU multicœurs ont aussi un tel réseau d'interconnexion, pour relier les cœurs entre eux. La différence est que le réseau d'interconnexion est placé dans le processeur, pas sur la carte mère. Les systèmes multi-cœurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils sont aussi utilisés pour faire communiquer entre eux plusieurs processeurs. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] ===Le bus partagé entre plusieurs cœurs=== Pour un faible nombre de coeurs/processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Pour les systèmes multicœurs, l'usage d'un bus partagé doit être adaptée pour tenir compte des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts. Par exemple, les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. D'autres défauts très importants seront abordés en détail dans le chapitre sur la cohérence des caches [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Le multiprocesseur/multicœur asymétrique : les co-processeur== Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. L'usage de multiprocesseur asymétrique peut sembler assez contreintuitif, mais nous en avons rencontré précédemment dans ce cours ! Rappelez-vous quand nous avions parlé des co-processeurs, utilisés en complément du processeur principal, afin de le décharger de certains calculs. Il s'agissait formellement de multiprocesseur asymétrique. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le Z80 est utilisé comme co-processeur audio. Il commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. [[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]] Un exemple d'implémentation très simple est celui de la console Néo-géo, qui contenait deux processeurs. Le premier est un Motorola 68000 qui sert de processeur principal, l'autre est un Z80 qui sert de processeur dédié à l'audio. Le Z80 commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des interruptions au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', un circuit sur la carte mère connecté au bus, qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80. Le code de 8 bits est envoyé au processeur Z80, en parallèle de l'interruption. : Il est possible de voir ce ''IO arbiter chip'' comme un contrôleur d'interruptions spécialisé dans les interruptions inter-processeur. ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> 9lzfd02mnlgpvjujs2imvtm1t7jtwqb 762862 762861 2026-04-03T19:47:36Z Mewtow 31375 /* Le multiprocesseur/multicœur asymétrique : les co-processeur */ 762862 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Puis, en 2005, les '''processeurs multicœurs''' sont arrivés. Ils peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. : Dans ce qui suit, nous utiliserons les termes "processeurs" et "cœurs" comme s'ils étaient synonymes. Tout ce qui vaut pour les systèmes multiprocesseur vaut aussi pour les systèmes multicœurs. ==Les interruptions inter-processeurs== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les systèmes avec plusieurs processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les CPU multicœurs ont aussi un tel réseau d'interconnexion, pour relier les cœurs entre eux. La différence est que le réseau d'interconnexion est placé dans le processeur, pas sur la carte mère. Les systèmes multi-cœurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils sont aussi utilisés pour faire communiquer entre eux plusieurs processeurs. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] ===Le bus partagé entre plusieurs cœurs=== Pour un faible nombre de coeurs/processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Pour les systèmes multicœurs, l'usage d'un bus partagé doit être adaptée pour tenir compte des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts. Par exemple, les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. D'autres défauts très importants seront abordés en détail dans le chapitre sur la cohérence des caches [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Le multiprocesseur/multicœur asymétrique : les co-processeur== Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. L'usage de multiprocesseur asymétrique peut sembler assez contreintuitif, mais nous en avons rencontré précédemment dans ce cours ! Rappelez-vous quand nous avions parlé des co-processeurs, utilisés en complément du processeur principal, afin de le décharger de certains calculs. Il s'agissait formellement de multiprocesseur asymétrique. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le Z80 est utilisé comme co-processeur audio. Il commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. [[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]] Un exemple d'implémentation très simple est celui de la console Néo-géo, qui contenait deux processeurs. Le premier est un Motorola 68000 qui sert de processeur principal, l'autre est un Z80 qui sert de processeur dédié à l'audio. Le Z80 commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des interruptions au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', un circuit sur la carte mère connecté au bus, qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80. Le code de 8 bits est envoyé au processeur Z80, en parallèle de l'interruption. : Il est possible de voir ce ''IO arbiter chip'' comme un contrôleur d'interruptions spécialisé dans les interruptions inter-processeur. Maintenant que nous venons de voir différents types de coprocesseurs, passons maintenant aux généralités sur ceux-ci. Le CPU peut soit exécuter des programmes en parallèle du coprocesseur, soit se mettre en pause en attendant que le coprocesseur finisse son travail. Dans l'exemple des coprocesseurs arithmétiques, le processeur principal passe la main au coprocesseur et attend sagement qu'il finisse son travail. Les deux processeurs se passent donc la main pour exécuter un programme unique. On parle alors de '''coprocesseurs fortement couplés'''. Pour les autres coprocesseurs, le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On a un programme qui s’exécute sur le coprocesseur, un autre qui s’exécute sur le CPU. On parle alors de '''coprocesseurs faiblement couplés'''. C'est le cas pour les coprocesseurs d'accès au périphérique, pour ceux de rendu 2D/3D, etc. ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> lr5ciighauh3h8we6zn3k7npn93gglw 762863 762862 2026-04-03T19:50:59Z Mewtow 31375 /* Le multiprocesseur/multicœur asymétrique : les co-processeur */ 762863 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Puis, en 2005, les '''processeurs multicœurs''' sont arrivés. Ils peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. : Dans ce qui suit, nous utiliserons les termes "processeurs" et "cœurs" comme s'ils étaient synonymes. Tout ce qui vaut pour les systèmes multiprocesseur vaut aussi pour les systèmes multicœurs. ==Les interruptions inter-processeurs== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les systèmes avec plusieurs processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les CPU multicœurs ont aussi un tel réseau d'interconnexion, pour relier les cœurs entre eux. La différence est que le réseau d'interconnexion est placé dans le processeur, pas sur la carte mère. Les systèmes multi-cœurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils sont aussi utilisés pour faire communiquer entre eux plusieurs processeurs. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] ===Le bus partagé entre plusieurs cœurs=== Pour un faible nombre de coeurs/processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Pour les systèmes multicœurs, l'usage d'un bus partagé doit être adaptée pour tenir compte des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts. Par exemple, les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. D'autres défauts très importants seront abordés en détail dans le chapitre sur la cohérence des caches [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Le multiprocesseur/multicœur asymétrique : les co-processeur== Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. L'usage de multiprocesseur asymétrique peut sembler assez contreintuitif, mais nous en avons rencontré précédemment dans ce cours ! Rappelez-vous quand nous avions parlé des co-processeurs, utilisés en complément du processeur principal, afin de le décharger de certains calculs. Il s'agissait formellement de multiprocesseur asymétrique. Précisons cependant qu'il existe deux types de co-processeurs. Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Mais il existe une seconde solution, où il n'y a qu'un seul programme à exécuter. Ce dernier contient des instructions à destination du CPU, d'autres à destination du co-processeur. Les instructions sont exécutées soit par le CPU, soit par le co-processeur, une par une. En clair, le CPU et le co-processeur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''. Pour résumer, le CPU peut soit exécuter des programmes en parallèle du coprocesseur, soit les deux se passent la main à tour de rôle. Le premier est un cas de système parallèle, pas le second. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le Z80 est utilisé comme co-processeur audio. Il commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. [[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]] Un exemple d'implémentation très simple est celui de la console Néo-géo, qui contenait deux processeurs. Le premier est un Motorola 68000 qui sert de processeur principal, l'autre est un Z80 qui sert de processeur dédié à l'audio. Le Z80 commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des interruptions au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', un circuit sur la carte mère connecté au bus, qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80. Le code de 8 bits est envoyé au processeur Z80, en parallèle de l'interruption. : Il est possible de voir ce ''IO arbiter chip'' comme un contrôleur d'interruptions spécialisé dans les interruptions inter-processeur. ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> l6opqr4wcmj89kuvuzipg8had8igics 762864 762863 2026-04-03T19:51:13Z Mewtow 31375 /* Le multiprocesseur/multicœur asymétrique : les co-processeur */ 762864 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Puis, en 2005, les '''processeurs multicœurs''' sont arrivés. Ils peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. : Dans ce qui suit, nous utiliserons les termes "processeurs" et "cœurs" comme s'ils étaient synonymes. Tout ce qui vaut pour les systèmes multiprocesseur vaut aussi pour les systèmes multicœurs. ==Les interruptions inter-processeurs== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les systèmes avec plusieurs processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les CPU multicœurs ont aussi un tel réseau d'interconnexion, pour relier les cœurs entre eux. La différence est que le réseau d'interconnexion est placé dans le processeur, pas sur la carte mère. Les systèmes multi-cœurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils sont aussi utilisés pour faire communiquer entre eux plusieurs processeurs. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] ===Le bus partagé entre plusieurs cœurs=== Pour un faible nombre de coeurs/processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Pour les systèmes multicœurs, l'usage d'un bus partagé doit être adaptée pour tenir compte des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts. Par exemple, les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. D'autres défauts très importants seront abordés en détail dans le chapitre sur la cohérence des caches [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Le multiprocesseur/multicœur asymétrique : les co-processeurs== Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. L'usage de multiprocesseur asymétrique peut sembler assez contreintuitif, mais nous en avons rencontré précédemment dans ce cours ! Rappelez-vous quand nous avions parlé des co-processeurs, utilisés en complément du processeur principal, afin de le décharger de certains calculs. Il s'agissait formellement de multiprocesseur asymétrique. Précisons cependant qu'il existe deux types de co-processeurs. Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Mais il existe une seconde solution, où il n'y a qu'un seul programme à exécuter. Ce dernier contient des instructions à destination du CPU, d'autres à destination du co-processeur. Les instructions sont exécutées soit par le CPU, soit par le co-processeur, une par une. En clair, le CPU et le co-processeur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''. Pour résumer, le CPU peut soit exécuter des programmes en parallèle du coprocesseur, soit les deux se passent la main à tour de rôle. Le premier est un cas de système parallèle, pas le second. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le Z80 est utilisé comme co-processeur audio. Il commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. [[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]] Un exemple d'implémentation très simple est celui de la console Néo-géo, qui contenait deux processeurs. Le premier est un Motorola 68000 qui sert de processeur principal, l'autre est un Z80 qui sert de processeur dédié à l'audio. Le Z80 commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des interruptions au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', un circuit sur la carte mère connecté au bus, qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80. Le code de 8 bits est envoyé au processeur Z80, en parallèle de l'interruption. : Il est possible de voir ce ''IO arbiter chip'' comme un contrôleur d'interruptions spécialisé dans les interruptions inter-processeur. ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> 70hjcuhhv0emcphnguwbjc5221ds0n3 762865 762864 2026-04-03T19:53:46Z Mewtow 31375 /* Le multiprocesseur/multicœur asymétrique : les co-processeurs */ 762865 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Puis, en 2005, les '''processeurs multicœurs''' sont arrivés. Ils peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. : Dans ce qui suit, nous utiliserons les termes "processeurs" et "cœurs" comme s'ils étaient synonymes. Tout ce qui vaut pour les systèmes multiprocesseur vaut aussi pour les systèmes multicœurs. ==Les interruptions inter-processeurs== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les systèmes avec plusieurs processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les CPU multicœurs ont aussi un tel réseau d'interconnexion, pour relier les cœurs entre eux. La différence est que le réseau d'interconnexion est placé dans le processeur, pas sur la carte mère. Les systèmes multi-cœurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils sont aussi utilisés pour faire communiquer entre eux plusieurs processeurs. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] ===Le bus partagé entre plusieurs cœurs=== Pour un faible nombre de coeurs/processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Pour les systèmes multicœurs, l'usage d'un bus partagé doit être adaptée pour tenir compte des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts. Par exemple, les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. D'autres défauts très importants seront abordés en détail dans le chapitre sur la cohérence des caches [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Le multiprocesseur/multicœur asymétrique : les co-processeurs== Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. L'usage de multiprocesseur asymétrique peut sembler assez contreintuitif, mais nous en avons rencontré précédemment dans ce cours, quand nous avions parlé des co-processeurs, utilisés en complément du processeur principal pour le décharger de certains calculs. Précisons cependant qu'il existe deux types de co-processeurs. Le premier est un cas de système parallèle, pas le second. Le CPU peut soit exécuter des programmes en parallèle du coprocesseur, soit les deux se passent la main à tour de rôle. Voyons cela rapidement. Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Mais il existe une seconde solution, où il n'y a qu'un seul programme à exécuter. Ce dernier contient des instructions à destination du CPU, d'autres à destination du co-processeur. Les instructions sont exécutées soit par le CPU, soit par le co-processeur, une par une. En clair, le CPU et le co-processeur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le Z80 est utilisé comme co-processeur audio. Il commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. [[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]] Un exemple d'implémentation très simple est celui de la console Néo-géo, qui contenait deux processeurs. Le premier est un Motorola 68000 qui sert de processeur principal, l'autre est un Z80 qui sert de processeur dédié à l'audio. Le Z80 commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des interruptions au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', un circuit sur la carte mère connecté au bus, qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80. Le code de 8 bits est envoyé au processeur Z80, en parallèle de l'interruption. : Il est possible de voir ce ''IO arbiter chip'' comme un contrôleur d'interruptions spécialisé dans les interruptions inter-processeur. ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> evafgt0k27whq8u8f1q21gni3ptyirx 762866 762865 2026-04-03T19:54:34Z Mewtow 31375 /* Le multiprocesseur/multicœur asymétrique : les co-processeurs */ 762866 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Puis, en 2005, les '''processeurs multicœurs''' sont arrivés. Ils peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. : Dans ce qui suit, nous utiliserons les termes "processeurs" et "cœurs" comme s'ils étaient synonymes. Tout ce qui vaut pour les systèmes multiprocesseur vaut aussi pour les systèmes multicœurs. ==Les interruptions inter-processeurs== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les systèmes avec plusieurs processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les CPU multicœurs ont aussi un tel réseau d'interconnexion, pour relier les cœurs entre eux. La différence est que le réseau d'interconnexion est placé dans le processeur, pas sur la carte mère. Les systèmes multi-cœurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils sont aussi utilisés pour faire communiquer entre eux plusieurs processeurs. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] ===Le bus partagé entre plusieurs cœurs=== Pour un faible nombre de coeurs/processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Pour les systèmes multicœurs, l'usage d'un bus partagé doit être adaptée pour tenir compte des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts. Par exemple, les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. D'autres défauts très importants seront abordés en détail dans le chapitre sur la cohérence des caches [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Le multiprocesseur/multicœur asymétrique : les co-processeurs== Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. L'usage de multiprocesseur asymétrique peut sembler assez contreintuitif, mais nous en avons rencontré précédemment dans ce cours, quand nous avions parlé des co-processeurs, utilisés en complément du processeur principal pour le décharger de certains calculs. Précisons cependant qu'il existe deux types de co-processeurs. Le premier est un cas de système parallèle, pas le second. Le CPU peut soit exécuter des programmes en parallèle du coprocesseur, soit les deux se passent la main à tour de rôle. Voyons cela rapidement. Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Mais il existe une seconde solution, où il n'y a qu'un seul programme à exécuter. Ce dernier contient des instructions à destination du CPU, d'autres à destination du co-processeur. Les instructions sont exécutées soit par le CPU, soit par le co-processeur, une par une. En clair, le CPU et le co-processeur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''. Un exemple de ces dernier est le cas des FPU x87 anciennes, quand elles étaient séparées du processeur. Mais parlons maintenant des coprocesseurs parallèles, faiblement couplés. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le Z80 est utilisé comme co-processeur audio. Il commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. [[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]] Un exemple d'implémentation très simple est celui de la console Néo-géo, qui contenait deux processeurs. Le premier est un Motorola 68000 qui sert de processeur principal, l'autre est un Z80 qui sert de processeur dédié à l'audio. Le Z80 commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des interruptions au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', un circuit sur la carte mère connecté au bus, qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80. Le code de 8 bits est envoyé au processeur Z80, en parallèle de l'interruption. : Il est possible de voir ce ''IO arbiter chip'' comme un contrôleur d'interruptions spécialisé dans les interruptions inter-processeur. ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> pklik0jhzi0on9wnraqxyjwriohbsv4 762867 762866 2026-04-03T20:00:26Z Mewtow 31375 /* Le multiprocesseur/multicœur asymétrique : les co-processeurs */ 762867 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Puis, en 2005, les '''processeurs multicœurs''' sont arrivés. Ils peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. : Dans ce qui suit, nous utiliserons les termes "processeurs" et "cœurs" comme s'ils étaient synonymes. Tout ce qui vaut pour les systèmes multiprocesseur vaut aussi pour les systèmes multicœurs. ==Les interruptions inter-processeurs== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les systèmes avec plusieurs processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les CPU multicœurs ont aussi un tel réseau d'interconnexion, pour relier les cœurs entre eux. La différence est que le réseau d'interconnexion est placé dans le processeur, pas sur la carte mère. Les systèmes multi-cœurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils sont aussi utilisés pour faire communiquer entre eux plusieurs processeurs. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] ===Le bus partagé entre plusieurs cœurs=== Pour un faible nombre de coeurs/processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Pour les systèmes multicœurs, l'usage d'un bus partagé doit être adaptée pour tenir compte des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts. Par exemple, les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. D'autres défauts très importants seront abordés en détail dans le chapitre sur la cohérence des caches [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Le multiprocesseur/multicœur asymétrique : les co-processeurs== Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. Un cas sur lequel il n'y a pas de débat est celui des '''co-processeurs''', utilisés en complément du processeur principal pour le décharger de certains calculs. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Précisons cependant qu'il existe deux types de co-processeurs. Le premier est un cas de système parallèle, pas le second. Le CPU peut soit exécuter des programmes en parallèle du coprocesseur, soit les deux se passent la main à tour de rôle. Voyons cela rapidement. Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Mais il existe une seconde solution, où il n'y a qu'un seul programme à exécuter. Ce dernier contient des instructions à destination du CPU, d'autres à destination du co-processeur. Les instructions sont exécutées soit par le CPU, soit par le co-processeur, une par une. En clair, le CPU et le co-processeur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''. Un exemple de ces dernier est le cas des FPU x87 anciennes, quand elles étaient séparées du processeur. Mais parlons maintenant des coprocesseurs parallèles, faiblement couplés. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] Un exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le Z80 est utilisé comme co-processeur audio. Il commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. [[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]] Un exemple d'implémentation très simple est celui de la console Néo-géo, qui contenait deux processeurs. Le premier est un Motorola 68000 qui sert de processeur principal, l'autre est un Z80 qui sert de processeur dédié à l'audio. Le Z80 commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des interruptions au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', un circuit sur la carte mère connecté au bus, qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80. Le code de 8 bits est envoyé au processeur Z80, en parallèle de l'interruption. : Il est possible de voir ce ''IO arbiter chip'' comme un contrôleur d'interruptions spécialisé dans les interruptions inter-processeur. ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> ojx2l6r4qj3qpff0lvut9d5w74xnvjq 762868 762867 2026-04-03T20:01:32Z Mewtow 31375 /* Le multiprocesseur/multicœur asymétrique : les co-processeurs */ 762868 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Puis, en 2005, les '''processeurs multicœurs''' sont arrivés. Ils peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. : Dans ce qui suit, nous utiliserons les termes "processeurs" et "cœurs" comme s'ils étaient synonymes. Tout ce qui vaut pour les systèmes multiprocesseur vaut aussi pour les systèmes multicœurs. ==Les interruptions inter-processeurs== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les systèmes avec plusieurs processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les CPU multicœurs ont aussi un tel réseau d'interconnexion, pour relier les cœurs entre eux. La différence est que le réseau d'interconnexion est placé dans le processeur, pas sur la carte mère. Les systèmes multi-cœurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils sont aussi utilisés pour faire communiquer entre eux plusieurs processeurs. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] ===Le bus partagé entre plusieurs cœurs=== Pour un faible nombre de coeurs/processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Pour les systèmes multicœurs, l'usage d'un bus partagé doit être adaptée pour tenir compte des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts. Par exemple, les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. D'autres défauts très importants seront abordés en détail dans le chapitre sur la cohérence des caches [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Le multiprocesseur/multicœur asymétrique : les co-processeurs== Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. Un cas sur lequel il n'y a pas de débat est celui des '''co-processeurs''', utilisés en complément du processeur principal pour le décharger de certains calculs. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Précisons cependant qu'il existe deux types de co-processeurs. Le premier est un cas de système parallèle, pas le second. Le CPU peut soit exécuter des programmes en parallèle du coprocesseur, soit les deux se passent la main à tour de rôle. Voyons cela rapidement. Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Mais il existe une seconde solution, où il n'y a qu'un seul programme à exécuter. Ce dernier contient des instructions à destination du CPU, d'autres à destination du co-processeur. Les instructions sont exécutées soit par le CPU, soit par le co-processeur, une par une. En clair, le CPU et le co-processeur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''. Un exemple de ces dernier est le cas des FPU x87 anciennes, quand elles étaient séparées du processeur. Mais parlons maintenant des coprocesseurs parallèles, faiblement couplés. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] Un autre exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le Z80 est utilisé comme co-processeur audio. Il commande un synthétiseur sonore, et est relié à sa propre mémoire, distincte de la mémoire principale. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', un circuit sur la carte mère connecté au bus, qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80. Le code de 8 bits est envoyé au processeur Z80, en parallèle de l'interruption. : Il est possible de voir ce ''IO arbiter chip'' comme un contrôleur d'interruptions spécialisé dans les interruptions inter-processeur. [[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]] ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> 18agmjf2z5228cdfp8ipocfq0rjwtps 762869 762868 2026-04-03T20:02:37Z Mewtow 31375 /* Le multiprocesseur/multicœur asymétrique : les co-processeurs */ 762869 wikitext text/x-wiki Pour réellement tirer parti du parallélisme de taches, rien ne vaut l'utilisation de plusieurs processeurs et/ou de plusieurs cœurs, qui exécutent chacun un ou plusieurs programmes dans leur coin. Des solutions multiprocesseurs ont alors vu le jour pour rendre l'usage de plusieurs processeurs plus adéquat. Avant de poursuivre, nous allons voir les systèmes multiprocesseur à part des processeurs multicœurs. Il faut dire qu'utiliser plusieurs processeurs et avoir plusieurs cœurs sur la même puce, ce n'est pas la même chose. Particulièrement pour ce qui est de la mémoire cache. Les '''systèmes multiprocesseur''' placent plusieurs processeurs sur la même carte mère. Ils sont courants dans les serveurs ou les ''data centers'', mais sont beaucoup plus rares pour les ordinateurs grand public. Il y a eu quelques systèmes multiprocesseur vendus au grand public dans les années 2000, certaines cartes mères avaient deux sockets pour mettre deux processeurs. Mais les logiciels et les systèmes d'exploitation grand public n'étaient pas adaptés pour, ce qui fait que la technologie est restée confidentielle. Puis, en 2005, les '''processeurs multicœurs''' sont arrivés. Ils peuvent être vus comme un regroupement de plusieurs processeurs dans le même circuit intégré. Pour être plus précis, ils contiennent plusieurs ''cœurs'', chaque cœur pouvant exécuter un programme tout seul. Un cœur dispose de toute la machinerie électronique pour exécuter un programme, que ce soit un séquenceur d'instruction, des registres, une unité de calcul. Par contre, certains circuits du processeur sont partagés entre les cœurs, comme les circuits d’interfaçage avec la mémoire. Les processeurs multicœurs sont devenus la norme dans les ordinateurs grand public et les logiciels et systèmes d'exploitation se sont adaptés. Suivant le nombre de cœurs présents dans notre processeur, celui-ci sera appelé un processeur double-cœur (deux cœurs), quadruple-cœur (4 cœurs), octuple-cœur (8 cœurs), etc. Les processeurs grand public ont généralement entre 8 et 16 cœurs, à l'heure où j'écris ces lignes (2025), rarement au-delà. Par contre, les processeurs pour serveurs dépassent la vingtaine de cœurs. Les serveurs utilisent souvent des architectures dites '''''many core''''', qui ont un très grand nombre de cœurs, plus d'une cinquantaine, voire plusieurs centaines ou milliers. : Dans ce qui suit, nous utiliserons les termes "processeurs" et "cœurs" comme s'ils étaient synonymes. Tout ce qui vaut pour les systèmes multiprocesseur vaut aussi pour les systèmes multicœurs. ==Les interruptions inter-processeurs== Les différents processeurs sont gérés par le système d'exploitation de l'ordinateur, avec l'aide d''''interruptions inter-processeurs''', des interruptions déclenchées sur un processeur et exécutées sur un autre, éventuellement par tous les autres pour certaines interruptions spécifiques. Pour générer des interruptions inter-processeur, le contrôleur d'interruption doit pouvoir rediriger des interruptions déclenchées par un processeur vers un autre. Les anciens PC incorporaient sur leur carte mère un contrôleur d'interruption créé par Intel, le 8259A, qui ne gérait pas les interruptions inter-processeurs. Les cartes mères multiprocesseurs devaient incorporer un contrôleur spécial en complément. De nos jours, chaque cœur x86 possède son propre contrôleur d’interruption, le local APIC, qui gère les interruptions en provenance ou arrivant vers ce processeur. On trouve aussi un IO-APIC, qui gère les interruptions en provenance des périphériques et de les redistribuer vers les APIC locaux. L'IO-APIC gère aussi les interruptions inter-processeurs en faisant passer les interruptions d'un local APIC vers un autre. Tous les APIC locaux et l'IO-APIC sont reliés ensembles par un bus APIC spécialisé, par lequel ils vont pouvoir communiquer et s'échanger des demandes d'interruptions. [[File:Contrôleurs d'interrptions sur systèmes x86 multicoeurs.png|centre|vignette|upright=1.5|Contrôleurs d’interruptions sur systèmes x86 multicœurs.]] On peut préciser 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 sélectionné. Cela se fait avec l'aide d'un registre de 64 bits nommé l'''Interrupt Command Register''. Pour simplifier le mécanisme complet, chaque processeur se voit attribuer un Id au démarrage qui permet de l'identifier (en fait, cet Id est attribué au local APIC de chaque processeur). Certains bits de ce registre permettent de préciser 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 préciser l'Id du processeur qui va devoir recevoir l'interruption. À charge de l'APIC de faire ce qu'il faut en fonction du contenu de ce registre. ==Le partage des caches== Quand on conçoit un processeur multicœur, il ne faut pas oublier ce qui arrive à la pièce maîtresse de tout processeur actuel : le cache ! Pour le moment nous allons oublier le fait que les processeurs ont une hiérarchie de caches, avec des caches L1, L2, L3 et autres. Nous allons partir du principe qu'un processeur simple cœur a un seul cache, et voir comment adapter le cache à la présence de plusieurs cœurs. Nous allons rapidement lever cette hypothèse, pour étudier le cas où un processeur multicœur a une hiérarchie de caches, mais seulement après avoir vu le cas le plus simple à un seul cache. ===Le partage des caches sans hiérarchie de caches : caches dédiés et partagés=== Avec un seul niveau de cache, sans hiérarchie, deux solutions sont possibles. La première consiste à garder un seul cache, et de le partager entre les cœurs. L'autre solution est de dupliquer le cache et d'utiliser un cache par cœur. Les deux solutions sont appelées différemment. On parle de '''caches dédiés''' si chaque cœur possède son propre cache, et de '''cache partagé''' avec un cache partagé entre tous les cœurs. Ces deux méthodes ont des inconvénients et des avantages. {| |[[File:Caches dédiés.png|vignette|Caches dédiés]] |[[File:Caches partagés.png|vignette|Cache partagé]] |} Le premier point sur lequel comparer caches dédiés et partagés est celui de la capacité du cache. La quantité de mémoire cache que l'on peut placer dans un processeur est limitée, car le cache prend beaucoup de place, près de la moitié des circuits du processeur. Aussi, un processeur incorpore une certaine quantité de mémoire cache, qu'il faut répartir entre un ou plusieurs caches. Les caches dédiés et partagés ne donnent pas le même résultat. D'un côté, le cache partagé fait que toute la mémoire cache est dédiée au cache partagé, qui est très gros. De l'autre, on doit répartir la capacité du cache entre plusieurs caches séparés, individuellement plus petits. En conséquence, on a le choix entre un petit cache pour chaque processeur ou un gros cache partagé. Le choix entre les deux n'est pas simple, mais doit tenir compte du fait que les programmes exécutés sur les cœurs n'ont pas les mêmes besoins. Certains programmes sont plus gourmands et demandent beaucoup de cache, alors que d'autres utilisent peu la mémoire cache. Avec un cache dédié, tous les programmes ont accès à la même quantité de cache, car les caches des différents cœurs sont de la même taille. Les caches dédiés étant assez petits, les programmes plus gourmands devront se débrouiller avec un petit cache, alors que les autres programmes auront du cache en trop. À l'opposé, un cache partagé répartit le cache de manière optimale : un programme gourmand peut utiliser autant de cache qu'il veut, laissant juste ce qu'il faut aux programmes moins gourmands. le cache peut être répartit plus facilement selon les besoins des différents programmes. [[File:Cache partagé contre cache dédié.png|centre|vignette|upright=2.5|Cache partagé contre cache dédié]] Un autre avantage des caches partagés est quand plusieurs cœurs accèdent aux même données. C'est un cas très courant, souvent lié à l'usage de mémoire partagé ou de ''threads''. Avec des caches dédiés, chaque cœur a une copie des données partagées. Mais avec un cache partagé, il n'y a qu'une seule copie de chaque donnée, ce qui utilise moins de mémoire cache. Imaginons que l'on sait 8 caches dédiés de 8 Kibioctets, soit 64 kibioctets au total, comparé à un cache partagé de même capacité totale. Les doublons dans les caches dédiés réduiront la capacité mémoire utile, effective, comparé à un cache partagé. S'il y a 1 Kibioctet de mémoire partagé, 8 kibioctets seront utilisés pour stocker ces données en doublons, seulement 1 kibioctet sur un cache partagé. Ajoutons aussi que la cohérence des caches est grandement simplifiée avec l'usage d'un cache partagé, vu que les données ne sont pas dupliquées dans plusieurs caches. Mais le partage du cache peut se transformer en inconvénient si les programmes entrent en compétition pour le cache, que ce soit pour y placer des données ou pour les accès mémoire. Deux programmes peuvent vouloir accéder au cache en même temps, voire carrément se marcher sur les pieds. La résolution des conflits d'accès au cache est résolu soit en prenant un cache multiport, avec un port dédié par cœur, soit par des mécanismes d'arbitrages avec des circuits dédiés. Le revers de la médaille tient au temps de latence. Plus un cache est gros, plus il est lent. En conséquence, des caches dédiés seront plus rapides qu'un gros cache partagé plus lent. ===Le partage des caches adapté à une hiérarchie de caches=== Dans la réalité, un processeur multicœur ne contient pas qu'un seul cache, mais une hiérarchie de caches avec des caches L1, L2 et L3, parfois L4. Dans cette hiérarchie, certains caches sont partagés entre plusieurs cœurs, les autres sont dédiés. Le cache L1 n'est jamais partagé, car il doit avoir un temps d'accès très faible. Pour les autres caches, tout dépend du processeur. [[File:Dual Core Generic.svg|vignette|Cache L2 partagé.]] Les premiers processeurs multicœurs commerciaux utilisaient deux niveaux de cache : des caches L1 dédiés et un cache L2 partagé. Le cache L2 partagé était relié aux caches L1, grâce à un système assez complexe d'interconnexions. Le cache de niveau L2 était souvent simple port, car les caches L1 se chargent de filtrer les accès aux caches de niveau inférieurs. Les processeurs multicœurs modernes ont des caches L3 et même L4, de grande capacité, ce qui a modifié le partage des caches. Le cache de dernier niveau, à savoir le cache le plus proche de la mémoire, est systématiquement partagé, car son rôle est d'être un cache lent mais gros. Il s'agit le plus souvent d'un cache de L3, plus rarement L4. Sur certains processeurs multicœurs, le cache de dernier niveau n'est techniquement pas dans le cœur, mais fait partie d'un ensemble de circuits reliés, comme le contrôleur mémoire ou l'interface mémoire. Il fonctionne à une fréquence différente du processeur, n'a pas la même tension d'alimentation, etc. Le cas du cache L2 dépend des architectures : il est partagé sur certains processeurs, dédié sur d'autres. Mais sur les processeurs modernes, c'est un cache dédié soit par cœur, soit pour un groupe de cœurs. Dans le cas le plus courant, chaque cache L2 est partagé entre plusieurs cœurs mais pas à tous. En effet, on peut limiter le partage du cache à quelques cœurs particuliers pour des raisons de performances. [[File:Partage des caches sur un processeur multicoeurs.png|centre|vignette|upright=2.0|Partage des caches sur un processeur multicœur.]] D'autres processeurs ont des caches L2 dédiés. Il s'agit surtout des processeurs multicœurs anciens, parmi les premières générations de processeurs multicœurs. Un exemple est celui de la microarchitecture Nehalem d'Intel. Il avait des caches L1 et L2 dédiés, mais un cache L3 partagé. [[File:Nehalem EP.png|centre|vignette|upright=2.0|Partage des caches sur un processeur Intel d'architecture Nehalem.]] ===Les caches partagés centralisés et distribués=== Un point important est que quand on parle de cache partagé ou de cache dédié, on ne parle que de la manière dont les cœurs peuvent accéder au cache, pas de la manière dont le caches est réellement localisé sur la puce. En théorie, qui dit plusieurs caches dédiés signifie que l'on a vraiment plusieurs caches séparés sur la puce. Et chaque cache dédié est proche du cœur qui lui est attribué. Et pour les caches partagés unique, une portion de la puce de silicium contient le cache, que cette portion est un énorme bloc de transistors. Il est généralement placé au milieu de la puce ou sur un côté, histoire de facilement le connecter à tous les cœurs. Mais pour les caches séparés, ce n'est pas toujours le cas. Avoir un cache énorme poserait des problèmes sur les architectures avec beaucoup de cœurs. En réalité, le cache est souvent découpé en plusieurs banques, reliées à un contrôleur du cache par un système d'interconnexion assez complexe. Les banques sont physiquement séparées, et il arrive qu'elles soient placées proche d'un cœur chacune. L'organisation des banques ressemble beaucoup à l'organisation des caches dédiés, avec une banque étant l'équivalent d'un cache dédié. La différence est que les cœurs peuvent lire et écrire dans toutes les banques, grâce au système d'interconnexion et au contrôleur de cache. Tel était le cas sur les processeurs AMD Jaguar. Ils avaient un cache L2 de 2 mébioctets, partagés entre tous les cœurs, qui était composé de 4 banques de 512 Kibioctets. Les quatre banques du cache étaient reliées aux 4 cœurs par un réseaux d'interconnexion assez complexe. [[File:AMDJaguarModule.png|centre|vignette|upright=2|AMD Jaguar Module]] La différence entre les deux solutions pour les caches partagés porte le nom de cache centralisés versus distribués. Un gros cache unique sur la puce est un '''cache centralisé''', et c'est généralement un cache partagé. Mais un cache composé de plusieurs banques dispersées sur la puce est un '''cache distribué''', qui peut être aussi bien dédié que partagé. ===Les caches virtualisés=== Il faut noter que quelques processeurs utilisent cette technique pour fusionnent le cache L2 et le cache L3. Par exemple, les processeurs IBM Telum utilisent des caches L3 virtualisés, dans leurs versions récentes. Le processeur Telum 2 contient 10 caches L2 de 36 mébioctets chacun, soit 360 mébioctets de cache. L'idée est que ces 360 mébioctets sont partagés à la demande entre cache L2 dédié et cache L3. On parle alors de '''cache virtualisé'''. Un cache de 36 mébioctet est associé à un cœur, auquel il est directement relié. Les cœurs n'utilisent pas tous leur cache dédié à 100% Il arrive que des cœurs aient des caches partiellement vides, alors que d'autres on un cache qui déborde. L'idée est que si un cœur a un cache plein, les données évincées du cache L2 privé sont déplacées dans le cache L2 d'un autre cœur, qui lui est partiellement vide. Le cache L2 en question est alors partitionné en deux : une portion pour les données associée à son cœur, une portion pour les données des L2 des autres cœurs. Pour que la technique fonctionne, le processeur mesure le remplissage de chaque cache L2. De plus, il faut gérer la politique de remplacement des lignes de cache. Une ligne de cache évincée du cache doit être déplacé dans un autre L2, pas dans les niveaux de cache inférieur, ni dans la mémoire. Du moins, tant qu'il reste de la place dans le cache L3. De plus, une lecture/écriture dans le cache L3 demande de localiser le cache L2 contenant la donnée. Pour cela, les caches L2 sont tous consultés lors d'un accès au L3, c'est la solution la plus simple, elle marche très bien si le taux de défaut du cache L2 est faible. Une telle optimisation ressemble beaucoup à un cache L2/L3 distribué, mais il y a quelques différences qui sont décrites dans le paragraphe précédent. Avec un L2 distribué, tout accès au L2 déclencherait une consultation de toutes les banques du L2. Avec un cache L3 virtualisé, ce n'est pas le cas. Le cache L2 associé au cœur est consulté, et c'est seulement en cas de défaut de cache que les autres caches L2 sont consultés. De plus, avec un cache L2 distribué, il n'y a pas de déplacement d'une ligne de cache entre deux banques, entre deux caches L2 physiques. Alors qu'avec un cache L3 virtualisé, c'est le cas en cas de remplacement d'une ligne de cache dans le cache L2. Sur le processeur Telum 1, le partage du cache L2 est assez simple. Un cache L2 fait 32 mébioctets et est découpé en deux banques de 16 mébioctets. En temps normal, les premiers 16 mébioctets sont toujours associé au cache L2, au cœur associé. Les 16 mébioctets restants peuvent soit être attribués au cache L3, soit fusionnés avec les 16 premiers mébioctets. Dans le cas où le cœur associé est en veille, n'est absolument pas utilisé, les 32 mébioctets sont attribués au cache L3. Un partage assez simple, donc. Le partage du cache L2/L3 sur les processeurs Telum 2 n'est pas connu, il est supposé être plus flexible. ==Le réseau d'interconnexion entre cœurs== Les systèmes avec plusieurs processeurs incorporent un réseau d'interconnexion pour connecter les processeurs entre eux, ainsi qu'à la mémoire RAM. Il s'agit d'un '''réseau d'interconnexion inter-processeur''', placé sur la carte mère. Les CPU multicœurs ont aussi un tel réseau d'interconnexion, pour relier les cœurs entre eux. La différence est que le réseau d'interconnexion est placé dans le processeur, pas sur la carte mère. Les systèmes multi-cœurs modernes utilisent des réseaux d'interconnexion standardisés, les standards les plus communs étant l'HyperTransport, l'Intel QuickPath Interconnect, l'IBM Elastic Interface, le Intel Ultra Path Interconnect, l'Infinity Fabric, etc. Ils sont aussi utilisés pour faire communiquer entre eux plusieurs processeurs. [[File:Architecture multicoeurs et réseau sur puce.png|centre|vignette|upright=1.5|Architecture multicoeurs et réseau sur puce]] ===Le bus partagé entre plusieurs cœurs=== Pour un faible nombre de coeurs/processeurs, la solution utilisée relie les processeurs entre eux grâce au bus mémoire. Le bus mémoire est donc un '''bus partagé''', avec tout ce que cela implique. [[File:Architecture multicoeurs à bus partagé.png|centre|vignette|upright=2|Architecture multiprocesseurs à bus partagé]] Pour les systèmes multicœurs, l'usage d'un bus partagé doit être adaptée pour tenir compte des caches partagés. Voyons d'abord le cas d'un CPU avec deux niveaux de cache, dont un cache L2 est partagé entre tous les cœurs. Les caches L1 sont reliés au cache L2 partagé par un bus, qui n'a souvent pas de nom. Nous désignerons le bus entre le cache L1 et le cache L2 : '''bus partagé''', sous-entendu partagé entre tous les caches. C'est lui qui sert à connecter les cœurs entre eux. [[File:Architecture multicoeurs à bus partagé entre caches L1 et L2.png|centre|vignette|upright=2|Architecture multicoeurs à bus partagé entre caches L1 et L2]] Un processeur multicœur typique a une architecture avec trois niveaux de cache (L1, L2 et L3), avec un niveau L1 dédié par cœur, un niveau L2 partiellement partagé et un L3 totalement partagé. Le bus partagé est alors difficile à décrire, mais il correspond à l'ensemble des bus qui connectent les caches L1 aux caches L2, et les caches L2 au cache L3. Il s'agit alors d'un ensemble de bus, plus que d'un bus partagé unique. L'usage d'un bus partagé a cependant de nombreux défauts. Par exemple, les processeurs doivent se répartir l'accès au bus mémoire, il faut gérer le cas où deux processeurs accèdent au bus en même temps, etc. Pour cela, un composant dédié s'occupe de l'arbitrage entre processeurs. Il est généralement placé sur la carte mère de l'ordinateur, dans le ''chipset'', dans le pont nord, ou un endroit proche. D'autres défauts très importants seront abordés en détail dans le chapitre sur la cohérence des caches [[File:Intel486 System Arbitration.png|centre|vignette|upright=2|Système avec deux Intel486 - Arbitrage du bus mémoire.]] ===Le réseau d'interconnexion entre plusieurs cœurs=== Relier plusieurs cœurs avec des bus pose de nombreux problèmes techniques qui sont d'autant plus problématiques que le nombre de cœurs augmente. Le câblage est notamment très complexe, les contraintes électriques pour la transmission des signaux sont beaucoup plus fortes, les problèmes d'arbitrages se font plus fréquents, etc. Pour régler ces problèmes, les processeurs multicoeurs n'utilisent pas de bus partagé, mais un réseau d'interconnexion plus complexe. Un exemple de réseau d'interconnexion est celui des architectures AMD EPYC, de microarchitecture Zen 1. Elles utilisaient des chiplets, à savoir que le processeur était composé de plusieurs puces interconnectées entre elles. Chaque puce contenait un processeur multicoeurs intégrant un cache L3, avec un réseau d'interconnexion interne au processeur sans doute basé sur un ensemble de bus. De plus, les puces étaient reliées à une puce d'interconnexion qui servait à la fois d'interface entre les processeurs, mais aussi d'interface avec la R1AM, le bus PCI-Express, etc. La puce d'interconnexion était gravée en 14 nm contre 7nm pour les chiplets des cœurs. {| |[[File:AMD Epyc 7702 delidded.jpg|centre|vignette|upright=2|AMD Epyc 7702.]] |[[File:AMD Epyc Rome Aufbau.png|centre|vignette|upright=2|Schéma fonctionnel de l'AMD Epyc.]] |} Le réseau d'interconnexion peut être très complexe, avec des connexions réseau, des commutateurs, et des protocoles d'échanges entre processeurs assez complexes basés sur du passage de messages. De telles puces utilisent un '''réseau sur puce''' (''network on chip''). Mais d'autres simplifient le réseau d'interconnexion, qui se résume à un réseau ''crossbar'', voire à des mémoires FIFO pour faire l'interface entre les cœurs. Le problème principal des réseaux sur puce est que les mémoires FIFOs sont difficiles à implémenter sur une puce de silicium. Elles prennent beaucoup de place, utilisent beaucoup de portes logiques, consomment beaucoup d'énergie, sont difficiles à concevoir pour diverses raisons (les accès concurrents/simultanés sont fréquents et font mauvais ménage avec les ''timings'' serrés de quelques cycles d'horloges requis). Il est donc impossible de placer beaucoup de mémoires FIFO dans un processeur, ce qui fait que les commutateur sont réduits à leur strict minimum : un réseau d'interconnexion, un système d'arbitrage simple parfois sans aucune FIFO, guère plus. ===Les architectures en ''tile''=== Un cas particulier de réseau sur puce est celui des '''architectures en ''tile''''', des architectures avec un grand nombre de cœurs, connectés les unes aux autres par un réseau d'interconnexion "rectangulaire". Chaque cœur est associé à un commutateur (''switch'') qui le connecte au réseau d'interconnexion, l'ensemble formant une ''tile''. [[File:Tile64-Tile.svg|centre|vignette|upright=1.5|''Tile'' de base du Tile64.]] Le réseau est souvent organisé en tableau, chaque ''tile'' étant connectée à plusieurs voisines. Dans le cas le plus fréquent, chaque ''tile'' est connectée à quatre voisines : celle du dessus, celle du dessous, celle de gauche et celle de droite. Précisons que cette architecture n'est pas une architecture distribuée dont tous les processeurs seraient placés sur la même puce de silicium. En effet, la comparaison ne marche pas pour ce qui est de la mémoire : tous les cœurs accèdent à une mémoire partagée située en dehors de la puce de silicium. Le réseau ne connecte pas plusieurs ordinateurs séparés avec chacun leur propre mémoire, mais plusieurs cœurs qui accèdent à une mémoire partagée. Un bon exemple d'architecture en ''tile'' serait les déclinaisons de l'architecture Tilera. Les schémas du-dessous montrent l'architecture du processeur Tile 64. Outre les ''tiles'', qui sont les éléments de calcul de l'architecture, on trouve plusieurs contrôleurs mémoire DDR, divers interfaces réseau, des interfaces série et parallèles, et d'autres entrées-sorties. [[File:Tile64.svg|centre|vignette|upright=2|Architecture Tile64 du Tilera.]] ==Le multiprocesseur/multicœur asymétrique : les co-processeurs== Sur les processeurs grand public actuels, les cœurs d'un processeur multicœurs sont tous identiques. Mais ce n'est certainement pas une obligation. On peut très bien regrouper plusieurs cœurs très différents, par exemple un cœur principal avec des cœurs plus spécialisés autour. Il faut ainsi distinguer le '''multicœurs symétrique''', dans lequel on place des processeurs identiques sur la même puce de silicium, du '''multicœurs asymétrique''' où les cœurs ne sont pas identiques. Et il en est de même sur les systèmes avec plusieurs processeurs : on parle de '''multiprocesseur symétrique''' si les processeurs sont identiques, '''multiprocesseur asymétrique''' s'ils sont différents. Précisons ce que nous entendons par "cœurs différents" ou "identiques". Les processeurs Intel modernes utilisent deux types de cœurs différents : des cœurs P et des cœurs E. Le P est pour ''Performance'', le E est pour "Efficiency". Les deux ont le même jeu d'instruction : ce sont des processeurs x86. Par contre, ils ont des microarchitectures différentes. Et Intel n'est pas le seul à utiliser cette technique : ARM a fait pareil avec ses CPU d'architecture ''Big-little''. Il n'est pas clair si de telles organisation sont du multicœur symétrique ou asymétrique. Le jeu d'instruction est identique, sauf éventuellement pour certaines extension comme l'AVX. Les deux coeurs n'ont pas les mêmes performances, mais est-ce suffisant ? La terminologie n'est pas claire. Un cas sur lequel il n'y a pas de débat est celui des '''co-processeurs''', utilisés en complément du processeur principal pour le décharger de certains calculs. Nous en avions déjà vu dans le chapitre sur l'architecture de base, et avions vu quelques exemples, provenant tous d'anciennes consoles de jeu. Précisons cependant qu'il existe deux types de co-processeurs. Le premier est un cas de système parallèle, pas le second. Le CPU peut soit exécuter des programmes en parallèle du coprocesseur, soit les deux se passent la main à tour de rôle. Voyons cela rapidement. Le cas le plus fréquent est celui où le CPU et le coprocesseur travaillent en parallèle et exécutent des programmes différents. On parle alors de '''coprocesseurs faiblement couplés'''. Mais il existe une seconde solution, où il n'y a qu'un seul programme à exécuter. Ce dernier contient des instructions à destination du CPU, d'autres à destination du co-processeur. Les instructions sont exécutées soit par le CPU, soit par le co-processeur, une par une. En clair, le CPU et le co-processeur se passent à la main à tour de rôle, ils ne travaillent pas en parallèle. On parle alors de '''coprocesseurs fortement couplés'''. Un exemple de ces dernier est le cas des FPU x87 anciennes, quand elles étaient séparées du processeur. Mais parlons maintenant des coprocesseurs parallèles, faiblement couplés. Un exemple de multicœurs asymétrique est celui du processeur CELL de la console de jeu PS3. Il intègre un cœur principal POWER PC v5 et 8 cœurs qui servent de processeurs auxiliaires. Le processeur principal est appelé le PPE et les processeurs auxiliaires sont les SPE. Les SPE sont reliés à une mémoire locale (''local store'') de 256 kibioctets qui communique avec le processeur principal via un bus spécial. Les SPE communiquent avec la RAM principale via des contrôleurs DMA. Les SPE possèdent des instructions permettant de commander leur contrôleur DMA et c'est le seul moyen qu'ils ont pour récupérer des informations depuis la mémoire. Et c'est au programmeur de gérer tout ça ! C'est le processeur principal qui va envoyer aux SPE les programmes qu'ils doivent exécuter. Il délègue des calculs aux SPE en écrivant dans le local store du SPE et en lui ordonnant l’exécution du programme qu'il vient d'écrire. [[File:Schema Cell.png|centre|vignette|upright=2|Architecture du processeur CELL de la PS3. Le PPE est le processeur principal, les SPE sont des processeurs auxiliaires qui comprennent : un ''local store'' noté LS, un processeur noté SXU, et un contrôleur DMA pour échanger des informations avec la mémoire principale.]] Un autre exemple est celui des consoles néo-géo et Megadrive. Elles intègrent deux processeurs : un Motorola 68000 qui sert de processeur principal, un Z80 qui sert de processeur dédié à l'audio. Le Z80 commande un synthétiseur sonore et a sa propre mémoire, distincte de la mémoire principale, les trois étant reliées via un bus séparé. Le MC68000 est le processeur principal et a une relation maitre-esclave avec le Z80 : le MC68000 envoie des commandes au Z80, mais la communication ne va pas dans l'autre sens. Les deux processeurs communiquent via l'intermédiaire d'un ''IO arbiter chip'', qui gère les interruptions inter-processeur. Il contient un registre de 8 bits, dans lequel le MC68000 peut écrire dedans à sa guise, le registre étant adressable par le processeur. Les valeurs écrites dans ce registre sont des numéros d'interruption, qui indiquent quelle routine d'interruption exécuter. Lorsque le MC68000 écrit une valeur dedans, cela déclenche l’exécution automatique d'une interruption sur le Z80. Le code de 8 bits est envoyé au processeur Z80, en parallèle de l'interruption. : Il est possible de voir ce ''IO arbiter chip'' comme un contrôleur d'interruptions spécialisé dans les interruptions inter-processeur. [[File:Architecture de la Megadrive et de la Néogeo.png|centre|vignette|upright=2.5|Architecture de la Megadrive et de la Néogeo]] ==Annexe : les architectures à cœurs conjoints== Sur certains processeurs multicœurs, certains circuits sont partagés entre plusieurs cœurs. Typiquement, l'unité de calcul flottante est partagée entre deux coeurs/''threads'', les unités SIMD qu'on verra dans quelques chapitres sont aussi dans ce cas. Le partage de circuits permet d'éviter de dupliquer trop de circuits et donc d'économiser des transistors. Le problème est que ce partage est source de dépendances structurelles, ce qui peut entraîner des pertes de performances. Cette technique consistant de partage d'unités de calcul entre coeurs s'appelle le '''cluster multithreading''', ou encore les '''architectures à cœurs conjoints''' (''Conjoined Core Architectures''). Elle est notamment utilisée sur les processeurs AMD de microarchitecture Bulldozer, incluant ses trois révisions ultérieures nommées Piledriver, Steamroller et Excavator. Un exemple est celui des processeurs AMD FX-8150 et FX-8120. Sur ces processeurs, les instructions sont chargées dans deux files d'instructions séparées, une par ''thread'' matériel. Les instructions sont ensuite décodées par un décodeur unique et renommées dans une unité de renommage unique. Par la suite, il y a deux voies entières séparées et une voie flottante partagée. Chaque voie entière a sa propre fenêtre d'instruction entière, son tampon de ré-ordonnancement, ses unités de calcul dédiées, ses registres, sa ''load-store queue'', son cache L1. Par contre, la voie flottante partage les unités de calcul flottantes et n'a qu'une seule fenêtre d'instruction partagée par les deux ''threads''. [[File:AMD Bulldozer microarchitecture.png|centre|vignette|upright=3|Microarchitecture Bulldozer d'AMD.]] La révision Steamroller sépara le ''front-end'' en deux voies distinctes, une par ''thread''. Concrètement, elle ajouta un second décodeur d'instruction, une seconde file de micro-opération et une seconde unité de renommage de registres, afin d'améliorer les performances. Niveaux optimisations mineures, les stations de réservation ont été augmentées, elles peuvent mémoriser plus de micro-opérations, idem pour les bancs de registre et les files de lecture/écriture. Un cache de micro-opérations a été ajouté, de même que des optimisations quant au renommage de registre. Des ALU ont aussi été ajoutées, des FPU retirées. [[File:AMD excavator microarchitecture.png|centre|vignette|upright=3|Microarchitecture Excavator d'AMD.]] <noinclude> {{NavChapitre | book=Fonctionnement d'un ordinateur | prev=Les architectures parallèles | prevText=Les architectures parallèles | next=Architectures multithreadées et Hyperthreading | nextText=Architectures multithreadées et Hyperthreading }} </noinclude> 216od0i0qwoe16587k1148dcp3icyj0 Neurosciences/L'activité électrique du cerveau 0 69437 762831 762599 2026-04-03T13:29:16Z JackPotte 5426 Révocation de 2 modifications de [[Special:Contributions/~2026-19939-14|~2026-19939-14]] ([[User talk:~2026-19939-14|discussion]]) vers la dernière version de [[User:DavidL|DavidL]] 755752 wikitext text/x-wiki Le cerveau a une activité électrique relativement soutenue : les neurones sont parcourus par des potentiels d'action, qui ne sont ni plus ni moins que des signaux électriques. L'ensemble des potentiels d'action à un moment donné causent une activité électrique mesurable à la surface du crâne, de l'ordre de quelques microvolts. Cette activité électrique cérébrale peut se mesurer avec un instrument composé de plusieurs électrodes placées à la surface du crâne : l'électroencéphalographe, ou EEG. Celui-ci est utilisé pour étudier le fonctionnement du cerveau, diagnostiquer certaines maladies qui modifient l'activité électrique cérébrale et observer le déroulement du sommeil. Dans ce chapitre, nous allons parler de l'activité électrique normale du cerveau, ainsi que d'une pathologie liée l’'''épilepsie'''. L'activité électrique subit aussi des variations au cours de la veille et du sommeil. Mais nous laissons ces variations pour le chapitre sur le sommeil : ce chapitre se limite à quelques généralités et à l’épilepsie. ==L'activité électrique cérébrale non-pathologique== Si l'ensemble du cerveau a une activité électrique, celle-ci n'est pas tout à fait uniforme : si une aire cérébrale s'active plus qu'une autre, les variations de potentiel seront plus importantes près de cette aire cérébrale que sur une surface éloignée. Généralement, seules les aires cérébrales du cortex, situées sous la surface du crâne, donnent des variations significatives sur l'EEG : le signal des autres aires cérébrales est atténué par le cortex situé au-dessus. Pour localiser la source de l'activité cérébrale, la mesure du potentiel s'effectue souvent avec plusieurs électrodes disposées régulièrement sur la surface du crâne. Les mesures montrent que l'activité électrique est quelque peu structurée et possède quelques régularités assez visibles sur les mesures à l'EEG. Contrairement à ce qu'on pourrait croire, les neurones ont tendance à synchroniser leurs émissions, à émettre plus ou moins en même temps. Sur l'EEG, cela se traduit par une activité cyclique, périodique (à quelques variations près) : on observe des '''rythmes cérébraux''', parfois improprement appelés ondes cérébrales. Au-dessus de ces rythmes, on observe des fluctuations aléatoires de l'activité électrique. Parmi ces fluctuations, certaines sont causées par une perception extérieure ou une activité intellectuelle : ce sont des '''potentiels évoqués'''. [[File:SimulationNeuralOscillations.png|centre|vignette|upright=1.5|Illustration des rythmes cérébraux, en fonction des potentiels d'action individuels émis par chaque neurone cortical.]] ===Les rythmes cérébraux=== Chaque rythme cérébral a une fréquence bien particulière, et on classe arbitrairement les ondes cérébrales dans 4 à 6 bandes de fréquences nommées alpha, bêta, gamma, delta, et thêta. Les scientifiques ont remarqué que chaque bande de fréquence correspond à des niveaux d'éveil et de sommeil différents. De plus, ces ondes cérébrales sont localisées dans des zones différentes du cerveau : les ondes gamma et bêta sont essentiellement localisées dans les lobes frontaux, alors que les rythmes Alpha sont localisées dans le cortex sensoriel et moteur. {|class="wikitable" |- !Onde cérébrale !Bande de fréquence |- !Delta |Inférieur à 4 hertz |- !Thêta |4 à 7 hertz |- !Alpha |8 à 15 hertz |- !Bêta |16 à 31 hertz |- !Gamma |Plus de 32 hertz |} Une activité rythmique peut s'observer à de nombreux niveau : au niveau d'un neurone isolé, au niveau d'un petit réseau de neurones, au niveau du cerveau. Pour ce qui est des neurones isolés, il existe des '''neurones pacemakers''' émettent des potentiels d'action à des intervalles réguliers, même s'ils ne sont pas stimulés par d'autres neurones. Pour les petits réseaux de neurones, composés de quelques centaines ou milliers de neurones, voire plus, des mécanismes de synchronisation peuvent se mettre en place par les synapses. Vu qu'un neurone tend à ^émettre un potentiel d'action quand il est stimulé par un autre, deux neurones reliés entre eux par une synapse tendent à émettre des potentiels d'actions en même temps. Dans une assemblée de neurones fortement reliés entre eux, ce mécanisme tend à synchroniser les neurones entre eux, de sorte qu'ils émettent des potentiels d'action plus ou moins en même temps. Pour ce qui est du niveau cérébral, les rythmes cérébraux trouvent leur origine dans le fait que certaines aires cérébrales sont reliées et forment des boucles dans lesquelles des influx nerveux se propagent cycliquement. Les boucles les plus importantes relient les neurones du cortex au thalamus, d'où leur nom de '''boucles thalamico-corticales'''. Ces boucles thalamico-corticales sont au nombre de deux, la boucle ventrobasale impliquant le thalamus ventrolatéral (à la base et sur le côté) alors que la boucle centrolatérale implique le thalamus centrolatéral (au centre et sur l'intérieur). La boucle ventrobasale s'occupe du traitement des informations sensorielles, alors que la boucle centrolatérale dédiée à la conscience et à la pensée. Lors de l'éveil, les sensations rentrent librement dans la boucle ventrobasale. Mais lors du sommeil, le thalamus ventrobasal ne laisse pas passer les informations sensorielles : les deux boucles thalamico-corticales sont en circuit fermé. Ce comportement est commandé par des neurones réticulaires, eux-mêmes commandés par la formation réticulée (une aire cérébrale dédiée à la gestion de l'éveil, de l'attention, et de l'activation physiologique). ===Les potentiels évoqués=== [[File:ComponentsofERP.svg|vignette|Exemple d'ERP.]] L'étude des potentiels évoqués est plus complexe, vu qu'ils sont noyés dans l'activité électrique aléatoire de base. Distinguer ce qui vient des rythmes, de l'activité aléatoire et potentiels évoqués est quelque peu compliqué. La méthode la plus utilisée est de répéter les mesures sur un grand nombre de sujets et de faire la moyenne entre elles. Ce faisant, les variations aléatoires sont annulées, de même que les rythmes (du fait de leur déphasage), alors que les potentiels évoqués restent. On voit alors que certains stimulus déclenchent systématiquement des potentiels évoqués après un délai fixe. Par exemple, quand on capte un stimulus verbal, un potentiel évoqué apparait environ 400 millisecondes après sa perception Ce stimulus a reçu son propre nom et est étudié pour sa relation avec les processus de traitement du langage. C'est d'ailleurs loin d'être le seul : de nombreux potentiels évoqués ont reçu un nom et ont été identifié comme entités à étudier. Les potentiels évoqués peuvent être des potentiels positifs, où la tension augmente comparé à son niveau de base, ou des potentiels négatifs où la tension diminue. Cela se retient dans la notation des potentiels évoqués, qui en tient compte. La notation de ces potentiels est composée d'une lettre suivie d'un nombre. La lettre indique si le potentiel est positif ou négatif : P pour positif et N pour négatif. Le nombre est le délai entre la présentation du stimulus et l'apparition du potentiel évoqué. Pour donner un exemple, reprenons le potentiel évoqué mentionné précédemment, où un stimulus verbal déclenche un potentiel négatif 400 ms après : ce potentiel est appelé le potentiel N400. ==Les crises épileptiques== Les crises épileptiques sont des crises où l'activité cérébrale s'emballe dans une portion plus ou moins importante du cerveau. L'idée que se fait le grand public se fait d'une crise d'épilepsie ressemble globalement à une perte de connaissance avec des convulsions. Pour rappel, les convulsions sont des contractions musculaires involontaires et assez violentes, qui peuvent toucher toute partie du corps. De telles crises existent, sont très fréquentes, et correspondent à un cas où une crise d'épilepsie embrase tout le cerveau. Mais il existe des crises épileptiques sans convulsions, ou sans perte de conscience, voire avec des symptômes sensoriels, cognitifs, et autres. Malgré la présentation très variée des crises épileptiques, leur mécanisme est le même : l'activité électrique cérébrale s'emballe. Cela se voit bien lorsqu'on fait passer en EEG pendant une crise, comme montré ci-dessous. [[File:Spike-waves.png|centre|vignette|upright=2|EEG d'une crise d'épilepsie : le patient déclenche une crise au beau milieu de l'EEG. On voit le changement de l'activité électrique, clairement visible sur l'EEG.]] La présentation symptomatique d'une crise épileptique est très variable et la classification des crises épileptiques est assez complexe, comme vous allons le voir dans ce qui suit. Pour simplifier le diagnostic, les médecins ont classé les épilepsies selon leurs symptômes,. La classification de ce type la plus connue est l'''International classification of seizure types'', datée de 1981. Elle a ensuite été suivie par la ''ILAE classification of seizure types 2017'', plus moderne. Dans cette dernière, la classification de ces crises se fait sur trois points, auquel nous allons ajouter un quatrième. * Le premier point est si la crise est focale ou généralisée, à savoir si elle touche seulement un hémisphère cérébral ou tout le cerveau. * Le second point est si la crise s’accompagne ou non d'une perte de conscience. * Le troisième point est les symptômes moteurs qui se manifestent lors de la crise. ===Les manifestations motrices d'une crise épileptique=== Les crises épileptiques impliquent souvent des manifestations motrices. Dans les grandes lignes, on distingue les convulsions, les pertes de tonus musculaire, et les automatismes moteurs. La plupart impliquent des '''convulsions''', des contractions musculaires involontaires, même si ce n'est pas systématique. On distingue trois types de convulsions : * les ''convulsions toniques'', où la contraction est prolongée avec une contraction complète des muscles : * les ''convulsions cloniques'' où les contractions sont très courtes et se succèdent rapidement ; * les ''convulsions myocloniques'' sont des convulsions cloniques très courtes, de moins de quelques secondes, parfois moins de la seconde. Une crise épileptique convulsive peut présenter soit des convulsions cloniques, soit des convulsions toniques, soit des convulsions myocloniques, soit une succession des trois. D'autres crises n'impliquent pas de convulsions, qu'elles soient toniques ou cloniques. Certaines crises épileptiques manifestent un mélange de plusieurs types de convulsions, ou une succession de celles-ci. Les myoclonies sans pertes de consciences ne sont pas exclusives aux crises épileptiques. Certains patients sains en ont avant de s'endormir sans que cela ne soit le signe d'une quelconque maladie. Il peut aussi y avoir une '''atonie''', à savoir une perte totale de tonus musculaire, avec ou sans perte de conscience. Elle dure quelques secondes, survient soudainement, et touche souvent un côté du corps ou un membre. Dans le cas le plus sévère, le patient chute brutalement et perd tout contrôle musculaire. Il perd connaissance dans la plupart des cas, mais pas toujours. Dans d'autres, la perte de tonus musculaire ne touche qu'un bras, qu'une jambe, un seul côté du corps. À l'inverse, d'autres crises épileptiques se caractérisent par des '''spasmes épileptiques'''. Un spasme est une contraction musculaire spontanée, soudaine, parfois douloureuse. De tels spasmes d’origine épileptique touchent souvent les bras, jambes, mains et poignets. Typiquement, on observe le plus souvent une extension des bras ou des jambes, ou encore du poignet. Ils sont plus fréquents chez les enfants, où ils sont appelés des spasmes infantiles. Le patient garde souvent conscience lors de crises de spasmes épileptiques, mais ce n'est clairement pas systématique. Le patient peut aussi effectuer des mouvements complexes sont appelés des '''automatismes'''. Le sujet peut par exemple effectuer des mouvements simples, comme se gratter, mastiquer sa nourriture, déambuler, marcher en ligne droite, etc. Mais il peut aussi faire des choses bien plus compliquées, comme déplacer un meuble par exemple. Tout cela permet de distinguer plusieurs types purs de crises épileptiques, dans lesquels on n'observe qu'une manifestation motrice précise, pas un mélange de plusieurs : * les crises toniques ; * les crises cloniques ; * les crises myocloniques; * les crises atoniques ; * les crises d'automatisme ; * les crises de spasmes épileptiques. ===La conscience lors d'une crise d'épilepsie=== Les crises épileptiques sont souvent accompagnées d'une '''altération de la conscience''' bien que ce ne soit pas systématique. Cela peut aller d'une perte de connaissance complète, à une altération plus légère de la conscience avec amnésie. Il arrive que le sujet continue à effectuer des mouvements ou certains automatismes alors que sa conscience est altérée, voire abolie. Le sujet peut alors ressembler à quelqu'un qui fait une crise de somnambulisme, même si ce n'est vrai qu'en apparence. Le critère de la perte de conscience est encore utilisé dans la classification des crises épileptiques. Mais dans certains cas, il est difficile de savoir si le patient a perdu conscience, notamment si les crises sont très courtes et ne durent que quelques secondes. Il existe des crises d'épilepsie qui se manifestent seulement par une perte de conscience isolée, sans symptôme moteur. Il s'agit des '''crises d'absence'''. Durant ces crises, le sujet devient mutique et immobile durant un temps inférieur à la minute. Ces crises ne durent que quelques dizaines de secondes, rarement plus. La survenue de la crise d'absence est soudaine, son entourage remarque qu'il cesse brusquement ce qu'il était en train de faire, voit que le patient regarde dans le vide. Le patient reste debout, mais ne réagit à aucun stimulus : l'appeler par son nom ou le toucher est inutile. La crise cesse généralement aussi rapidement qu'elle a commencé. ===Les crises focales et généralisées=== Les classifications des crises épileptiques distinguent en premier lieu crise focale et  généralisée. La différence entre les deux tient au fait que l'embrasement cérébral est soit limité à une portion limitée du cerveau, soit envahit tout le cerveau. Les '''crises focales''' touchent un seul hémisphère, parfois seulement partiellement, tandis que les '''crises généralisées''' touchent les deux hémisphères, tout le cerveau. Il faut cependant signaler que les crises focales peuvent évoluer vers une crise généralisée, bien que ce ne soit pas systématique. Les crises focales ont vraisemblablement une origine assez différente des crises généralisées. Les crises focales naissent à partir d'un amas/réseau de neurones hyper-excitable, appelé '''foyer épileptique'''. Ce foyer peut naitre à la suite d'une lésion, peu importe que celle-ci provienne d'un traumatisme crânien ou d'un AVC, mais certaines lésions naissent spontanément, sans origine déterminée. Les crises naissent quand ce foyer épileptique manifeste une bouffée spontanée de potentiels d'action. L'hyperactivité électrique va ensuite se propager aux alentours du foyer, passant de synapses en synapses et peut finir par toucher une grande partie du cerveau. Dans la majorité des cas, la bouffée épileptique reste confinée dans une zone épileptogéne assez limitée : la crise ne se propage pas bien loin dans le cerveau. Mais dans d'autres cas, plus rares, la crise peut se généraliser à l'ensemble du cerveau : la crise focale évolue alors en crise généralisée. Ce phénomène fait que les chercheurs classifient les crises focales selon qu'elles se généralisent ou non. Les crises généralisées touchent les deux hémisphères, et entrainent généralement des pertes de conscience, accompagnée ou non de manifestations motrices. La '''crise tonique-clonique''' est la présentation auquel le grand public pense quand on évoque une crise généralisée. Elle se déroule en plusieurs phases successives très caractéristiques : * La première phase, appelée '''phase tonique''' se traduit par une contraction complète des muscles, ainsi qu'une perte de connaissance totale. Le patient tombe alors par terre, sans même essayer de se rattraper. Plus inquiétant, sa respiration s'arrête durant la phase tonique. Dans le cas où la crise se poursuit trop longtemps, le sujet finit même par avoir le teint bleuté à la fin de la crise tonique. Mais fort heureusement, la crise tonique ne dure que quelques secondes à quelques minutes. * Vient ensuite la '''phase clonique''', où le patient est pris de contractions et de soubresauts aléatoires. La respiration reprend, mais est totalement désorganisée et son rythme est totalement aléatoire. Le patient reste inconscient. * Puis vient la phase de '''coma''' : le patient reste inconscient, mais sa respiration reprendre normalement et ses muscles sont totalement décontractés, sans tonus. La couleur du patient revient progressivement à la normale. * Enfin, la crise cesse et le patient reprend conscience. S’ensuit une période où le patient est confus, ne se souvient plus de la crise, a mal à la tête. ===Les classifications des crises épileptiques=== L{{'}}''International classification of seizure types'' de 1981 et l{{'}}''ILAE classification of seizure types 2017'' classent les crises épileptiques différemment. Mais les deux classifications ont beaucoup en commun, et distinguent crises d'absence, crises atones, et crises avec convulsions. ====L{{'}}''International classification of seizure types'' de 1981==== L{{'}}''International classification of seizure types'' de 1981 distingue les crises généralisées et partielles. Le classement des crises généralisées distingues celles avec et sans convulsions. {|class="wikitable" |+ ''International classification of seizure types'', 1981 |- ! colspan="2" | Type de crise épileptique généralisée ! Description ! Perte de conscience |- ! rowspan="4" | Crises convulsives ! Crise clonique | rowspan="2" | Convulsions cloniques | Perte de conscience |- ! Crise myoclonique | Pas de perte de conscience |- ! Crise tonique | Convulsions toniques durant plusieurs minutes/heures | Très fréquente mais pas systématique. |- ! Crise tonique-clonique | colspan="2" | Crise en trois temps : une crise clonique avec perte de conscience, suivie par une crise tonique, elle-même suivie par un coma temporaire de quelques minutes. |- | colspan="3" | |- ! rowspan="2" | Crises non-convulsives ! Crise d'absence | colspan="2" | Interruption de la conscience, sans chute. |- ! Crise atone | Perte totale du tonus musculaire | Très fréquente mais pas systématique. |} Les crises focales pures sont elles subdivisées en deux sous-types selon que la conscience est abolie lors de la crise ou non : on parle de crises focales simples si la conscience n'est pas touchée, et de crise complexe quand la conscience est altérée. Et quand on parle d'altération de la conscience lors d’une crise focale complexe, cela ne signifie pas que le sujet perd conscience, ni même qu'il reste inactif (même si c'est fréquent). {|class="wikitable" |- ! Type de crise épileptique focale ! Description ! Sous-types |- ! Crise simple | Pas d'altération de la conscience. | Les symptômes sont assez différents selon la localisation des aires touchées par la crise. On distingue ainsi : * les crises avec des symptômes moteurs ; * les crises avec des symptômes sensoriels ; * les crises avec des symptômes comportementaux ; * les crises avec des symptômes liés au système nerveux autonome. |- ! Crise complexe | Altération de la conscience. | La classification des crises complexes se borne à faire la distinction entre : * crises où la perturbation de la conscience apparait au début de la crise (crises complexes pures) ; * et celles où la conscience disparait pendant la crise (crises simples qui évoluent en crises complexes). |} Les crises focales qui évoluent en crises généralisées sont un second type de crises focales. Elles sont classées en trois sous-types suivant leur évolution : * crises simples qui évoluent vers une crise généralisée ; * crises complexes qui évoluent en crise généralisée ; * crises simples qui évoluent en crises complexes, qui elle-mêmes évoluent en crise généralisée ; ====L{{'}}''ILAE classification of seizure types 2017''==== Dans l{{'}}''ILAE classification of seizure types 2017'', la classification est beaucoup plus simple. Elle distingue les crises focales des crises généralisées, avec un troisième type pour les crises dont le caractère focal/généralisé est inconnu, et subdivise ces trois types en crises motrices et non-motrices. Pour les crises focales, on ajoute un critères quant à l'altération de conscience ou son absence. L{{'}}''ILAE classification of seizure types 2017'' distingue les crises généralisées avec symptômes moteurs et celles sans. La différence est que certaines crises d'épilepsies se caractérisent par des contractures musculaires, mais sans convulsions proprement dit. Les crises généralisées non-motrices ne sont autre que les crises d'absence proprement dit. Les crises généralisées motrices regroupent les crises convulsives, les crises atoniques et quelques autres. Pour les crises généralisées, on oublie le critère sur l'altération de la conscience, car la conscience est très souvent altérée dans les crises généralisées. Ajouter une subdivision suivant une éventuelle perte de conscience serait donc redondant. {|class="wikitable" |+ ''ILAE classification of seizure types 2017'', Types de crises épileptiques généralisées |- ! rowspan="10" | Crises motrices |- | Crise tonique-clonique |- | Crise tonique |- | Crise clonique |- | Crise myoclonique |- | Crise atonique |- | Crise myoclonique-atonique |- | Crise tonique-clonique-atonique |- | Spasme épileptiques |- | Autres |- | colspan="3" | |- ! rowspan="4" | Crises non-motrices (crise d'absence) | typique |- | Atypique |- | Myoclonique |- | Myoclonies limitées aux yeux (''eyelid myoclonia'') |} Pour les crises focales, elle mesure deux critères : s'il y a ou non altération de la conscience, et les crises avec ou sans symptômes moteurs. Les crises motrices sont classées suivant le symptôme moteur observé, les manifestations motrices manifestées. On a ainsi des crises focales clonique, toniques, atoniques, myocloniques, à automatisme, avec spasmes épileptiques, etc. Les crises non-motrices, sans manifestations motrices, sont classées de la même manière que les crises simples, avec les crises avec symptômes sensoriels, cognitifs, émotionnels, autonomiques, et un cinquième type de crise appelé les crises avec arrêt comportemental (le patient s’arrête de faire ce qu'il faisait durant quelques secondes/minutes). L'avantage de cette classification est qu'elle reconnait que des crises clonique, toniques, atoniques, myocloniques, à automatisme peuvent être focales et ne sont pas systématiquement généralisées. La distinction entre crises simples et complexes, peu parlante pour le grand public, est aussi supprimée, car peu utile en pratique. Les crises peuvent se généraliser, mais la classification ne garde qu'un cas, du fait de la suppression de la distinction crise simple/complexe : celui où une crise focale se généralise, typiquement en crise tonico-clonique. ==L'épilepsie== L{{'}}'''épilepsie''' est une maladie chronique qui se manifeste par des crises d'épilepsies récurrentes. Un point important est qu'une simple crise épileptique ne suffit pas à diagnostiquer une épilepsie. Des crises isolées sont possibles chez des patients non-épileptiques, par exemple parce qu'ils ont une maladie cérébrale qui cause des crises. C'est un point important dans le diagnostic d'une épilepsie : cette maladie est considérée comme étant une maladie chronique et de long-terme, qu'on peut certes guérir, là où une crise unique est peut-être un évènement temporaire , un effet secondaire de médicament, le symptôme temporaire d'une autre maladie, etc. Pour éviter de diagnostiquer des épilepsies à tord et à travers, le neurologue doit éliminer toute maladie pouvant causer de telles crises, ainsi qu'une origine médicamenteuse. De plus, les crises épileptiques doivent se répéter pour que le diagnostic d'épilepsie soit posé, et les crises doivent être séparées par un intervalle de temps suffisant (typiquement 24 heures, au moins). Le diagnostic fait appel à la clinique, mais aussi à l'électroencéphalographie. Un EEG est évidemment anormal lors d'une crise d'épilepsie, mais il peut aussi montrer des anomalies en-dehors des crises, ce qui aide le diagnostic. ===La distinction entre crises provoquées et non-provoquées=== Pour faciliter le diagnostic d'une épilepsie, on distingue les crises épileptiques dites "provoquées", de celles qui ne le sont pas. Les crises provoquées sont liées à un évènement temporaire, qui favorise l'apparition d'une crise. Par exemple, une simple fièvre peut déclencher des convulsions, surtout chez le petit enfant : on parle alors de '''crises fébriles'''. Environ 2 à 5% des enfants ont une telle crise, la majorité des enfants n'ont qu'une seule crise et les cas où les crises sont récurrentes sont assez rares. De telles crises sont rarement inquiétantes et il est d'ailleurs très rare que ces enfants soient atteints d'épilepsie à l'âge adulte ou lors de l'adolescence, bien que ces crises fébriles puissent rarement indiquer un terrain favorable. Les crises provoquées surviennent aussi quand le cerveau est en souffrance, comme lors d'un AVC, pendant une méningite, juste après un traumatisme crânien, etc. Dans les grandes lignes, voici les causes les plus fréquentes des crises symptomatiques : les traumatismes crâniens, les AVC, les tumeurs et cancers du cerveau, les infections cérébrales (encéphalopathies, méningites), certaines maladies auto-immunes telle la sclérose en plaque, les causes métaboliques (hypoglycémie, hypocalcémie, insuffisance hépatique), la prise de certains médicaments, l'excès d'alcool ou de drogues, l'arrêt de l'alcool. Les crises non-provoquées sont celles qui surviennent alors qu'aucune des raisons précédente n'est présente. Le sujet déclenche une crise épileptique, mais sans raison apparente, sans que son cerveau soit sous l'effet d'un stress quelconque. Divers facteurs peuvent favoriser la survenue d'une crise épileptique non-provoquée chez un patient totalement sain, ou chez un sujet prédisposé. Par exemple, le manque de sommeil favorise la survenue d'une crise. L'exposition à certains stimulus visuels est aussi un facteur déclenchant (d'où l'avertissement présent dans tous les jeux vidéo). Une crise non-provoquée unique n'est pas suffisant pour diagnostiquer l’épilepsie : 50% des personnes ayant subit une crise non-provoquée ont juste une crise unique, sans aucune répétition ultérieure. Les crises épileptiques doivent se répéter pour que le diagnostic d'épilepsie soit posé, et les crises doivent être séparées par un intervalle de temps suffisant (typiquement 24 heures, au moins). Seule la répétition de plusieurs crises non-provoquées permet de poser un diagnostic d'épilepsie. Certaines formes d'épilepsies peuvent cependant être diagnostiquées après une seule crise grâce à un électro-encéphalogramme, si l'EEG est clairement anormal, mais elles sont loin d'être une majorité. ===Les formes familiales et symptomatiques=== Les causes de l’épilepsie sont nombreuses, et varient considérablement chez les patients atteints. Le grand public pense le plus souvent que l'épilepsie est une maladie d'origine génétique. Mais ce n'est réellement le cas que pour une partie des épilepsies, dites '''familales''', dont l'origine est un dysfonctionnement des canaux ioniques et de certains récepteurs synaptiques. D'autres épilepsies ne sont que des conséquences d'un trauma cérébral ancien, comme les épilepsies qui s'installent suite à un AVC ou un traumatisme crânien. On parle alors d'épilepsies '''symptomatiques''', dans le sens où elles ne sont qu'une conséquence d'une maladie cérébrale indépendante : encéphalite, conséquence d'un AVC, tumeur au cerveau, etc. Par exemple, certains patients développent une épilepsie après une méningite ou une encéphalite, parfois pendant l'infection, ou quelques jours/semaines/mois après. D'autres formes d'épilepsie sont liée à une maladie auto-immune, où le corps produits des anticorps qui attaquent certains récepteurs synaptiques ou des les canaux ioniques. Il est assez rare que la transmission de l'épilepsie soit héréditaire. La raison à cela est que la majorité des prédispositions génétiques à l’épilepsie sont codées par plusieurs gènes : la transmission de l'ensemble de ces gènes à l'enfant est alors difficile. De ce fait, il arrive qu'un individu ait des prédispositions génétiques qui favorisent la survenue d'une épilepsie suite à un traumatisme cérébral ou un stress quelconque. Mais les cas où l'épilepsie se transmet de parents à enfants sont assez rares. C'est ce qui explique que l'épilepsie est assez commune dans diverses maladies génétiques, telle la trisomie 21 ou le syndrome de l'X fragile. Il existe cependant un faible nombre d'épilepsies qui se transmettent via un seul gène, et qui sont donc facilement transmissibles à la descendance. Il existe de nombreux syndromes épileptiques reconnus chez le nouveau-né, l'enfant et l'adolescent. Parmi les épilepsies du nouveau-né, on peut citer l''''épilepsie néonatale familiale bénigne''' et les '''benign neonatal seizures'''. Ces deux syndromes sont des formes rares d'épilepsie du nouveau-né, où les convulsions apparaissent dès les premiers jours de vie et cessent à partir du sixième mois de vie. Elles se caractérisent par des crises d'épilepsie focales, de type cloniques pour l'épilepsie néonatale familiale bénigne, clonique et/ou tonique pour les ''benign neonatal seizures''. Elles sont liées à des mutations des gènes KCNQ2 et KCNQ3, ainsi que du gène SCN2A pour certains cas de ''benign neonatal seizures''. Les anomalies se localisent sur le chromosome numéro 20, plus rarement sur le 8ᵉ chromosome. Ces gènes codent les canaux ionique au potassium dépendant du voltage. Les canaux potassiques sont alors moins perméables que la normale, ce qui augmente la teneur en potassium dans les neurones, comparé à la normale. Ces deux syndromes sont relativement bénins et répondent bien aux traitements médicamenteux. Les deux autres syndromes d'épilepsies du nouveau-né ne sont pas bénigne comme le sont les deux précédentes. L{{'}}''Early Myoclonic Encephalopathy'' et le syndrome d'Ohtahara. Elles ont des causes similaires et présentent des anomalies caractéristiques très similaires sur l'EEG. Les deux se manifestent par des crises focales ou généralisées : surtout des spasmes épileptiques pour le syndrome d'Ohtahara, des spasmes accompagnés de crises toniques pour l'autre. La mortalité des très grande dans ces deux maladies, et les enfants qui survient ont généralement un retard mental et moteur important. Comme autre exemple d'épilepsie mono-génétique, les '''épilepsies myocloniques progressives''' viennent en second. Elles sont causées par des mutations du chromosome 6. Enfin, citons l''''épilepsie frontale nocturne''', causée par une mutation autosomale dominante du chromosome 20. ===Les traitements de l'épilepsie === Le traitement de l'épilepsie se fait principalement avec des '''médicaments anti-épileptiques'''. Une autre option, réservée aux cas les plus graves et résistants aux traitements, est la '''chirurgie'''. Celle-ci consiste à retirer la zone où naissent et partent les décharges électriques à l'origine des crises. Une autre option, réservée à certains cas et aujourd’hui de plus en plus rare, consiste à sectionner le corps calleux, pour éviter que les crises se propagent dans l'autre hémisphère que celui de naissance de la crise. La stimulation du nerf vague est aussi efficace, bien que très peu utilisée. Outre les médications, l'éducation du patient sur la maladie est d'une importance capitale : il faut informer le patient sur les mesures à prendre pour éviter les facteurs déclenchant d'une crise. Les anti-épileptiques réduisent l'activité cérébrale générale, en agissant sur les neurotransmetteurs GABA et glutamate, ou en agissant sur les canaux ioniques. Ces anti-épileptiques sont le plus souvent : * des '''modulateurs allostériques positifs des récepteurs GABA''', à savoir des barbituriques ou des benzodiazépines ; * des '''antagonistes compétitifs du glutamate''' ; * des '''bloqueurs des canaux ioniques au sodium et au calcium'''. Parmi les médicaments anti-épileptiques, les plus connus et utilisés sont le valproate de sodium, la carbamazépine, la phénytoïne, les barbituriques, et les benzodiazépines. Le type d'épilepsie joue beaucoup dans le choix du traitement : par exemple, certains traitements sont efficaces sur les crises tonico-cloniques mais inefficaces sur les crises atoniques, tandis que d'autres ont une efficacité inverse. Par exemple, il semblerait que l'ethosuximide soit plus efficace que les autres traitements sur les crises d'absences. De même, les crises d'absences sont bien soignées par le valproate de sodium, alors que la carbamazépine les aggrave ! ==En savoir plus== Ceux qui sont veulent en savoir plus sur l'épilepsie peuvent consulter le site de l'ILAE, ainsi que leur site sur le diagnostic de l'épilepsie : * [http://www.ilae.org/ site de l'ILAE] ; <noinclude> {{NavChapitre | book=Neurosciences | prev=Le système ventriculaire | prevText=Le système ventriculaire | next=La barrière hémato-encéphalique | nextText=La barrière hémato-encéphalique }} </noinclude> {{AutoCat}} 9drk7ipv5op8214ipvlk1ul1lhe1lkp Neurosciences/L'anatomie du système nerveux 0 70087 762832 731925 2026-04-03T13:51:30Z Mewtow 31375 /* La séparation entre système nerveux central et périphérique */ 762832 wikitext text/x-wiki Le présent chapitre va aborder l'anatomie d'un cerveau humain et du système nerveux en général. Nous allons voir la terminologie de base, qui sera abondamment utilisée dans les prochains chapitres. Le système nerveux est subdivisé en plusieurs portions distinctes, et la subdivision en question sera la base des 10 prochains chapitres. Le présent chapitre va donner un aperçu du système nerveux, les prochains chapitre détaillerons l'anatomie du cerveau, de la moelle épinière, etc. ==Le système nerveux== [[File:Nervous system - Nervous system 1 -- Smart-Servier.png|vignette|upright=0.75|Système nerveux]] Le système nerveux d'un être humain est illustré ci-contre. Il illustre évidemment le '''cerveau''', situé dans le crâne. Du cerveau sortent un paquet de connexions qui relient le cerveau au reste du corps, illustrées en orange/jaune. Du cerveau sort la '''moelle épinière''', long câble qui longe le dos, pas loin de la colonne vertébrale (faites d'os nommés les vertèbres). De la moelle épinière sortent de longs câbles appelés des '''nerfs''' qui se dispersent dans le corps. Ils innervent la peau, les tissus mous, les organes internes, bref : tout ce qui est sensible ou peut bouger. Ils sont classés en plusieurs types, à savoir les nerfs moteurs, sensitifs et mixtes. Les nerfs sensitifs/sensoriels transmettent les sensations provenant du corps vers le système nerveux central. Les nerfs moteurs sont connectés aux muscles et assurent la transmission des ordres provenant du cerveau ou de la moelle épinière, vers les muscles. Les nerfs mixtes regroupent des axones sensoriels et moteurs. De plus, on trouve un peu partout dans le corps des amas de neurones appelés des '''ganglions''', connectés à la moelle épinière et/ou au cerveau. Pour simplifier, ils servent de relai entre les neurones dispersés dans le corps et la moelle épinière. Ces ganglions sont plus précisément l'endroit où naissent les nerfs, dont ils sont indissociables. Comme pour les nerfs, on fait la différence entre ganglions moteurs, sensitifs et mixtes, selon le type de nerf auxquels ils donnent naissance. Les neurones ne sont pas exactement les mêmes selon que le ganglion soit moteur ou sensitif. ===La séparation entre système nerveux central et périphérique=== [[File:Nervous system diagram unlabeled.svg|vignette|upright=0.75|Distinction entre système nerveux central en orange/jaune, et système nerveux périphérique en bleu.]] Il est d'usage de regrouper le cerveau, la moelle épinière et la rétine dans un seul ensemble, appelé '''système nerveux central''', le reste étant appelé le '''système nerveux périphérique'''. Le système nerveux périphérique est ce qui reste du système nerveux quand on retire le cerveau et la moelle épinière. Il innerve la peau, les organes internes, les muscles et quelques autres structures anatomiques. Une telle dénomination n'est pas anodine. Elle trahit le rôle du système nerveux central, qui est d'intégrer les informations transmises par le système nerveux périphérique. Il traite les informations sensorielles qu'il reçoit et commande les neurones moteurs, ce qui explique qu'il contient exclusivement des interneurones (sauf dans la rétine), alors que le système nerveux périphérique contient surtout des neurones sensoriels et moteurs. Ce dernier contient notamment de nombreux récepteurs, qui captent de la lumière (pour la vision), le son (pour l’ouïe), etc. Le cerveau est le centre de traitement principal, mais la moelle épinière a aussi un léger rôle de traitement. Elle prend notamment en charge la plupart des réflexes, et contrôle indirectement l'intensité de certaines sensations douloureuses. On pourrait croire que cette distinction est purement arbitraire, mais elle se base en réalité sur de nombreux arguments anatomiques et embryologiques assez subtils. Déjà, les cellules gliales du système nerveux central et périphériques ne sont pas exactement les mêmes : les oligodendrocytes du système nerveux central sont remplacés par les cellules de Schwann dans le système nerveux périphérique, par exemple. Ensuite, les neurotransmetteurs ne sont pas tout à fait les mêmes, si on regarde avec attention. Par exemple, le système nerveux périphérique moteur est très friand d’acétylcholine et de noradrénaline, là où le système nerveux central est surtout composé de neurones GABAergiques ou sensibles au glutamate. Plus de 70% des neurones du cerveau sont sensibles au glutamate, quand ils n'en émettent pas eux-mêmes, les 20% restants étant surtout des interneurones inhibiteurs GABAergiques. Ensuite, les deux ne se forment pas de la même manière. Nous verrons dans le chapitre sur l'embryologie du système nerveux que les deux apparaissent à des endroits différents de l'embryon et qu'ils se forment séparément. A ce propos, il existe des maladies néonatales qui empêchent le système nerveux périphérique de se former correctement, mais qui laissent intacte le système nerveux central. Et inversement, certaines maladies néonatales font que le cerveau et la moelle épinière ne se forment pas correctement, mais laissent le système nerveux périphérique intact. Une dernière distinction est que le système nerveux central est protégé par les '''méninges''', une enveloppe protectrice qui entoure le cerveau et la moelle épinière. Leur rôle principal est d'amortir les chocs que peut recevoir le cerveau ou la moelle épinière. Mais les méninges ont aussi des rôles secondaires : participer à l'immunité du système nerveux, apporter des nutriments aux neurones, éliminer les déchets du métabolisme neuronal, etc. Pour ces rôles secondaires, les méninges contiennent un liquide, le '''liquide cérébrospinal''', suffisamment visqueux pour amortir les chocs, mais suffisamment liquide pour transporter déchets et nutriments. Il y a un liquide équivalent pour les nerfs, mais pas de barrière de protection aussi développée que les méninges. Au passage, la rétine des yeux, le nerf optique et le nerf olfactif font partie du système nerveux central. La rétine est une extension du cerveau en dehors de la boite crânienne. La rétine des yeux contient des neurones pour capter la lumière, ainsi que des cellules gliales. Les cellules gliales sont d'ailleurs des cellules gliales qu'on trouve dans le système nerveux central, pas celles du système nerveux périphérique. Le nerf optique est le prolongement de la rétine et ne contient que des axones provenant de la rétine. Le nerf optique est protégé par les méninges, ce qui le place d'office dans le système nerveux central. ===La microanatomie du système nerveux=== A vue d’œil, le système nerveux périphérique est totalement différent du système nerveux central. Il est composé de nerfs et de ganglions, alors que le cerveau et la moelle épinière sont totalement différents. Quand on regarde le cerveau ou la moelle épinière à l’œil nu, on voit qu'ils sont composés de parties grises et de parties blanches, assez bien séparées. Les neurones forment la '''matière grise''', alors que les connexions entre neurones forment la '''matière blanche'''. Il y a donc une sorte de ségrégation entre les axones d'un côté, et les neurones/dendrites de l'autre. Dans le système nerveux périphériques, la séparation n'est pas aussi stricte, mais elle existe. Les nerfs sont composés de matière blanche (vu que de sont des regroupements d'axones), alors que les neurones sont regroupés dans les ganglions. le système nerveux périphérique est composé de nerfs et de ganglions. Et les deux se retrouvent dans le système nerveux central ! Dans le SNC, on trouve des amas de neurones appelés des '''noyaux''', de forme approximativement sphériques. Ils sont équivalents aux ganglions du système nerveux périphérique. Les amas de neurones sont appelés des ganglions dans le système nerveux périphérique, mais des noyaux dans le système nerveux central pour les noyaux. La distinction est purement arbitraire et n'est qu'une simple différence terminologique. Les nerfs ont aussi un équivalent dans le système nerveux central, qui porte le nom de '''faisceaux'''. Ils regroupent des paquets d'axones dans des fibres. On les trouve dans la moelle épinière, avec des faisceaux sensoriels qui transmettent les sensations du corps vers le cerveau, et des faisceaux moteurs qui partent du cerveau pour aller vers les muscles. Il y a aussi quelques faisceaux dans le cerveau, qui servent surtout à connecter entre les hémisphères cérébraux. Le cerveau, la moelle épinière et les ganglions/nerfs forment la portion visible à l’œil nu du système nerveux, mais ce n'est pas la seule. Le système nerveux contient des neurones, des cellules gliales, et quelques autres cellules de soutien. Et leur organisation diffère selon que l'on parle du système nerveux central ou périphérique. Le système nerveux périphérique et central ont des organisation similaires si on les regarde au microscope, mais les détails sont différents. Le SNC et le SNP sont donc composés de noyaux/ganglions qui sont interconnectés par des nerfs/faisceaux, ainsi que par des connexions plus courtes non regroupées appelées '''fibres nerveuses'''. Mais il y a aussi d'autres structures anatomiques qu'on ne trouve que dans le système nerveux central. Par exemple, les neurones du SNC sont organisés en noyaux, mais aussi en couches superposées. La grosse majorité des neurones du cerveau et de la moelle épinière forment des couches de neurones : on parle alors de '''cortex'''. La moelle épinière est majoritairement composée de cortex, avec quelques noyaux perdus dedans, il en est de même pour le cerveau. Le SNP n'a pas de cortex. Le système nerveux périphérique a aussi ses spécificités. Outre les ganglions, nerfs et fibres, le SNP contient aussi des neurones dispersés spécifiques au système nerveux périphérique. Des cellules nerveuses sont dispersées dans le corps et servent pour le toucher et les autres sensations. La peau est remplie de neurones sensibles au toucher, les muscles incorporent des récepteurs à l'étirement, les organes internes ont des récepteurs à la douleur, etc. Et ces '''neurones sensoriels''' sont dispersés dans la peau et/ou les organes, sans regroupement apparents. Ils émettent des axones qui se regroupent pour former des nerfs sensitifs, eux-mêmes reliés à la moelle épinière. ===L'évolution du système nerveux=== [[File:Human-leech-nervous-system-comparison.png|vignette|Comparaison entre le système nerveux d'un vers de terre et celui d'un humain.]] Précisons que ce que l'on vient de dire ne marche que pour les vertébrés et quelques autres animaux. Les animaux les plus simples ont un système nerveux plus rudimentaire. Sur les animaux invertébrés les plus simples, il n'y a pas de cerveau, ni de moelle épinière. Le système nerveux est juste composé de ganglions et de nerfs. Les ganglions sont répartis sur toute la longueur de l'animal, comme c'est le cas chez les vers plats. Il n'y a pas vraiment de moelle épinière qui parcours la longueur de l'animal, mais juste des nerfs entre ganglions successifs. Les ganglions n'ont pas tous la même taille et celui au niveau de la tête est plus gros que les autres, car les organes sensoriels sont souvent proches de la tête. Mais ce dernier n'est pas vraiment un cerveau, même s'il est parfois appelé comme tel par abus de langage. Par la suite, les ganglions au niveau de la tête ont commencé à grossir et à ses regrouper, pour donner une ébauche de cerveau. Ce processus de regroupement de tissus nerveux et d'organes sensoriels au niveau de la tête porte un nom : c'est le processus de '''céphalisation'''. C'est ainsi qu'est apparu le cerveau et la moelle épinière. Mais les ganglions autrefois présents sur toute la longueur de l'animal sont restés : ce sont les ganglions spinaux ! L'apparition du cerveau et de la moelle épinière fait que l'anatomie du système nerveux s'est fortement complexifiée. Pour résumer rapidement, le système nerveux a évolué d'une manière de plus en plus centralisée. La situation de départ était un système nerveux très décentralisé, composé d'un filet de neurones interconnectés entre eux. Puis, les neurones se sont progressivement regroupés, d'abord en ganglions, puis en un cerveau unique. La première phase de regroupement a formé des ganglions connectés entre eux, dont certains envoyaient des nerfs vers le reste du corps. Puis la seconde phase a regroupé et fusionné les ganglions, pour donner un cerveau. Les connexions avec le reste du corps se sont complexifiées et une moelle épinière est apparue en plus des nerfs proprement dit. Le système nerveux périphérique correspond approximativement aux structures anatomiques qui existaient avant céphalisation (ganglions et nerfs), alors que le système nerveux central correspond aux structures apparues après (cerveau et moelle épinière). Le système nerveux correspond donc aux structures centralisées, là où le système nerveux périphérique correspond aux neurones dispersés dans l'ensemble du corps. ==Le cerveau : tronc cérébral, cervelet et cerveau antérieur== Chez les espèces qui en ont un, le cerveau est de loin la structure qui contient le plus de cellules, celle dont la fonction est la plus importante. Le cerveau joue le rôle d'une sorte de centre de contrôle du corps, qui reçoit les informations sensorielles, les traite, et commande les muscles via la moelle épinière. Le cerveau contient de nombreuses aires différentes, que les scientifiques ont mis du temps à cartographier. Elles sont tellement nombreuses que trouver un plan d'organisation en se basant sur la fonction de chaque aire n'est pas quelque chose de facile. Pour le moment, nous allons juste dire que le cerveau est subdivisé en plusieurs structures anatomiques distinctes : le cervelet, le tronc cérébral et le reste (appelé parfois « cerveau » ou forebrain en anglais). [[File:Diagram showing some of the main areas of the brain CRUK 188.svg|vignette|Le cerveau antérieur, le cervelet et le tronc cérébral.]] Le '''cervelet''' est une sorte de mini-cerveau, posé sur le tronc cérébral, qui contient autant de neurones que le reste du cerveau ! Son rôle est essentiellement moteur : il corrige les mouvements fins, en corrigeant de potentielles erreurs de trajectoire. Lorsqu'il est endommagé, les mouvements sont maladroits, semblables à ceux d'une personne ivre. D'ailleurs, la démarche d'une personne ivre provient de l'action inhibitrice de l'alcool sur le cervelet. Le '''tronc cérébral''' se situe dans le prolongement de la moelle épinière, en dessous des structures cérébrales plus complexes. Il n'a pas une fonction unique, mais prend en charge tout un ensemble de fonctions assez complexes. Pour simplifier, il s'occupe des fonctions essentielles pour la survie : état de veille, respiration, rythme cardiaque, respiration, vomissement, digestion, etc. Le tronc cérébral est un regroupement de noyaux et de faisceaux, il ne possède pas de cortex. On verra dans quelques chapitres qu'il est segmenté en trois structures anatomiques différentes, qui portent les doux noms de myélencéphale, métencéphale et mésencéphale. Le est appelé le '''cerveau antérieur'''. Il prend en charge tout le reste, qu'il s'agisse de fonctions de base (homéostasie au niveau de l'hypothalamus) que la motricité, les sensations ou les fonctions intellectuelles de haut-niveau. Il est majoritairement composé de cortex, notamment du néocortex pour sa surface. Il regroupe deux structures anatomiques appelées diencéphale et télencéphale. Il est aussi découpé en deux hémisphères séparés. ==Le système nerveux périphérique== Le système nerveux périphérique est composé de neurones sensoriels dispersés dans le corps, de nerfs et d'amas de neurones appelés « '''ganglions''' ». Intuitivement, on se dit que le système nerveux périphérique est subdivisé en un sous-système sensoriel et un sous-système moteur. Le sous-système sensoriel contient tout ce qui permet de capter des signaux comme le toucher, la douleur, la température de la peau, etc. Le système nerveux moteur correspond aux axones des neurones moteurs qui sortent du système nerveux central pour innerver les muscles. Et concrètement, les deux systèmes sont assez bien séparés, que ce soit dans la moelle épinière, les nerfs, les ganglions, etc. ===Le système nerveux moteur, sympathique et parasympathique=== Le système nerveux moteur est lui séparé en deux sous-systèmes distincts, appelés système somatique et autonome. * Le '''système somatique''' prend en charge la motricité volontaire, les mouvements contrôlés consciemment. Il commande les muscles pour produire des mouvements volontaires, ainsi que pour garder l'équilibre. * Le système somatique est à contraster avec le '''système nerveux autonome''' qui prend en charge les mouvements automatiques, inconscients. Plus précisément, il commande les organes internes, comme le foie, le cœur, les poumons, bref : les muscles dits lisses qui ne sont pas en charge des mouvements volontaires (et aussi certaines glandes). * Le '''système nerveux entérique''' est lié au système digestif. Il est parfois considéré comme une portion du système autonome, parfois comme un système à part. Si on omet le système nerveux entérique, le système nerveux autonome est composé de deux sous-systèmes antagonistes, appelés systèmes sympathique et parasympathique. La fonction de ces deux systèmes est souvent résumée grossièrement en disant que le système sympathique prépare au "combat ou à la fuite", alors que le système parasympathique "digère et dort". Et ce n'est pas très loin de la réalité, le système sympathique dépensant l'énergie en situation de stress, alors que le système parasympathique favorise la conservation de l'énergie en permettant la récupération et le repos. * Le '''système nerveux sympathique''' est celui qui prépare au combat ou à la fuite. Il s'active lors des situations de danger ou de stress, sous l'action du cerveau. Dans ces situations, le système nerveux va avoir divers effets sur le cœur, les glandes, et les muscles lisses. Dans les grandes lignes, toute l'énergie du corps va être mobilisée. * Le '''système nerveux parasympathique''' a un effet strictement inverse à celui du système sympathique : il commande le corps quand aucun danger n'est présent, en situation relaxante. Il ralentit le rythme cardiaque et la respiration, favorise la digestion, etc. Voici les actions du système nerveux sympathique et parasympathique. {|class="wikitable" |- ! ! Sympathique ! Parasympathique |- ! Système cardiovasculaire | Augmente le débit cardiorespiratoire : * Augmente du rythme cardiaque * Augmente la pression artérielle par vasoconstriction * Augmente le débit respiratoire en dilatant les bronches | Réduit le débit cardiorespiratoire : * Réduit le rythme cardiaque * Baisse la pression artérielle par vasodilation * Réduit le débit respiratoire en contractant les bronches |- ! Système digestif | Stoppe la digestion : * réduit la salivation ; * stoppe les mouvements digestifs ; * stoppe la sécrétion de sucs digestifs ; * Vasocontraction locale. | Favorise la digestion : * augmente la salivation ; * stimule la motricité intestinale et stomacale ; * stimule les sécrétions digestives gastriques, hépatiques et pancréatiques ; * Vasodilatation locale. |- ! Système hormonal et glandes | * favorise la production de glucagon par le foie. * Stimule la sécrétion d'hormones du stress par la glande surrénale * Stimule la transpiration | * stimule la production d'insuline par le pancréas. |- ! Yeux | Dilate la pupille || Ferme la pupille, accommode la vision |- ! Système urinaire | Défavorise la miction || Favorise la miction par détente des muscles urinaires |} Les systèmes sympathiques et parasympathiques innervent les yeux, le nez, les glandes salivaires, le cœur, les vaisseaux sanguins de grande taille, les poumons, le système digestif, les reins et la vessie, les organes génitaux. Le système sympathique innerve aussi les grandes surrénales (des glandes posées sur le rein qui secrètent diverses hormones dans le sang). Elles sont innervées uniquement par le système sympathique. Il faut dire que ces glandes sont impliquées dans la réaction sympathique au stress, à savoir qu'elles libèrent de l'adrénaline ou de la noradrénaline quand le cerveau le demande. Les glandes sudoripares sont elles aussi stimulées par le système nerveux sympathique uniquement. À quelques exceptions près, les organes innervés par le système nerveux autonome le sont à la fois par le système sympathique et le système parasympathique. Pour prendre un exemple, c'est le cas du cœur, qui est innervé par le nerf vague (parasympathique) et par le plexus cardiaque (sympathique). Les deux systèmes ont des actions antagonistes. Pour l'exemple du cœur, le système sympathique accélère le rythme cardiaque, alors que le parasympathique le ralentit. Cependant, quelques organes sont innervés par un seul système et pas par l'autre, l'exemple le plus notable étant les glandes surrénales. Les deux schémas ci-dessous illustre les nerfs et ganglions pour le système sympathique et parasympathique, mais nous détaillerons le tout dans un chapitre ultérieur. Les deux impliquent des nerfs foncièrement différents. Les nerfs du système nerveux sympathique sortent tous de la moelle épinière, alors que ceux du système parasympathique sortent du cerveau (à l'exception du bas de la moelle épinière). {| |[[File:Blausen 0838 Sympathetic Innervation.png|centre|vignette|upright=2.0|Innervation du système nerveux sympathique.]] |[[File:Blausen 0703 Parasympathetic Innervation.png|centre|vignette|upright=2.0|Innervation du système nerveux parasympathique.]] |} ===Le système nerveux entérique=== Moins connu, le '''système nerveux entérique''' est une subdivision du système nerveux répartie dans le tube digestif. On le trouve plus précisément dans l’œsophage, l'estomac, l'intestin. Il est composé de ganglions répartis dans tout le tube digestif et contient un grand nombre de neurones. On estime que ce système nerveux comprend entre 200 et 600 millions de neurones, ce qui est à peu-près le même nombre de neurones que le cerveau ou la moelle épinière, ce qui lui vaut le nom abusif de "deuxième cerveau". Cependant, les fonctions du système nerveux entérique et du cerveau sont loin d'être comparables. Le système nerveux entérique ne fait que commander quelques réflexes indépendamment de la moelle épinière ou du cerveau. Précisément, il commande des réflexes qui vont de la sécrétion de mucus ou de substances chimiques dans l'intestin à la commande du flux sanguin du tube digestif en passant par la motricité intestinale. Le système nerveux entérique utilise de nombreux neurotransmetteurs comma la sérotonine ou la dopamine. On estime que plus de 50% de la dopamine et 90% de la sérotonine est produite dans l'intestin et y agissent. Cela a poussé certains scientifiques à supposer une influence du système nerveux entérique sur l'humeur ou la cognition, bien que les preuves soient faibles. On voit mal comment les neurotransmetteurs produit par le tube digestif pourraient passer la barrière hémato-encéphalique (une couche de protection qui empêche certaines substances d'arriver au cerveau) et influencer le cerveau. Les médias ont beaucoup monté en épingle la relation entre cerveau et système nerveux entérique, notamment en mettant en avant de possibles interactions de l'intestin sur la santé mentale ou neurologique. Par exemple, il a été rapporté une corrélation entre problèmes intestinaux et maladie de Parkinson ou schizophrénie. On sait que les malades de Parkinson et les schizophrènes ont plus de problèmes digestifs que la population générale, sans que l'on sache dans quel sens va la causalité ou s'il y a une raison intermédiaire. Il a été parfois dit que l'usage de probiotiques (de la flore intestinale en poudre, pour simplifier) pourrait améliorer l'humeur de patients dépressifs ou schizophrènes ou soigner certaines maladies neurodégénératives. Mais au-delà de quelques corrélations difficiles à interpréter, les preuves validant ces allégations sont rares ou douteuses et il n'est pas impossible qu'elles ne soient que chimères. ===Le système nerveux sensoriel=== Le système nerveux périphérique sensoriel est lui aussi subdivisé en plusieurs systèmes différents. La première division est liée aux cinq sens. Vous avez peut-être vu qu'il existe 5 sens principaux : toucher, vision, audition, gout, odorat. Il s'agit là d'une simplification, car il faut idéalement ajouter d'autres sens comme la perception de la douleur, de la température, de la position des membres, et quelques autres. Mais pas besoin de rentrer dans ce genre de détails pour le moment. Toujours est-il que la subdivision sépare les '''sens spéciaux''' des autres. Les sens spéciaux ont un organe spécialisé pour leur perception : l'oreille pour l'audition et l'équilibre, la langue pour le gout et l'odorat, les yeux pour la vision. Les autres sens sont perçus par des récepteurs sensoriels dispersés dans le corps tout entier. La séparation entre sens spéciaux colle aussi avec une autre distinction. Les sens spéciaux ne passent pas par la moelle épinière, mais passent par des nerfs crâniens qui entrent directement dans le cerveau. Les autres sens passent par le relai de la moelle épinière. Les nerfs et faisceaux sensoriels sont ainsi classés en plusieurs types, avec une distinction entre les nerfs spéciaux et les nerfs/faisceaux généraux. Les nerfs spéciaux regroupent plusieurs nerfs. Les deux nerfs principaux sont le nerf optique qui sort des yeux, le nerf vestibulo-cochléaire qui sort des oreilles et transmet l'audition. Il comprend aussi plusieurs fibres liées à la transmission du gout, dispersées dans plusieurs nerfs du visage. Les nerfs/faisceaux généraux sont eux subdivisés en deux catégories : une pour la transmission des sensations conscientes, l'autre pour les sensations inconscientes. La distinction est assez proche de la différence entre système moteur somatique et autonome, mais pour le système sensoriel. On fait ainsi la différence entre des nerfs/faisceaux somatiques et viscéraux. Le '''système sensoriel somatique''' transmet les sensations conscientes, à savoir le toucher, la douleur, la perception de la température. Les sensations sont captées par la peau, mais aussi par les organes internes pour la douleur (avoir mal au ventre, au cœur, etc). Par contre, le '''système sensoriel viscéral''' transmet des sensations inconscientes, liées aux organes internes, aux vaisseaux sanguins. Il sert à mesurer en permanence la tension artérielle, l'état de l'abdomen, etc. <noinclude> {{NavChapitre | book=Neurosciences | prev=Le codage neuronal | prevText=Le codage neuronal | next=La moelle épinière | nextText=La moelle épinière }}{{autocat}} </noinclude> 8wzh01zqki19gfc2eigupogmcy7q7vs Neurosciences/Le tronc cérébral 0 70501 762835 731798 2026-04-03T14:16:36Z Mewtow 31375 762835 wikitext text/x-wiki [[File:Brain sagittal section stem highlighted.svg|vignette|Tronc cérébral.]] Le tronc cérébral est une partie du cerveau qu'il est très difficile de décrire simplement. Sa complexité fait qu'il vaut mieux lui dédier un chapitre complet. Le tronc cérébral contient à la fois de la substance grise (neurones) que de la substance blanche (axones myélinisés). La substance blanche est surtout composée de faisceaux qui poursuivent les voies ascendantes/descendantes et traversent le tronc cérébral sur la majeure partie de sa longueur. La matière grise se limite à des noyaux enfouis dans la substance blanche et ne contient aucun cortex (couches de neurones). Et ces noyaux sont très nombreux. Les noyaux du tronc cérébral regroupent des noyaux moteurs, des noyaux sensoriels, des noyaux mixtes sensimoteurs, des noyaux d'association, tous les types sont représentés. D'autres noyaux ont des rôles plus larges ou mal connus, avec une influence sur la cognition, le noyau du Raphé en étant un bon exemple. Ils contiennent aussi les '''noyaux des nerfs crâniens''', qui sont tous dans le tronc cérébral. Une bonne partie de ces noyaux sera étudié en détail dans les chapitres portant sur les sens ou la motricité. Nous allons cependant survoler les principaux noyaux, en donnant si possible quelques détails sur leur fonctionnement dans ce qui suit. ==L'anatomie en largeur (coupe-section) du tronc cérébral== [[File:Coupe section du tronc cérébral.png|vignette|Coupe section du tronc cérébral.]] Dans cette section suivante, nous verrons comment s'organise le tronc cérébral sur sa largeur, en coupe-section. On s’aperçoit alors que le tronc cérébral est composé de deux à trois subdivisions selon l'étage. On distingue une '''région ventrale''', une région dorsale appelée le '''tegmentum''', et une région qui n’apparaît que dans le mésencéphale : le '''tectum'''. La région ventrale est séparée du tegmentum par l’aqueduc de Sylvius, une sorte de canal qui relie le 3ème et le 4ème ventricule (on abordera les ventricules plus tard, dans un futur chapitre). La région ventrale contient surtout des faisceaux de matière blanche, dont la plupart se prolongent dans la moelle épinière. Tel est le cas du faisceau cortico-spinal, qui transmet les commandes motrices volontaires. Le tegmentum est lui-même composé de plusieurs formations. Parmi celles-ci, on trouve la formation réticulée, un lacis de noyaux et de fibres nerveuses assez complexe, lui-même formé de trois colonnes. ===La formation réticulée=== La '''formation réticulée''' est une structure complexe, qui mélange divers noyaux et un ensemble de fibres entrelacées très difficile à décrire. La formation réticulée a la forme d'une bande grise qui court sur toute la longueur du tronc cérébral. Elle est entremêlée avec divers noyaux, dont les noyaux des nerfs crâniens. Cette formation émet des axones dans tout le cerveau, et notamment dans la totalité du télencéphale. La formation réticulée est divisée en trois colonnes : une colonne centrale qui contient les noyaux du raphé, une colonne médiane et une colonne latérale. La colonne médiane est composée de neurones de grande taille, alors que la colonne latérale est composée de neurones de petite taille. Les scientifiques ont depuis longtemps subdivisé la formation réticulée en deux systèmes fonctionnels distincts : un système réticulé ascendant/activateur qui maintient l'état d'éveil, et un système réticulé descendant. Le '''système réticulaire descendant''' a plusieurs fonctions, qui vont de la modulation de la douleur à la commande de la motricité extrapyramidale. Il est à l'origine d'un des faisceaux de la substance blanche de la moelle épinière, le faisceau réticulospinal, une sous-portion du faisceau extrapyramidal (n'hésitez pas à consulter le chapitre sur la moelle épinière pour des rappels). La formation réticulée descendante commande les muscles du maintien de la posture, ainsi que les muscles qui gèrent les mouvements fins, précis. D'autres portions de ce faisceau jouent un rôle dans la modulation de la douleur. En comparaison, le '''système réticulaire ascendant''' joue un grand rôle dans l'état de veille, l'attention, la vigilance et l'activation physiologique (l'intensité de l'état de veille). Lors de l'éveil, ces axones excitent les aires cérébrales de destination, donnant naissance à une activité électrique minimale au repos, ce qui permet de maintenir le sujet éveillé ou conscient. Lors du sommeil, cette formation diminue son activité, mais ne l'annule pas totalement. Une lésion de cette structure peut causer un coma irréversible, alors que sa stimulation induit un éveil soutenu. Outre son rôle dans la vigilance et l'éveil, elle influence aussi la perception de la douleur ainsi que la motricité. Nous reparlerons plus loin de ces deux systèmes, dans les chapitres sur la motricité extrapyramidale et le sommeil. ====La colonne centrale : les noyaux du Raphé==== Les '''noyaux du Raphé''' sont des noyaux regroupés dans la colonne centrale de la formation réticulée, cette colonne contenant plusieurs noyaux. Suivant les nomenclatures, le nombre de noyaux du Raphé varient, allant de seulement deux à trois noyaux à plus de neufs noyaux notés B1, B2, ... , B9. Certains de ces noyaux ont un nom plus commun : le noyau obscurus, le noyau pallidus, le noyau magnus, le noyau pontin, le noyau central supérieur et le noyau dorsal. Les noyaux localisés dans la moelle allongée (bulbe rachidien) innervent la moelle épinière, et notamment les interneurones qui transmettent la douleur. Ces innervations permettent de contrôler les sensations douloureuses, leur intensité. Les noyaux localisés dans le reste du tronc cérébral secrètent de la sérotonine dans tout le cerveau. Les axones de ces noyaux innervent aussi bien le cervelet que le cortex ou d'autres structures cérébrales. Ces axones qui innervent le cerveau ont un effet stimulant sur celui-ci, qui maintient l'état d'éveil. Par contre, une lésion de ces centres induit une insomnie. Vu leur rôle, on devine rapidement qu'ils font partie intégrante du système réticulée ascendant. Ils sont aussi impliqués dans la régulation de l'humeur, avec un effet indirect sur l'impulsivité et l'agressivité. La raison à cela est que la sérotonine, émise en grande partie (mais pas seulement) par les noyaux du raphé, a un rôle prédominant dans la régulation de l'humeur. [[File:Serotonergic neurons.svg|centre|vignette|upright=1.5|Cette image montre les axones émis par les noyaux du Raphé. On voit que ceux-ci innervent la quasi-totalité du télencéphale, ainsi que le cervelet et certaines parties du diencéphale.]] ===Les noyaux des nerfs crâniens=== [[File:Gray697.png|vignette|Nerfs crâniens moteurs.]] [[File:Gray698.png|vignette|Nerfs crâniens sensoriels.]] Le tronc cérébral contient aussi les noyaux des nerfs crâniens, à l'exception des nerfs olfactifs et optiques. Les nerfs optiques et olfactifs forment un groupe à part, vu qu'ils sont dans le cerveau antérieur (le diencéphale, pour être précis) et qu'ils font partie du système nerveux central. Les noyaux des nerfs crâniens sont classés en plusieurs groupes, selon leur origine embryonnaire/évolutive. Les nerfs hypoglosse et vestibulo-cochléaire sont chacun une catégorie à part. Le reste sépare les nerfs oculomoteur et branchiaux. Les cinq '''nerfs branchiaux''' s’appellent ainsi car ils sont l'équivalent modernes des nerfs qui innervent les branchies des poissons. {|class="wikitable" |+ Nerfs crâniens du système nerveux périphérique |- !rowspan="3"| Nerfs oculo-moteurs |Nerf oculo-moteur commun (III) |- |Nerf trochléaire ou pathétique (IV) |- |Nerf oculo-moteur externe ou abducens (VI) |- !rowspan="5"| Nerfs branchiaux |Nerf trijumeau (V) |- |Nerf facial (VII) |- |Nerf glosso-pharyngien (IX) |- |Nerf vague ou pneumogastrique (X) |- |Nerf spinal ou accessoire (XI) |- !colspan="2"| Nerf hypoglosse (XII) |- !colspan="2"| Nerf vestibulo-cochléaire ou auditif (VIII) |} [[File:Brain stem sagittal.svg|centre|vignette|upright=2|Section du tronc cérébral avec la position des noyaux des nerfs crâniens.]] Les noyaux des nerfs crâniens sont regroupés en plusieurs colonnes de noyaux alignés sur la longueur du tronc cérébral. De plus, les colonnes de noyau sont spécialisées, avec des colonnes composées uniquement de noyaux moteurs, d'autres uniquement de noyaux sensoriels, une autre uniquement de noyaux parasympathiques, etc. Cette organisation dérive de l'évolution embryonnaire du cerveau. Le système nerveux embryonnaire démarre sous la forme d'un tube qui ressemble à une ébauche de moelle épinière : le tube neural. Par la suite, le tube neural se scinde en plusieurs colonnes, qui elles-mêmes se subdivisent en noyaux sur leur longueur. Une fois formés, les noyaux des nerfs crâniens ne migrent pas et restent en place. Leur position conserve donc l'organisation en colonnes sur une bonne partie du tronc cérébral. [[File:Cranial Nerves Apparent Orrigins.jpg|centre|vignette|upright=1.5|Origines apparentes des nerfs crâniens.]] Il existe en tout 6 colonnes de noyaux pour les nerfs crâniens. Il faut faire la différence entre les colonnes motrices et les colonnes sensorielles. Il y a aussi une différence entre les colonnes somatiques et viscérales. Les colonnes somatiques sont pour la motricité et la perception consciente, alors que les colonnes viscérales s'occupent de la motricité/sensibilité inconsciente. En tout, cela fait quatre colonnes, dont deux sont en doubles. Les colonnes motrices regroupent trois colonnes, une colonne somatique, une colonne viscérale non-spécialisée, et une colonne parasympathique. {|class="wikitable" |+ Colonnes motrices |- ! Colonne motrice somatique GSE ! Colonne motrice parasympathique GVE ! Colonne motrice viscérale SVE |- | Noyaux oculomoteurs : * Noyau oculomoteur * Noyau trochléaire * Noyau abducens | Noyaux des nerfs brachiaux : * Noyau moteur du nerf trigéminal * Noyau moteur du nerf facial * Noyau ambigu (nerfs glossopharyngéal, vague et accessoire) | Noyau oculomoteur accessoire d'Edinger-Westphal |- | Noyau hypoglosse || || Noyau salivaire |- | || || Noyau dorsal du nerf vague |} Les colonnes sensorielles sont elles aussi au nombre de trois. {|class="wikitable" |+ Colonnes sensorielles |- ! Colonne sensorielle somatique générale ! Colonne sensorielle somatique spéciale ! Colonne sensorielle viscérale |- | rowspan="3" | Nerfs du nerf trigéminal : * Noyau sensoriel principal du nerf trigéminal * Noyau spinal du nerf trigéminal * Noyau mésencéphalique du nerf trigéminal | rowspan="3" | Noyaux du nerf cochléo-vestibulaire * Noyau cochléaire * Noyau vestibulaire | Noyau du tractus solitaire |- | Noyau dorsal du nerf vague |- | Noyau gustatif |} ==L'anatomie en longueur du tronc cérébral== On a vu il y a quelques chapitres que le cerveau se développait à partir de cinq protubérances : le myélencéphale, le métencéphale, le mésencéphale, le diencéphale et le télencéphale. Au cours du développement, celles-ci se subdivisent en aires cérébrales distinctes. Le tronc cérébral regroupe myélencéphale, métencéphale et mésencéphale, tandis que le reste du cerveau contient le diencéphale et le télencéphale. On a ainsi une première subdivision du tronc cérébral en trois étages, sur sa longueur. * Le myélencéphale est aussi appelé le '''bulbe rachidien''', ou encore la moelle allongée. Ce dernier terme illustre bien l'anatomie du myélencéphale, qui est similaire à celle de la moelle épinière. La matière grise est localisée au centre de la moelle allongée, la substance blanche entourant celle-ci. La différence avec la moelle épinière tient dans la présence de quelques noyaux, sources de nerfs crâniens divers. * Le '''pont de Varole''', ou métencéphale, se situe en dessous du cervelet. Il ressemble à une sorte de renflement sur la face ventrale du tronc cérébral. Il sert de relai aux axones moteurs et sensoriels et prend en charge certaines fonctions autonomes. De nombreux noyaux de nerfs crâniens sont présents dans le pont, comme les noyaux des nerfs vestibulaires, salivaires, etc. * Le '''mésencéphale''' est une structure beaucoup plus complexe, qui surmonte toutes les autres. [[File:Tronc-cerebral-ventrale.jpg|centre|vignette|upright=2.0|Tronc cérébral.]] Dans ce qui va suivre, nous allons voir les noyaux qui sont localisés uniquement dans une des trois sections précédentes. Nous ne reparlerons pas des noyaux de nerfs crâniens ou de la formation réticulée, mais verrons surtout les noyaux qui n'appartiennent pas à ces structures. ===Le myélencéphale=== Les noyaux de la moelle allongée sont essentiellement consacrés à la gestion du système nerveux périphérique autonome, qu'il soit sympathique ou parasympathique. Ils gèrent ainsi la pression sanguine, le rythme cardiaque, la respiration, ainsi que les réflexes de salivation, vomissement, mastication et autres. On y trouve le '''centre respiratoire''', un noyau qui s'occupe de l'inspiration et de l'expiration. Ce centre reçoit des informations comme le PH et le taux de dioxyde de carbone dissous du sang, histoire d'augmenter la fréquence de respiration suivant les besoins. Si ce centre est atteint, la mort par cessation de la respiration est immédiate. Plus important, on y trouve le '''noyau du tractus solitaire''', un noyau impliqué dans le gout, la régulation cardiovasculaire et la sensibilité digestive. Il forme une colonne de neurones anatomiquement unie, que son organisation fonctionnelle segmente en trois parties. Il est segmenté en un segment gustatif spécialisé pour le gout, un centre pour le vomissement et un segment de neurones dédiés à la régulation cardiovasculaire et respiratoire. Il émet des axones regroupés dans un faisceau appelé le '''faisceau du noyau solitaire''', ou encore faisceau solitaire. Le faisceau solitaire fait synapse avec un grand nombre d'aires cérébrales : thalamus, hypothalamus, formation réticulée, locus coeruleus, noyaux du Raphé, noyaux parasympathiques, quelques nerfs crâniens, etc. Il reçoit des afférences en provenance de plusieurs nerfs crâniens : le nerf vague, le nerf facial et le nerf glossopharyngien. Le nerf facial et le nerf glossopharyngien lui envoient les sensation du gout, des perceptions captées par la langue. Par contre, le nerf vague transmet des sensations corporelles inconscientes, comme l'état du tube digestif, la teneur en oxygène du sang, la pression artérielle, etc. Elles proviennent de récepteurs sensoriels du tube digestif, de capteurs respiratoires et cardiaques dispersés dans l'organisme, ainsi que de la langue. Ces afférences ne se mélangent pas et atterrissent dans des segments séparés. Les afférences provenant de la langue sont impliqués dans la perception du gout, et atterrissent dans un noyau spécialisé : le ''noyau gustatif''. Les afférences en provenance du tube digestif font synapse avec plusieurs noyaux responsables de l'innervation parasympathique du tube digestif. De même, elles font synapse avec le ''centre du vomissement'', dont le nom parle de lui-même. Les afférences provenant des capteurs respiratoires et cardiaques perçoivent la teneur en CO2 ou en O2 du sang, son pH, la pression sanguine, etc. Ils sont surtout localisés dans l'aorte et les carotides, mais on peut parfois en trouver ailleurs (dans les méninges, par exemple). Ils font synapses sur une colonne de noyaux qui s'occupent de réguler la respiration et le rythme cardiaque, ainsi que la pression sanguine. Ces noyaux sont donc un centre de régulation cardiovasculaire cérébral. Ils communiquent avec d'autres noyaux impliqués dans la respiration, avec le nerf vague, et bien d'autres encore. Le myélencéphale contient aussi l''''olive bulbaire''', composée d'une olive inférieure et supérieure : l'olive supérieure a un rôle de traitement des informations auditives alors que l'olive inférieure reçoit des informations via des axones en provenance du cortex moteur et envoie des axones dans le métencéphale. C'est aussi dans la moelle allongée qu'on trouve les noyaux des nerfs crâniens qui transmettent la sensibilité du visage et en gèrent la motricité. * Le premier est le '''noyau ambigu''' contrôle la mastication et a un rôle dans la parole. * Le second est le '''noyau salivaire inférieur''' contrôle la salivation par les glandes salivaires. * Et enfin, on y trouve aussi le '''noyau du nerf vague'''. ===Le métencéphale=== Le pont de Varole contient divers noyaux, dont le plus connu est manifestement le '''locus coerulus'''. Il s'agit d'un noyau qui projette des axones à noradrénaline et à acétylcholine dans tous le cerveau. Fait étrange, les neurones de ce noyau sont remplis d'une substance de couleur noire, la mélanine. Sa présence en faible quantité fait que le locus coerulus a une belle couleur bleue, qui lui a donné son nom (noyau coloré en latin). L'activité de ce noyau favorise l'attention et la vigilance : une activité intense dans ce noyau entraine généralement une amélioration de la vigilance et de l'attention. Ce noyau subit des pointes d'activité transitoires lors de la présentation de stimulus salients. Ce noyau est impliqué dans les réponses au stress ou à la peur : la stimulation de ce noyau a tendance à entrainer de l'anxiété. Les anxiolytiques diminuent l'activité cérébrale dans ce noyau, de même que les antidépresseurs noradrénergiques. Il est totalement inactivé dans certaines phases du sommeil, le sommeil paradoxal pour être précis. Les '''noyaux pédiculopontiques''' sont un ensemble de noyaux qui émettent de l'acétylcholine dans l'ensemble du cerveau. Ils sont impliqués dans diverses fonctions, la principale étant l'activité cholinergique liée à l'éveil ou au sommeil paradoxal. Enfin, on trouve aussi d'autres noyaux qui communiquent avec le cervelet : les '''pédoncules cérébrelleux'''. Nous verrons ceux-ci plus en détail dans le chapitre sur le cervelet. ===Le mésencéphale=== [[File:Cn3nucleus-fr.svg|vignette|upright=1.5|Schéma simplifié du mésencéphale.]] [[File:Mesencephalus colliculus sup.jpg|vignette|upright=1.5|Schéma plus détaillé du mésencéphale.]] On a vu plus haut que le tronc cérébral est traditionnellement divisé en une région ventrale et un tegmentum. Au niveau du mésencéphale, il faut y ajouter une troisième subdivision : le '''tectum'''. Ce dernier n'est présent que dans le mésencéphale, là où le tegmentum est présent dans toute la longueur du tronc cérébral. Le tectum prend en charge un certain nombre de réflexes oculaires et auditifs, comme les réflexes liés à la pupille ou aux muscles du tympan. Le reste du mésencéphale est appelé l'ensemble des pédoncules cérébraux. Il est découpé en deux parties : le '''colliculus inférieur''' et le '''colliculus supérieur'''. Le premier fait partie du système auditif, avec de nombreuses autres aires cérébrales. Sa fonction, très spécialisée, est de localiser la source d'un son, sa position dans l'espace. Le colliculus supérieur a une fonction totalement différente : il ne fait même pas partie du système auditif, mais appartient aux systèmes visuel et moteur. Sa fonction principale est de diriger le regard vers les objets à regarder. Pour cela, il commande les saccades oculaires (les mouvements des yeux). Le tegmentum et la région ventrale sont séparés par la substance noire, une structure de couleur noire en forme de bandes. La région ventrale est appelée le pied, ou encore le '''crux cerebis'''. Le pied est surtout composé de faisceaux qui prennent naissance dans le cortex cérébral, les plus importants étant les faisceaux moteurs (pyramidaux). La '''substance noire''' est un amas de neurones dopaminergiques impliqué dans la motricité, connue pour être l'aire dont la dégénérescence cause la maladie de Parkinson. Chez le parkinsonien, les neurones de cette zone meurent prématurément, ce qui se traduit par des gestes rigides et saccadés, des mouvements lents et rares, des tremblements, et parfois des troubles intellectuels légers et une baisse de la mémoire ou de la motivation. Elle est divisée en une pars compacta et une pars reticula. Nous verrons cette aire plus en détail dans le chapitre sur les ganglions de la base. Le tegmentum regroupe divers noyaux. Parmi ceux-ci, il nous faut citer le '''noyau rouge''', appelé ainsi à cause de sa couleur légèrement rougeâtre, qui vient de la présence de pigments ferriques dans ses neurones. Ce noyau reçoit des axones provenant du cervelet et des aires motrices du cortex cérébral. On devine ainsi qu'il s'agit d'un noyau essentiellement moteur. Il émet des axones qui se rassemblent pour former un des faisceaux de la moelle épinière : le faisceau rubro-spinal de la substance blanche, appartenant à la voie extra-pyramidale. Le noyau rouge commande les muscles de la posture et joue donc un rôle certain dans le maintien de l'équilibre. Chez les vertébrés "peu évolués", le noyau rouge est la seule aire cérébrale qui prend en charge l'équilibre et le maintien de la posture. Mais chez les animaux "plus évolués", ce rôle est maintenant dévolu principalement au cervelet, le noyau rouge ayant un rôle mineur et/ou plus spécialisé. Il faut aussi parler d'un noyau de neurones dopaminergiques qui gère le plaisir suite à une récompense : l''''aire tegmentale ventrale'''. Elle contient de nombreux neurones dopaminergiques qui envoient des axones dans tout le cerveau. Elle est impliquée dans la motivation et la cognition. Elle jouerait un rôle dans les addictions, la dépression, et potentiellement dans les troubles bipolaires et la schizophrénie. ==Les syndromes et maladies du tronc cérébral== Le tronc cérébral est une structure anatomique assez vaste, aux fonctions multiples et variées. Toute lésion dans le tronc cérébral a des conséquences très variables, qui mêlent symptômes moteurs, sensoriels, végétatifs, et autres. En théorie, les fonctions cognitives et intellectuelles sont épargnées après une lésion du tronc cérébral, peu importe sa localisation. Mais on ne peut en dire autant du reste. Précisons que les syndromes du tronc cérébral sont très nombreux, et qu'il n'est pas pertinent de tous les aborder. Raison pour laquelle, dans cette section, nous allons voir quels sont les syndromes les plus communs que l'on peut observer après une lésion du tronc cérébral. Rarement, un noyau ou un faisceau est atteint de manière isolée, sans que les autres structures du tronc soient touchées. Les déficits induits par cette lésion sont alors assez clairs et précis et donnent ce qu'on appelle des ''syndromes purs''. Mais dans la réalité, plusieurs noyaux et/ou faisceaux sont touchés lors d'un AVC ou d'un traumatisme important, ce qui fait que la symptomatologie d'une lésion est la somme de plusieurs syndromes purs. En général, les lésions sont causées par un AVC, de type ischémique, ce qui fait que c'est le territoire de perfusion d'une artère qui est lésé. Les déficits obtenus sont la somme des syndromes associés aux noyaux et faisceaux perfusés par l'artère. Dans ce qui suit, nous allons étudier les syndromes purs, puis les syndromes combinés. ===L'atteinte isolée d'un noyau/faisceau du tronc cérébral=== Les symptômes qui font suite à une lésion du tronc cérébral peuvent se déduire assez facilement quand on sait ce que contient le tronc cérébral : les noyaux des nerfs crâniens, les faisceaux ascendants/descendants, et la formation réticulée. Ces trois structures peuvent être atteintes suite à une lésion, donnant des symptômes divers. {|class="wikitable" |+Symptômes d'une atteinte du tronc cérébral |- !Noyaux des nerfs crâniens |Symptômes dépendants du noyau touché. * Vertiges (noyaux cochléovestibulaire). * Troubles des mouvements oculaires (noyau du nerf oculomoteur commun, noyau du nerf pathétique, noyau du nerf oculomoteur externe). * Déviation de la langue, paralysie linguale (noyau du nerf hypoglosse). * Perte de la sensibilité et de la motricité du visage (noyau du nerf trijumeau ou du nerf facial, ...). * Dysarthrie (difficultés à articuler), dysphagie (difficultés pour avaler). * Diplopie, cécité, troubles visuels. * ... |- !Faisceaux ascendants | * Perte de la sensibilité du corps (sur une moitié du corps pour une lésion unilatérale, sur la totalité du corps pour une lésion bilatérale). |- !Faisceaux descendants | * Paralysie : hémiplégie, quadriplégie * Faiblesse musculaire, dans les cas les moins graves. * Ataxie, dans les cas les moins graves. |- !Formation réticulée | * Troubles de la vigilance/conscience * Perturbations motrices : paralysie, hémiplégie, ... |} Une lésion des noyaux de nerfs crâniens peut causer des troubles de la motricité et de la sensibilité du visage, alors que les lésions des faisceaux tendent à obérer la motricité/sensibilité du corps. Avec les lésions unilatérales (d'un côté du tronc), la perte de sensibilité/motricité ne touche qu'un seul côté du visage/corps, donnant une hémiplégie/hémiparésie ou la perte de la sensibilité sur une moitié du corps/visage. Les lésions de la formation réticulée donnent des troubles de la conscience, pouvant aller jusqu’au coma, parfois accompagnés de troubles moteurs caractéristiques. ===Les syndromes alternes du tronc cérébral=== Les lésions unilatérales du tronc cérébral causent une lésion de plusieurs noyaux de nerfs crâniens, couplée à la lésion d'un faisceau ascendant/descendant. Elles ont une symptomatologie assez particulière au niveau sensitif/moteur, qui leur vaut le nom de '''syndromes alternes'''. Elles se caractérisent par au moins un des deux symptômes suivants : * Une perte des sensations controlatérale pour le corps, mais ipsilatérale pour le visage. * Une perte de la motricité (paralysie, faiblesse musculaire) controlatérale pour le corps, mais ipsilatérale pour le visage. Comme on le voit, le côté du corps touché n'est pas le même que le côté du visage atteint. Cela s'explique par les faits suivants. C'est dans le tronc cérébral que les faisceaux/nerfs changent de côté pour innerver le corps. Par exemple, les fibres sensorielles ascendantes provenant du côté droit du corps vont passer du côté gauche, et réciproquement (même chose pour les fibres motrices). Par contre, ce n'est pas le cas pour les nerfs crâniens, qui restent du même côté du corps. Toute lésion unilatérale (d'un côté) du tronc cérébral va donc léser le faisceau innervant la moitié du corps, alors que les noyaux des nerfs crâniens innervent le visage de l'autre côté. ====Le syndrome latéral médullaire (de Wallenberg)==== [[File:Stickerman Wallenberg.png|vignette|Syndrome de Wallenberg.]] Le syndrome alterne le plus fréquemment rencontré par les neurologues est le '''syndrome de Wallenberg'''. Il apparaît quand un AVC a lieu dans l'artère cérébelleuse inféro-postérieure, qui alimente en sang le cervelet et la moelle allongée, ou dans une artère proche. Les noyaux et faisceaux suivants sont touchés : le faisceau spino-thalamique, les faisceaux sympathiques, le noyau vestibulaire, le cervelet, et quelques autres noyaux. En général, le syndrome n'est pas complet chez la plupart des patients, qui ne montrent qu'une partie des symptômes. Mais sa présentation classique est la suivante : {|class="wikitable" |- ! colspan="2" | Du côté de la lésion (côté ipsilatéral) ! colspan="2" | Du côté opposé à la lésion (côté controlatéral) |- !Symptôme !Localisation de la lésion (ipsilatérale) !Symptôme !Localisation de la lésion (controlatérale) |- |Syndrome de Claude-Bernard-Horner. |Fibres sympathiques |- |Syndrome vestibulaire (vertiges, perte de l'équilibre, chutes, nausées, vomissements). Syndrome cérébelleux (ataxie, nystagmus, pertes d'équilibre, vertiges, ...). |Noyau vestibulaire Cervelet |- |Paralysie de l'hémi-voile, l'hémi-pharynx et de la corde vocale, qui entraîne dysphagie, dysphonie et dysarthrie. |Noyau ambigu |- |Anesthésie thermoalgique (perte de la douleur et des sensations de chaleur/froid) sur le visage. |Noyau du trijumeau | rowspan="5" | Anesthésie thermoalgique (perte de la douleur et des sensations de chaleur/froid) sur le corps. | rowspan="5" | Faisceau spino-thalamique. |} : Parfois, ce syndrome se complète d'une hémiplégie/hémiparésie controlatérale. On parle alors d'un '''syndrome de Babinski-Nageotte'''. Plus rarement on peut observer, du côté de la lésion : * Des douleurs dans le visage, avec une perte de sensibilité facile - atteinte du noyau du trijumeau. * Une réduction du goût, voir une agueusie (perte totale du goût) - atteinte du noyau gustatif. * Une paralysie de la langue - atteinte du noyau hypoglosse. ====Le syndrome médullaire médian (de Dejerine)==== Le '''syndrome de Dejerine ''' se caractérise par une lésion du noyau hypoglosse, du faisceau pyramidal, et du lemnisque médian (faisceau qui suit les colonnes dorsales de la moelle épinière). Il se traduit par : * une déviation de la langue du côté de la lésion, causée par la lésion du noyau hypoglosse ; * une faiblesse musculaire/paralysie du côté controlatéral à la lésion, causée par la lésion du faisceau pyramidal ; * et enfin une perte des sensations tactiles sur le côté controlatéral du corps, causé par la lésion du lemnisque médian. : Il est souvent accompagné d'un syndrome de Wallenberg. ====Les syndromes pontiques==== Les syndromes de cette section ont pour origine une atteinte du métencéphale (le pont de Varole). Ils sont assez nombreux, aussi nous allons nous concentrer sur les principaux. Pour la plupart, ils ont pour symptôme une paralysie ou du moins une parésie, controlatérale. Sa cause est une lésion du faisceau pyramidal, qui traverse le pont de Varole. Précisons d'ailleurs que tout syndrome du tronc cérébral peut entraîner une paralysie par section du faisceau pyramidal, et pas seulement les syndromes pontiques. Même chose pour les autres faisceaux spino-thalamique et spino-corticaux, ce qui peut entraîner une anesthésie tactile et/ou thermoalgique, controlatérale pour le corps, mais ipsilatérale pour le visage. Un syndrome de Horner est aussi possible, vu que les fibres sympathiques parcourent le pont de Varole. La différence entre les syndromes pontiques se fait sur les noyaux des nerfs crâniens touchés par la lésion. Le '''syndrome de Millard-Gubler ''', aussi appelé syndrome du pont ventral, est causé par une lésion des noyaux des nerfs crâniens moteurs 6 et 7, en plus de la lésion du faisceau pyramidal. Le syndrome pyramidal se couple donc avec d'autres symptômes moteurs. La lésion du noyau 6 entraîne une paralysie oculaire pour les mouvements latéraux, une diplopie et un strabisme. La lésion du noyau 7 cause une paralysie facile ipsilatérale. ====Les syndromes mésencéphaliques==== Les syndromes précédents surviennent suite à des lésions dans le pont de Varole ou la moelle allongée, éventuellement accompagnées de lésions au cervelet. Dans cette section, nous allons aborder les syndromes causés par une lésion du mésencéphale. Nous les voyons dans une même section, car ils sont tous très ressemblants et qu'ils partagent des symptômes communs, au point qu'il est très difficile de les distinguer. Il s'agit des syndromes de Claude, Benedict et Weber. Les autres syndromes mésencéphaliques sont beaucoup plus rares que le syndrome de Weber, avec une prévalence bien plus faible. Dans tous les symptômes mésencéphaliques, on observe un même ensemble commun de symptômes, qui regroupe : * Des troubles oculomoteurs ipsilatéraux : paralysie oculaire, pupilles dilatées, chute des paupières (ptosis), etc. * Des troubles moteurs controlatéraux, qui dépendent du syndrome considéré : ataxie, tremblements ou hémiparésie/hémiplégie. Les trois syndromes considérés se traduisent par des lésions des noyaux oculomoteurs, qui entraînent les troubles oculaires mentionnés plus haut. Les symptômes moteurs dépendent du syndrome. Dans le '''syndrome de Weber''', le trouble moteur de ce syndrome est une hémiparésie/hémiplégie corporelle (les membres et éventuellement le reste du corps est paralysé), controlatérale. Il est causé par une atteinte des noyaux oculomoteurs couplée à une lésion du faisceau pyramidal, sans atteinte supplémentaire. Dans le '''syndrome de Claude''' et le '''syndrome de Benedict''', on observe des lésions du noyau rouge et du cervelet, en plus des lésions des noyaux oculomoteurs. Cela se traduit par l'apparition d'une ataxie et/ou de tremblements, complémentaires de l’hémiparésie/hémiplégie. La différence entre les trois syndromes se fait sur le symptôme moteur prédominant : hémiparésie pour le syndrome de Weber, ataxie pour le syndrome de Benedict, tremblement pour le syndrome de Benedict. {|class="wikitable" |+ Syndromes mésencéphaliques |- ! ! Syndrome de Weber ! Syndrome de Claude ! Syndrome de Benedict ! Localisation de la lésion responsable du symptôme |- ! rowspan="4" | Symptômes | colspan="3" | Troubles oculomoteurs : paralysie oculaire, ptosis, pupilles dilatées, etc. | Noyaux des nerfs crâniens oculomoteurs |- | Hémiplégie/hémiparésie controlatérale | | | Faisceau cortico-spinal (pyramidal) |- | | Ataxie | | Cervelet, noyau rouge |- | | | Tremblements | Cervelet, noyau rouge |} Dans les faits, il n'est pas rare que d'autres manifestations neurologiques s'y ajoutent. Par exemple, on peut observer un syndrome parkinsonien, résultant d'une atteinte de la substance noire, proche des autres structures lésées. Ou encore, on peut observer des problèmes linguaux, causés par une atteinte du noyau hypoglosse et du faisceau cortico-bulbaire. Si le syndrome de Weber est un très mauvais signe à court-terme, les patients qui survivent récupèrent bien dans le cas général, à condition que le syndrome de Weber soit pur (sans symptômes ajoutés). <noinclude> {{NavChapitre | book=Neurosciences | prev=Les nerfs crâniens | prevText=Les nerfs crâniens | next=Le cerveau antérieur | nextText=Le cerveau antérieur }}{{autoCat}} </noinclude> bb0p18voo26wxsm57wdxxqjo08v6ywf 762836 762835 2026-04-03T14:26:27Z Mewtow 31375 /* L'anatomie en longueur du tronc cérébral */ 762836 wikitext text/x-wiki [[File:Brain sagittal section stem highlighted.svg|vignette|Tronc cérébral.]] Le tronc cérébral est une partie du cerveau qu'il est très difficile de décrire simplement. Sa complexité fait qu'il vaut mieux lui dédier un chapitre complet. Le tronc cérébral contient à la fois de la substance grise (neurones) que de la substance blanche (axones myélinisés). La substance blanche est surtout composée de faisceaux qui poursuivent les voies ascendantes/descendantes et traversent le tronc cérébral sur la majeure partie de sa longueur. La matière grise se limite à des noyaux enfouis dans la substance blanche et ne contient aucun cortex (couches de neurones). Et ces noyaux sont très nombreux. Les noyaux du tronc cérébral regroupent des noyaux moteurs, des noyaux sensoriels, des noyaux mixtes sensimoteurs, des noyaux d'association, tous les types sont représentés. D'autres noyaux ont des rôles plus larges ou mal connus, avec une influence sur la cognition, le noyau du Raphé en étant un bon exemple. Ils contiennent aussi les '''noyaux des nerfs crâniens''', qui sont tous dans le tronc cérébral. Une bonne partie de ces noyaux sera étudié en détail dans les chapitres portant sur les sens ou la motricité. Nous allons cependant survoler les principaux noyaux, en donnant si possible quelques détails sur leur fonctionnement dans ce qui suit. ==L'anatomie en largeur (coupe-section) du tronc cérébral== [[File:Coupe section du tronc cérébral.png|vignette|Coupe section du tronc cérébral.]] Dans cette section suivante, nous verrons comment s'organise le tronc cérébral sur sa largeur, en coupe-section. On s’aperçoit alors que le tronc cérébral est composé de deux à trois subdivisions selon l'étage. On distingue une '''région ventrale''', une région dorsale appelée le '''tegmentum''', et une région qui n’apparaît que dans le mésencéphale : le '''tectum'''. La région ventrale est séparée du tegmentum par l’aqueduc de Sylvius, une sorte de canal qui relie le 3ème et le 4ème ventricule (on abordera les ventricules plus tard, dans un futur chapitre). La région ventrale contient surtout des faisceaux de matière blanche, dont la plupart se prolongent dans la moelle épinière. Tel est le cas du faisceau cortico-spinal, qui transmet les commandes motrices volontaires. Le tegmentum est lui-même composé de plusieurs formations. Parmi celles-ci, on trouve la formation réticulée, un lacis de noyaux et de fibres nerveuses assez complexe, lui-même formé de trois colonnes. ===La formation réticulée=== La '''formation réticulée''' est une structure complexe, qui mélange divers noyaux et un ensemble de fibres entrelacées très difficile à décrire. La formation réticulée a la forme d'une bande grise qui court sur toute la longueur du tronc cérébral. Elle est entremêlée avec divers noyaux, dont les noyaux des nerfs crâniens. Cette formation émet des axones dans tout le cerveau, et notamment dans la totalité du télencéphale. La formation réticulée est divisée en trois colonnes : une colonne centrale qui contient les noyaux du raphé, une colonne médiane et une colonne latérale. La colonne médiane est composée de neurones de grande taille, alors que la colonne latérale est composée de neurones de petite taille. Les scientifiques ont depuis longtemps subdivisé la formation réticulée en deux systèmes fonctionnels distincts : un système réticulé ascendant/activateur qui maintient l'état d'éveil, et un système réticulé descendant. Le '''système réticulaire descendant''' a plusieurs fonctions, qui vont de la modulation de la douleur à la commande de la motricité extrapyramidale. Il est à l'origine d'un des faisceaux de la substance blanche de la moelle épinière, le faisceau réticulospinal, une sous-portion du faisceau extrapyramidal (n'hésitez pas à consulter le chapitre sur la moelle épinière pour des rappels). La formation réticulée descendante commande les muscles du maintien de la posture, ainsi que les muscles qui gèrent les mouvements fins, précis. D'autres portions de ce faisceau jouent un rôle dans la modulation de la douleur. En comparaison, le '''système réticulaire ascendant''' joue un grand rôle dans l'état de veille, l'attention, la vigilance et l'activation physiologique (l'intensité de l'état de veille). Lors de l'éveil, ces axones excitent les aires cérébrales de destination, donnant naissance à une activité électrique minimale au repos, ce qui permet de maintenir le sujet éveillé ou conscient. Lors du sommeil, cette formation diminue son activité, mais ne l'annule pas totalement. Une lésion de cette structure peut causer un coma irréversible, alors que sa stimulation induit un éveil soutenu. Outre son rôle dans la vigilance et l'éveil, elle influence aussi la perception de la douleur ainsi que la motricité. Nous reparlerons plus loin de ces deux systèmes, dans les chapitres sur la motricité extrapyramidale et le sommeil. ====La colonne centrale : les noyaux du Raphé==== Les '''noyaux du Raphé''' sont des noyaux regroupés dans la colonne centrale de la formation réticulée, cette colonne contenant plusieurs noyaux. Suivant les nomenclatures, le nombre de noyaux du Raphé varient, allant de seulement deux à trois noyaux à plus de neufs noyaux notés B1, B2, ... , B9. Certains de ces noyaux ont un nom plus commun : le noyau obscurus, le noyau pallidus, le noyau magnus, le noyau pontin, le noyau central supérieur et le noyau dorsal. Les noyaux localisés dans la moelle allongée (bulbe rachidien) innervent la moelle épinière, et notamment les interneurones qui transmettent la douleur. Ces innervations permettent de contrôler les sensations douloureuses, leur intensité. Les noyaux localisés dans le reste du tronc cérébral secrètent de la sérotonine dans tout le cerveau. Les axones de ces noyaux innervent aussi bien le cervelet que le cortex ou d'autres structures cérébrales. Ces axones qui innervent le cerveau ont un effet stimulant sur celui-ci, qui maintient l'état d'éveil. Par contre, une lésion de ces centres induit une insomnie. Vu leur rôle, on devine rapidement qu'ils font partie intégrante du système réticulée ascendant. Ils sont aussi impliqués dans la régulation de l'humeur, avec un effet indirect sur l'impulsivité et l'agressivité. La raison à cela est que la sérotonine, émise en grande partie (mais pas seulement) par les noyaux du raphé, a un rôle prédominant dans la régulation de l'humeur. [[File:Serotonergic neurons.svg|centre|vignette|upright=1.5|Cette image montre les axones émis par les noyaux du Raphé. On voit que ceux-ci innervent la quasi-totalité du télencéphale, ainsi que le cervelet et certaines parties du diencéphale.]] ===Les noyaux des nerfs crâniens=== [[File:Gray697.png|vignette|Nerfs crâniens moteurs.]] [[File:Gray698.png|vignette|Nerfs crâniens sensoriels.]] Le tronc cérébral contient aussi les noyaux des nerfs crâniens, à l'exception des nerfs olfactifs et optiques. Les nerfs optiques et olfactifs forment un groupe à part, vu qu'ils sont dans le cerveau antérieur (le diencéphale, pour être précis) et qu'ils font partie du système nerveux central. Les noyaux des nerfs crâniens sont classés en plusieurs groupes, selon leur origine embryonnaire/évolutive. Les nerfs hypoglosse et vestibulo-cochléaire sont chacun une catégorie à part. Le reste sépare les nerfs oculomoteur et branchiaux. Les cinq '''nerfs branchiaux''' s’appellent ainsi car ils sont l'équivalent modernes des nerfs qui innervent les branchies des poissons. {|class="wikitable" |+ Nerfs crâniens du système nerveux périphérique |- !rowspan="3"| Nerfs oculo-moteurs |Nerf oculo-moteur commun (III) |- |Nerf trochléaire ou pathétique (IV) |- |Nerf oculo-moteur externe ou abducens (VI) |- !rowspan="5"| Nerfs branchiaux |Nerf trijumeau (V) |- |Nerf facial (VII) |- |Nerf glosso-pharyngien (IX) |- |Nerf vague ou pneumogastrique (X) |- |Nerf spinal ou accessoire (XI) |- !colspan="2"| Nerf hypoglosse (XII) |- !colspan="2"| Nerf vestibulo-cochléaire ou auditif (VIII) |} [[File:Brain stem sagittal.svg|centre|vignette|upright=2|Section du tronc cérébral avec la position des noyaux des nerfs crâniens.]] Les noyaux des nerfs crâniens sont regroupés en plusieurs colonnes de noyaux alignés sur la longueur du tronc cérébral. De plus, les colonnes de noyau sont spécialisées, avec des colonnes composées uniquement de noyaux moteurs, d'autres uniquement de noyaux sensoriels, une autre uniquement de noyaux parasympathiques, etc. Cette organisation dérive de l'évolution embryonnaire du cerveau. Le système nerveux embryonnaire démarre sous la forme d'un tube qui ressemble à une ébauche de moelle épinière : le tube neural. Par la suite, le tube neural se scinde en plusieurs colonnes, qui elles-mêmes se subdivisent en noyaux sur leur longueur. Une fois formés, les noyaux des nerfs crâniens ne migrent pas et restent en place. Leur position conserve donc l'organisation en colonnes sur une bonne partie du tronc cérébral. [[File:Cranial Nerves Apparent Orrigins.jpg|centre|vignette|upright=1.5|Origines apparentes des nerfs crâniens.]] Il existe en tout 6 colonnes de noyaux pour les nerfs crâniens. Il faut faire la différence entre les colonnes motrices et les colonnes sensorielles. Il y a aussi une différence entre les colonnes somatiques et viscérales. Les colonnes somatiques sont pour la motricité et la perception consciente, alors que les colonnes viscérales s'occupent de la motricité/sensibilité inconsciente. En tout, cela fait quatre colonnes, dont deux sont en doubles. Les colonnes motrices regroupent trois colonnes, une colonne somatique, une colonne viscérale non-spécialisée, et une colonne parasympathique. {|class="wikitable" |+ Colonnes motrices |- ! Colonne motrice somatique GSE ! Colonne motrice parasympathique GVE ! Colonne motrice viscérale SVE |- | Noyaux oculomoteurs : * Noyau oculomoteur * Noyau trochléaire * Noyau abducens | Noyaux des nerfs brachiaux : * Noyau moteur du nerf trigéminal * Noyau moteur du nerf facial * Noyau ambigu (nerfs glossopharyngéal, vague et accessoire) | Noyau oculomoteur accessoire d'Edinger-Westphal |- | Noyau hypoglosse || || Noyau salivaire |- | || || Noyau dorsal du nerf vague |} Les colonnes sensorielles sont elles aussi au nombre de trois. {|class="wikitable" |+ Colonnes sensorielles |- ! Colonne sensorielle somatique générale ! Colonne sensorielle somatique spéciale ! Colonne sensorielle viscérale |- | rowspan="3" | Nerfs du nerf trigéminal : * Noyau sensoriel principal du nerf trigéminal * Noyau spinal du nerf trigéminal * Noyau mésencéphalique du nerf trigéminal | rowspan="3" | Noyaux du nerf cochléo-vestibulaire * Noyau cochléaire * Noyau vestibulaire | Noyau du tractus solitaire |- | Noyau dorsal du nerf vague |- | Noyau gustatif |} ==L'anatomie en longueur du tronc cérébral== Le cerveau est subdivisé en cinq grandes aires : le myélencéphale, le métencéphale, le mésencéphale, le diencéphale et le télencéphale. Cette subdivision se développe d'abord dans l'embryon, où ces 5 subdivisons arrivent en premier, avant de se subdiviser en aires cérébrales plus petites. Le tronc cérébral regroupe myélencéphale, métencéphale et mésencéphale, tandis que le reste du cerveau contient le diencéphale et le télencéphale. On a ainsi une première subdivision du tronc cérébral en trois étages, sur sa longueur. * Le myélencéphale, aussi appelé le '''bulbe rachidien''', ou encore la moelle allongée. Comme pour la moelle épinière, la matière grise est localisée au centre de la moelle allongée, la substance blanche entourant celle-ci. La différence avec la moelle épinière tient dans la présence de quelques noyaux, sources de nerfs crâniens divers. * Le '''pont de Varole''', ou métencéphale, se situe en dessous du cervelet. Il ressemble à une sorte de renflement sur la face ventrale du tronc cérébral. Il sert de relai aux axones moteurs et sensoriels et prend en charge certaines fonctions autonomes. De nombreux noyaux de nerfs crâniens sont présents dans le pont, comme les noyaux des nerfs vestibulaires, salivaires, etc. * Le '''mésencéphale''' est une structure beaucoup plus complexe, qui surmonte toutes les autres. [[File:Tronc-cerebral-ventrale.jpg|centre|vignette|upright=2.0|Tronc cérébral.]] Dans ce qui va suivre, nous ne reparlerons pas des noyaux de nerfs crâniens ou de la formation réticulée, mais verrons les noyaux qui n'appartiennent pas à ces structures. ===Le myélencéphale=== C'est dans la moelle allongée qu'on trouve les noyaux des nerfs crâniens qui transmettent la sensibilité du visage et en gèrent la motricité. * Le premier est le '''noyau ambigu''' contrôle la mastication et a un rôle dans la parole. * Le second est le '''noyau salivaire inférieur''' contrôle la salivation par les glandes salivaires. * Et enfin, on y trouve aussi le '''noyau du nerf vague'''. Le myélencéphale contient aussi l''''olive bulbaire''', composée d'une olive inférieure et supérieure : l'olive supérieure a un rôle de traitement des informations auditives alors que l'olive inférieure reçoit des informations via des axones en provenance du cortex moteur et envoie des axones dans le métencéphale. Les autres noyaux de la moelle allongée sont surtout ceux du système nerveux périphérique autonome, qu'il soit sympathique ou parasympathique. Ils gèrent ainsi la pression sanguine, le rythme cardiaque, la respiration, ainsi que les réflexes de salivation, vomissement, mastication et autres. On y trouve le '''centre respiratoire''', un noyau qui s'occupe de l'inspiration et de l'expiration. Ce centre reçoit des informations comme le PH et le taux de dioxyde de carbone dissous du sang, histoire d'augmenter la fréquence de respiration suivant les besoins. Le '''noyau du tractus solitaire''' est impliqué dans le gout, la régulation cardiovasculaire et la sensibilité digestive. Il forme une colonne de neurones anatomiquement unie, que son organisation fonctionnelle segmente en trois parties. Il est segmenté en un segment gustatif spécialisé pour le gout, un centre pour le vomissement et un segment de neurones dédiés à la régulation cardiovasculaire et respiratoire. Il émet des axones regroupés dans un faisceau appelé le '''faisceau solitaire''', qui fait synapse avec un grand nombre d'aires cérébrales : thalamus, hypothalamus, formation réticulée, locus coeruleus, noyaux du Raphé, noyaux parasympathiques, quelques nerfs crâniens, etc. Il reçoit des afférences en provenance du nerf vague, du nerf facial et du nerf glossopharyngien. Ces afférences ne se mélangent pas et atterrissent dans des segments séparés. * Le nerf facial et le nerf glossopharyngien lui envoient les sensation du gout, des perceptions captées par la langue. Elles atterrissent dans un noyau spécialisé : le ''noyau gustatif''. * Le nerf vague transmet des sensations corporelles inconscientes, comme l'état du tube digestif, la teneur en oxygène du sang, la pression artérielle, etc. Elles proviennent de récepteurs sensoriels dispersés dans le tube digestif, de capteurs respiratoires et cardiaques dispersés dans l'organisme, ainsi que de la langue. Pour le nerf vague, les axones sont triés selon qu'ils viennent du tube digestif ou du reste. Les afférences provenant du tube digestif font synapse avec plusieurs noyaux responsables de l'innervation parasympathique du tube digestif, ainsi qu'avec le ''centre du vomissement'', dont le nom parle de lui-même. Les afférences provenant des capteurs respiratoires et cardiaques perçoivent la teneur en CO2 ou en O2 du sang, son pH, la pression sanguine, etc. Ils sont surtout localisés dans l'aorte et les carotides, mais on peut parfois en trouver ailleurs (dans les méninges, par exemple). Ils font synapses sur une colonne de noyaux qui s'occupent de réguler la respiration et le rythme cardiaque, ainsi que la pression sanguine. Ces noyaux sont donc un centre de régulation cardiovasculaire cérébral. Ils communiquent avec d'autres noyaux impliqués dans la respiration, avec le nerf vague, et bien d'autres encore. ===Le métencéphale=== Le pont de Varole contient divers noyaux, dont le plus connu est manifestement le '''locus coerulus'''. Il s'agit d'un noyau qui projette des axones à noradrénaline et à acétylcholine dans tous le cerveau. Fait étrange, les neurones de ce noyau sont remplis d'une substance de couleur noire, la mélanine. Sa présence en faible quantité fait que le locus coerulus a une belle couleur bleue, qui lui a donné son nom (noyau coloré en latin). L'activité de ce noyau est liée à l'attention et la vigilance. Ce noyau subit des pointes d'activité transitoires lors de la présentation de stimulus salients. Il est aussi impliqué dans les réponses au stress ou à la peur. La stimulation de ce noyau a tendance à entrainer de l'anxiété. Les anxiolytiques diminuent l'activité cérébrale dans ce noyau, de même que les antidépresseurs noradrénergiques. Il est totalement inactivé dans certaines phases du sommeil, le sommeil paradoxal pour être précis. Les '''noyaux pédiculopontiques''' sont un ensemble de noyaux qui émettent de l'acétylcholine dans l'ensemble du cerveau. Ils sont impliqués dans diverses fonctions, la principale étant l'activité cholinergique liée à l'éveil ou au sommeil paradoxal. Enfin, on trouve aussi d'autres noyaux qui communiquent avec le cervelet : les '''pédoncules cérébrelleux'''. Nous verrons ceux-ci plus en détail dans le chapitre sur le cervelet. ===Le mésencéphale=== [[File:Cn3nucleus-fr.svg|vignette|upright=1.5|Schéma simplifié du mésencéphale.]] [[File:Mesencephalus colliculus sup.jpg|vignette|upright=1.5|Schéma plus détaillé du mésencéphale.]] On a vu plus haut que le tronc cérébral est traditionnellement divisé en une région ventrale et un tegmentum. Au niveau du mésencéphale, il faut y ajouter une troisième subdivision : le '''tectum'''. Ce dernier n'est présent que dans le mésencéphale, là où le tegmentum est présent dans toute la longueur du tronc cérébral. Le tectum prend en charge un certain nombre de réflexes oculaires et auditifs, comme les réflexes liés à la pupille ou aux muscles du tympan. Le reste du mésencéphale est appelé l'ensemble des pédoncules cérébraux. Il est découpé en deux parties : le '''colliculus inférieur''' et le '''colliculus supérieur'''. Le premier fait partie du système auditif, avec de nombreuses autres aires cérébrales. Sa fonction, très spécialisée, est de localiser la source d'un son, sa position dans l'espace. Le colliculus supérieur a une fonction totalement différente : il ne fait même pas partie du système auditif, mais appartient aux systèmes visuel et moteur. Sa fonction principale est de diriger le regard vers les objets à regarder. Pour cela, il commande les saccades oculaires (les mouvements des yeux). Le tegmentum et la région ventrale sont séparés par la substance noire, une structure de couleur noire en forme de bandes. La région ventrale est appelée le pied, ou encore le '''crux cerebis'''. Le pied est surtout composé de faisceaux qui prennent naissance dans le cortex cérébral, les plus importants étant les faisceaux moteurs (pyramidaux). La '''substance noire''' est un amas de neurones dopaminergiques impliqué dans la motricité, connue pour être l'aire dont la dégénérescence cause la maladie de Parkinson. Chez le parkinsonien, les neurones de cette zone meurent prématurément, ce qui se traduit par des gestes rigides et saccadés, des mouvements lents et rares, des tremblements, et parfois des troubles intellectuels légers et une baisse de la mémoire ou de la motivation. Elle est divisée en une pars compacta et une pars reticula. Nous verrons cette aire plus en détail dans le chapitre sur les ganglions de la base. Le tegmentum regroupe divers noyaux. Parmi ceux-ci, il nous faut citer le '''noyau rouge''', appelé ainsi à cause de sa couleur légèrement rougeâtre, qui vient de la présence de pigments ferriques dans ses neurones. Ce noyau reçoit des axones provenant du cervelet et des aires motrices du cortex cérébral. On devine ainsi qu'il s'agit d'un noyau essentiellement moteur. Il émet des axones qui se rassemblent pour former un des faisceaux de la moelle épinière : le faisceau rubro-spinal de la substance blanche, appartenant à la voie extra-pyramidale. Le noyau rouge commande les muscles de la posture et joue donc un rôle certain dans le maintien de l'équilibre. Chez les vertébrés "peu évolués", le noyau rouge est la seule aire cérébrale qui prend en charge l'équilibre et le maintien de la posture. Mais chez les animaux "plus évolués", ce rôle est maintenant dévolu principalement au cervelet, le noyau rouge ayant un rôle mineur et/ou plus spécialisé. Il faut aussi parler d'un noyau de neurones dopaminergiques qui gère le plaisir suite à une récompense : l''''aire tegmentale ventrale'''. Elle contient de nombreux neurones dopaminergiques qui envoient des axones dans tout le cerveau. Elle est impliquée dans la motivation et la cognition. Elle jouerait un rôle dans les addictions, la dépression, et potentiellement dans les troubles bipolaires et la schizophrénie. ==Les syndromes et maladies du tronc cérébral== Le tronc cérébral est une structure anatomique assez vaste, aux fonctions multiples et variées. Toute lésion dans le tronc cérébral a des conséquences très variables, qui mêlent symptômes moteurs, sensoriels, végétatifs, et autres. En théorie, les fonctions cognitives et intellectuelles sont épargnées après une lésion du tronc cérébral, peu importe sa localisation. Mais on ne peut en dire autant du reste. Précisons que les syndromes du tronc cérébral sont très nombreux, et qu'il n'est pas pertinent de tous les aborder. Raison pour laquelle, dans cette section, nous allons voir quels sont les syndromes les plus communs que l'on peut observer après une lésion du tronc cérébral. Rarement, un noyau ou un faisceau est atteint de manière isolée, sans que les autres structures du tronc soient touchées. Les déficits induits par cette lésion sont alors assez clairs et précis et donnent ce qu'on appelle des ''syndromes purs''. Mais dans la réalité, plusieurs noyaux et/ou faisceaux sont touchés lors d'un AVC ou d'un traumatisme important, ce qui fait que la symptomatologie d'une lésion est la somme de plusieurs syndromes purs. En général, les lésions sont causées par un AVC, de type ischémique, ce qui fait que c'est le territoire de perfusion d'une artère qui est lésé. Les déficits obtenus sont la somme des syndromes associés aux noyaux et faisceaux perfusés par l'artère. Dans ce qui suit, nous allons étudier les syndromes purs, puis les syndromes combinés. ===L'atteinte isolée d'un noyau/faisceau du tronc cérébral=== Les symptômes qui font suite à une lésion du tronc cérébral peuvent se déduire assez facilement quand on sait ce que contient le tronc cérébral : les noyaux des nerfs crâniens, les faisceaux ascendants/descendants, et la formation réticulée. Ces trois structures peuvent être atteintes suite à une lésion, donnant des symptômes divers. {|class="wikitable" |+Symptômes d'une atteinte du tronc cérébral |- !Noyaux des nerfs crâniens |Symptômes dépendants du noyau touché. * Vertiges (noyaux cochléovestibulaire). * Troubles des mouvements oculaires (noyau du nerf oculomoteur commun, noyau du nerf pathétique, noyau du nerf oculomoteur externe). * Déviation de la langue, paralysie linguale (noyau du nerf hypoglosse). * Perte de la sensibilité et de la motricité du visage (noyau du nerf trijumeau ou du nerf facial, ...). * Dysarthrie (difficultés à articuler), dysphagie (difficultés pour avaler). * Diplopie, cécité, troubles visuels. * ... |- !Faisceaux ascendants | * Perte de la sensibilité du corps (sur une moitié du corps pour une lésion unilatérale, sur la totalité du corps pour une lésion bilatérale). |- !Faisceaux descendants | * Paralysie : hémiplégie, quadriplégie * Faiblesse musculaire, dans les cas les moins graves. * Ataxie, dans les cas les moins graves. |- !Formation réticulée | * Troubles de la vigilance/conscience * Perturbations motrices : paralysie, hémiplégie, ... |} Une lésion des noyaux de nerfs crâniens peut causer des troubles de la motricité et de la sensibilité du visage, alors que les lésions des faisceaux tendent à obérer la motricité/sensibilité du corps. Avec les lésions unilatérales (d'un côté du tronc), la perte de sensibilité/motricité ne touche qu'un seul côté du visage/corps, donnant une hémiplégie/hémiparésie ou la perte de la sensibilité sur une moitié du corps/visage. Les lésions de la formation réticulée donnent des troubles de la conscience, pouvant aller jusqu’au coma, parfois accompagnés de troubles moteurs caractéristiques. ===Les syndromes alternes du tronc cérébral=== Les lésions unilatérales du tronc cérébral causent une lésion de plusieurs noyaux de nerfs crâniens, couplée à la lésion d'un faisceau ascendant/descendant. Elles ont une symptomatologie assez particulière au niveau sensitif/moteur, qui leur vaut le nom de '''syndromes alternes'''. Elles se caractérisent par au moins un des deux symptômes suivants : * Une perte des sensations controlatérale pour le corps, mais ipsilatérale pour le visage. * Une perte de la motricité (paralysie, faiblesse musculaire) controlatérale pour le corps, mais ipsilatérale pour le visage. Comme on le voit, le côté du corps touché n'est pas le même que le côté du visage atteint. Cela s'explique par les faits suivants. C'est dans le tronc cérébral que les faisceaux/nerfs changent de côté pour innerver le corps. Par exemple, les fibres sensorielles ascendantes provenant du côté droit du corps vont passer du côté gauche, et réciproquement (même chose pour les fibres motrices). Par contre, ce n'est pas le cas pour les nerfs crâniens, qui restent du même côté du corps. Toute lésion unilatérale (d'un côté) du tronc cérébral va donc léser le faisceau innervant la moitié du corps, alors que les noyaux des nerfs crâniens innervent le visage de l'autre côté. ====Le syndrome latéral médullaire (de Wallenberg)==== [[File:Stickerman Wallenberg.png|vignette|Syndrome de Wallenberg.]] Le syndrome alterne le plus fréquemment rencontré par les neurologues est le '''syndrome de Wallenberg'''. Il apparaît quand un AVC a lieu dans l'artère cérébelleuse inféro-postérieure, qui alimente en sang le cervelet et la moelle allongée, ou dans une artère proche. Les noyaux et faisceaux suivants sont touchés : le faisceau spino-thalamique, les faisceaux sympathiques, le noyau vestibulaire, le cervelet, et quelques autres noyaux. En général, le syndrome n'est pas complet chez la plupart des patients, qui ne montrent qu'une partie des symptômes. Mais sa présentation classique est la suivante : {|class="wikitable" |- ! colspan="2" | Du côté de la lésion (côté ipsilatéral) ! colspan="2" | Du côté opposé à la lésion (côté controlatéral) |- !Symptôme !Localisation de la lésion (ipsilatérale) !Symptôme !Localisation de la lésion (controlatérale) |- |Syndrome de Claude-Bernard-Horner. |Fibres sympathiques |- |Syndrome vestibulaire (vertiges, perte de l'équilibre, chutes, nausées, vomissements). Syndrome cérébelleux (ataxie, nystagmus, pertes d'équilibre, vertiges, ...). |Noyau vestibulaire Cervelet |- |Paralysie de l'hémi-voile, l'hémi-pharynx et de la corde vocale, qui entraîne dysphagie, dysphonie et dysarthrie. |Noyau ambigu |- |Anesthésie thermoalgique (perte de la douleur et des sensations de chaleur/froid) sur le visage. |Noyau du trijumeau | rowspan="5" | Anesthésie thermoalgique (perte de la douleur et des sensations de chaleur/froid) sur le corps. | rowspan="5" | Faisceau spino-thalamique. |} : Parfois, ce syndrome se complète d'une hémiplégie/hémiparésie controlatérale. On parle alors d'un '''syndrome de Babinski-Nageotte'''. Plus rarement on peut observer, du côté de la lésion : * Des douleurs dans le visage, avec une perte de sensibilité facile - atteinte du noyau du trijumeau. * Une réduction du goût, voir une agueusie (perte totale du goût) - atteinte du noyau gustatif. * Une paralysie de la langue - atteinte du noyau hypoglosse. ====Le syndrome médullaire médian (de Dejerine)==== Le '''syndrome de Dejerine ''' se caractérise par une lésion du noyau hypoglosse, du faisceau pyramidal, et du lemnisque médian (faisceau qui suit les colonnes dorsales de la moelle épinière). Il se traduit par : * une déviation de la langue du côté de la lésion, causée par la lésion du noyau hypoglosse ; * une faiblesse musculaire/paralysie du côté controlatéral à la lésion, causée par la lésion du faisceau pyramidal ; * et enfin une perte des sensations tactiles sur le côté controlatéral du corps, causé par la lésion du lemnisque médian. : Il est souvent accompagné d'un syndrome de Wallenberg. ====Les syndromes pontiques==== Les syndromes de cette section ont pour origine une atteinte du métencéphale (le pont de Varole). Ils sont assez nombreux, aussi nous allons nous concentrer sur les principaux. Pour la plupart, ils ont pour symptôme une paralysie ou du moins une parésie, controlatérale. Sa cause est une lésion du faisceau pyramidal, qui traverse le pont de Varole. Précisons d'ailleurs que tout syndrome du tronc cérébral peut entraîner une paralysie par section du faisceau pyramidal, et pas seulement les syndromes pontiques. Même chose pour les autres faisceaux spino-thalamique et spino-corticaux, ce qui peut entraîner une anesthésie tactile et/ou thermoalgique, controlatérale pour le corps, mais ipsilatérale pour le visage. Un syndrome de Horner est aussi possible, vu que les fibres sympathiques parcourent le pont de Varole. La différence entre les syndromes pontiques se fait sur les noyaux des nerfs crâniens touchés par la lésion. Le '''syndrome de Millard-Gubler ''', aussi appelé syndrome du pont ventral, est causé par une lésion des noyaux des nerfs crâniens moteurs 6 et 7, en plus de la lésion du faisceau pyramidal. Le syndrome pyramidal se couple donc avec d'autres symptômes moteurs. La lésion du noyau 6 entraîne une paralysie oculaire pour les mouvements latéraux, une diplopie et un strabisme. La lésion du noyau 7 cause une paralysie facile ipsilatérale. ====Les syndromes mésencéphaliques==== Les syndromes précédents surviennent suite à des lésions dans le pont de Varole ou la moelle allongée, éventuellement accompagnées de lésions au cervelet. Dans cette section, nous allons aborder les syndromes causés par une lésion du mésencéphale. Nous les voyons dans une même section, car ils sont tous très ressemblants et qu'ils partagent des symptômes communs, au point qu'il est très difficile de les distinguer. Il s'agit des syndromes de Claude, Benedict et Weber. Les autres syndromes mésencéphaliques sont beaucoup plus rares que le syndrome de Weber, avec une prévalence bien plus faible. Dans tous les symptômes mésencéphaliques, on observe un même ensemble commun de symptômes, qui regroupe : * Des troubles oculomoteurs ipsilatéraux : paralysie oculaire, pupilles dilatées, chute des paupières (ptosis), etc. * Des troubles moteurs controlatéraux, qui dépendent du syndrome considéré : ataxie, tremblements ou hémiparésie/hémiplégie. Les trois syndromes considérés se traduisent par des lésions des noyaux oculomoteurs, qui entraînent les troubles oculaires mentionnés plus haut. Les symptômes moteurs dépendent du syndrome. Dans le '''syndrome de Weber''', le trouble moteur de ce syndrome est une hémiparésie/hémiplégie corporelle (les membres et éventuellement le reste du corps est paralysé), controlatérale. Il est causé par une atteinte des noyaux oculomoteurs couplée à une lésion du faisceau pyramidal, sans atteinte supplémentaire. Dans le '''syndrome de Claude''' et le '''syndrome de Benedict''', on observe des lésions du noyau rouge et du cervelet, en plus des lésions des noyaux oculomoteurs. Cela se traduit par l'apparition d'une ataxie et/ou de tremblements, complémentaires de l’hémiparésie/hémiplégie. La différence entre les trois syndromes se fait sur le symptôme moteur prédominant : hémiparésie pour le syndrome de Weber, ataxie pour le syndrome de Benedict, tremblement pour le syndrome de Benedict. {|class="wikitable" |+ Syndromes mésencéphaliques |- ! ! Syndrome de Weber ! Syndrome de Claude ! Syndrome de Benedict ! Localisation de la lésion responsable du symptôme |- ! rowspan="4" | Symptômes | colspan="3" | Troubles oculomoteurs : paralysie oculaire, ptosis, pupilles dilatées, etc. | Noyaux des nerfs crâniens oculomoteurs |- | Hémiplégie/hémiparésie controlatérale | | | Faisceau cortico-spinal (pyramidal) |- | | Ataxie | | Cervelet, noyau rouge |- | | | Tremblements | Cervelet, noyau rouge |} Dans les faits, il n'est pas rare que d'autres manifestations neurologiques s'y ajoutent. Par exemple, on peut observer un syndrome parkinsonien, résultant d'une atteinte de la substance noire, proche des autres structures lésées. Ou encore, on peut observer des problèmes linguaux, causés par une atteinte du noyau hypoglosse et du faisceau cortico-bulbaire. Si le syndrome de Weber est un très mauvais signe à court-terme, les patients qui survivent récupèrent bien dans le cas général, à condition que le syndrome de Weber soit pur (sans symptômes ajoutés). <noinclude> {{NavChapitre | book=Neurosciences | prev=Les nerfs crâniens | prevText=Les nerfs crâniens | next=Le cerveau antérieur | nextText=Le cerveau antérieur }}{{autoCat}} </noinclude> nhf1cu3yc350hl4jmzagibsog3i1kxv 762837 762836 2026-04-03T14:27:18Z Mewtow 31375 /* Le mésencéphale */ 762837 wikitext text/x-wiki [[File:Brain sagittal section stem highlighted.svg|vignette|Tronc cérébral.]] Le tronc cérébral est une partie du cerveau qu'il est très difficile de décrire simplement. Sa complexité fait qu'il vaut mieux lui dédier un chapitre complet. Le tronc cérébral contient à la fois de la substance grise (neurones) que de la substance blanche (axones myélinisés). La substance blanche est surtout composée de faisceaux qui poursuivent les voies ascendantes/descendantes et traversent le tronc cérébral sur la majeure partie de sa longueur. La matière grise se limite à des noyaux enfouis dans la substance blanche et ne contient aucun cortex (couches de neurones). Et ces noyaux sont très nombreux. Les noyaux du tronc cérébral regroupent des noyaux moteurs, des noyaux sensoriels, des noyaux mixtes sensimoteurs, des noyaux d'association, tous les types sont représentés. D'autres noyaux ont des rôles plus larges ou mal connus, avec une influence sur la cognition, le noyau du Raphé en étant un bon exemple. Ils contiennent aussi les '''noyaux des nerfs crâniens''', qui sont tous dans le tronc cérébral. Une bonne partie de ces noyaux sera étudié en détail dans les chapitres portant sur les sens ou la motricité. Nous allons cependant survoler les principaux noyaux, en donnant si possible quelques détails sur leur fonctionnement dans ce qui suit. ==L'anatomie en largeur (coupe-section) du tronc cérébral== [[File:Coupe section du tronc cérébral.png|vignette|Coupe section du tronc cérébral.]] Dans cette section suivante, nous verrons comment s'organise le tronc cérébral sur sa largeur, en coupe-section. On s’aperçoit alors que le tronc cérébral est composé de deux à trois subdivisions selon l'étage. On distingue une '''région ventrale''', une région dorsale appelée le '''tegmentum''', et une région qui n’apparaît que dans le mésencéphale : le '''tectum'''. La région ventrale est séparée du tegmentum par l’aqueduc de Sylvius, une sorte de canal qui relie le 3ème et le 4ème ventricule (on abordera les ventricules plus tard, dans un futur chapitre). La région ventrale contient surtout des faisceaux de matière blanche, dont la plupart se prolongent dans la moelle épinière. Tel est le cas du faisceau cortico-spinal, qui transmet les commandes motrices volontaires. Le tegmentum est lui-même composé de plusieurs formations. Parmi celles-ci, on trouve la formation réticulée, un lacis de noyaux et de fibres nerveuses assez complexe, lui-même formé de trois colonnes. ===La formation réticulée=== La '''formation réticulée''' est une structure complexe, qui mélange divers noyaux et un ensemble de fibres entrelacées très difficile à décrire. La formation réticulée a la forme d'une bande grise qui court sur toute la longueur du tronc cérébral. Elle est entremêlée avec divers noyaux, dont les noyaux des nerfs crâniens. Cette formation émet des axones dans tout le cerveau, et notamment dans la totalité du télencéphale. La formation réticulée est divisée en trois colonnes : une colonne centrale qui contient les noyaux du raphé, une colonne médiane et une colonne latérale. La colonne médiane est composée de neurones de grande taille, alors que la colonne latérale est composée de neurones de petite taille. Les scientifiques ont depuis longtemps subdivisé la formation réticulée en deux systèmes fonctionnels distincts : un système réticulé ascendant/activateur qui maintient l'état d'éveil, et un système réticulé descendant. Le '''système réticulaire descendant''' a plusieurs fonctions, qui vont de la modulation de la douleur à la commande de la motricité extrapyramidale. Il est à l'origine d'un des faisceaux de la substance blanche de la moelle épinière, le faisceau réticulospinal, une sous-portion du faisceau extrapyramidal (n'hésitez pas à consulter le chapitre sur la moelle épinière pour des rappels). La formation réticulée descendante commande les muscles du maintien de la posture, ainsi que les muscles qui gèrent les mouvements fins, précis. D'autres portions de ce faisceau jouent un rôle dans la modulation de la douleur. En comparaison, le '''système réticulaire ascendant''' joue un grand rôle dans l'état de veille, l'attention, la vigilance et l'activation physiologique (l'intensité de l'état de veille). Lors de l'éveil, ces axones excitent les aires cérébrales de destination, donnant naissance à une activité électrique minimale au repos, ce qui permet de maintenir le sujet éveillé ou conscient. Lors du sommeil, cette formation diminue son activité, mais ne l'annule pas totalement. Une lésion de cette structure peut causer un coma irréversible, alors que sa stimulation induit un éveil soutenu. Outre son rôle dans la vigilance et l'éveil, elle influence aussi la perception de la douleur ainsi que la motricité. Nous reparlerons plus loin de ces deux systèmes, dans les chapitres sur la motricité extrapyramidale et le sommeil. ====La colonne centrale : les noyaux du Raphé==== Les '''noyaux du Raphé''' sont des noyaux regroupés dans la colonne centrale de la formation réticulée, cette colonne contenant plusieurs noyaux. Suivant les nomenclatures, le nombre de noyaux du Raphé varient, allant de seulement deux à trois noyaux à plus de neufs noyaux notés B1, B2, ... , B9. Certains de ces noyaux ont un nom plus commun : le noyau obscurus, le noyau pallidus, le noyau magnus, le noyau pontin, le noyau central supérieur et le noyau dorsal. Les noyaux localisés dans la moelle allongée (bulbe rachidien) innervent la moelle épinière, et notamment les interneurones qui transmettent la douleur. Ces innervations permettent de contrôler les sensations douloureuses, leur intensité. Les noyaux localisés dans le reste du tronc cérébral secrètent de la sérotonine dans tout le cerveau. Les axones de ces noyaux innervent aussi bien le cervelet que le cortex ou d'autres structures cérébrales. Ces axones qui innervent le cerveau ont un effet stimulant sur celui-ci, qui maintient l'état d'éveil. Par contre, une lésion de ces centres induit une insomnie. Vu leur rôle, on devine rapidement qu'ils font partie intégrante du système réticulée ascendant. Ils sont aussi impliqués dans la régulation de l'humeur, avec un effet indirect sur l'impulsivité et l'agressivité. La raison à cela est que la sérotonine, émise en grande partie (mais pas seulement) par les noyaux du raphé, a un rôle prédominant dans la régulation de l'humeur. [[File:Serotonergic neurons.svg|centre|vignette|upright=1.5|Cette image montre les axones émis par les noyaux du Raphé. On voit que ceux-ci innervent la quasi-totalité du télencéphale, ainsi que le cervelet et certaines parties du diencéphale.]] ===Les noyaux des nerfs crâniens=== [[File:Gray697.png|vignette|Nerfs crâniens moteurs.]] [[File:Gray698.png|vignette|Nerfs crâniens sensoriels.]] Le tronc cérébral contient aussi les noyaux des nerfs crâniens, à l'exception des nerfs olfactifs et optiques. Les nerfs optiques et olfactifs forment un groupe à part, vu qu'ils sont dans le cerveau antérieur (le diencéphale, pour être précis) et qu'ils font partie du système nerveux central. Les noyaux des nerfs crâniens sont classés en plusieurs groupes, selon leur origine embryonnaire/évolutive. Les nerfs hypoglosse et vestibulo-cochléaire sont chacun une catégorie à part. Le reste sépare les nerfs oculomoteur et branchiaux. Les cinq '''nerfs branchiaux''' s’appellent ainsi car ils sont l'équivalent modernes des nerfs qui innervent les branchies des poissons. {|class="wikitable" |+ Nerfs crâniens du système nerveux périphérique |- !rowspan="3"| Nerfs oculo-moteurs |Nerf oculo-moteur commun (III) |- |Nerf trochléaire ou pathétique (IV) |- |Nerf oculo-moteur externe ou abducens (VI) |- !rowspan="5"| Nerfs branchiaux |Nerf trijumeau (V) |- |Nerf facial (VII) |- |Nerf glosso-pharyngien (IX) |- |Nerf vague ou pneumogastrique (X) |- |Nerf spinal ou accessoire (XI) |- !colspan="2"| Nerf hypoglosse (XII) |- !colspan="2"| Nerf vestibulo-cochléaire ou auditif (VIII) |} [[File:Brain stem sagittal.svg|centre|vignette|upright=2|Section du tronc cérébral avec la position des noyaux des nerfs crâniens.]] Les noyaux des nerfs crâniens sont regroupés en plusieurs colonnes de noyaux alignés sur la longueur du tronc cérébral. De plus, les colonnes de noyau sont spécialisées, avec des colonnes composées uniquement de noyaux moteurs, d'autres uniquement de noyaux sensoriels, une autre uniquement de noyaux parasympathiques, etc. Cette organisation dérive de l'évolution embryonnaire du cerveau. Le système nerveux embryonnaire démarre sous la forme d'un tube qui ressemble à une ébauche de moelle épinière : le tube neural. Par la suite, le tube neural se scinde en plusieurs colonnes, qui elles-mêmes se subdivisent en noyaux sur leur longueur. Une fois formés, les noyaux des nerfs crâniens ne migrent pas et restent en place. Leur position conserve donc l'organisation en colonnes sur une bonne partie du tronc cérébral. [[File:Cranial Nerves Apparent Orrigins.jpg|centre|vignette|upright=1.5|Origines apparentes des nerfs crâniens.]] Il existe en tout 6 colonnes de noyaux pour les nerfs crâniens. Il faut faire la différence entre les colonnes motrices et les colonnes sensorielles. Il y a aussi une différence entre les colonnes somatiques et viscérales. Les colonnes somatiques sont pour la motricité et la perception consciente, alors que les colonnes viscérales s'occupent de la motricité/sensibilité inconsciente. En tout, cela fait quatre colonnes, dont deux sont en doubles. Les colonnes motrices regroupent trois colonnes, une colonne somatique, une colonne viscérale non-spécialisée, et une colonne parasympathique. {|class="wikitable" |+ Colonnes motrices |- ! Colonne motrice somatique GSE ! Colonne motrice parasympathique GVE ! Colonne motrice viscérale SVE |- | Noyaux oculomoteurs : * Noyau oculomoteur * Noyau trochléaire * Noyau abducens | Noyaux des nerfs brachiaux : * Noyau moteur du nerf trigéminal * Noyau moteur du nerf facial * Noyau ambigu (nerfs glossopharyngéal, vague et accessoire) | Noyau oculomoteur accessoire d'Edinger-Westphal |- | Noyau hypoglosse || || Noyau salivaire |- | || || Noyau dorsal du nerf vague |} Les colonnes sensorielles sont elles aussi au nombre de trois. {|class="wikitable" |+ Colonnes sensorielles |- ! Colonne sensorielle somatique générale ! Colonne sensorielle somatique spéciale ! Colonne sensorielle viscérale |- | rowspan="3" | Nerfs du nerf trigéminal : * Noyau sensoriel principal du nerf trigéminal * Noyau spinal du nerf trigéminal * Noyau mésencéphalique du nerf trigéminal | rowspan="3" | Noyaux du nerf cochléo-vestibulaire * Noyau cochléaire * Noyau vestibulaire | Noyau du tractus solitaire |- | Noyau dorsal du nerf vague |- | Noyau gustatif |} ==L'anatomie en longueur du tronc cérébral== Le cerveau est subdivisé en cinq grandes aires : le myélencéphale, le métencéphale, le mésencéphale, le diencéphale et le télencéphale. Cette subdivision se développe d'abord dans l'embryon, où ces 5 subdivisons arrivent en premier, avant de se subdiviser en aires cérébrales plus petites. Le tronc cérébral regroupe myélencéphale, métencéphale et mésencéphale, tandis que le reste du cerveau contient le diencéphale et le télencéphale. On a ainsi une première subdivision du tronc cérébral en trois étages, sur sa longueur. * Le myélencéphale, aussi appelé le '''bulbe rachidien''', ou encore la moelle allongée. Comme pour la moelle épinière, la matière grise est localisée au centre de la moelle allongée, la substance blanche entourant celle-ci. La différence avec la moelle épinière tient dans la présence de quelques noyaux, sources de nerfs crâniens divers. * Le '''pont de Varole''', ou métencéphale, se situe en dessous du cervelet. Il ressemble à une sorte de renflement sur la face ventrale du tronc cérébral. Il sert de relai aux axones moteurs et sensoriels et prend en charge certaines fonctions autonomes. De nombreux noyaux de nerfs crâniens sont présents dans le pont, comme les noyaux des nerfs vestibulaires, salivaires, etc. * Le '''mésencéphale''' est une structure beaucoup plus complexe, qui surmonte toutes les autres. [[File:Tronc-cerebral-ventrale.jpg|centre|vignette|upright=2.0|Tronc cérébral.]] Dans ce qui va suivre, nous ne reparlerons pas des noyaux de nerfs crâniens ou de la formation réticulée, mais verrons les noyaux qui n'appartiennent pas à ces structures. ===Le myélencéphale=== C'est dans la moelle allongée qu'on trouve les noyaux des nerfs crâniens qui transmettent la sensibilité du visage et en gèrent la motricité. * Le premier est le '''noyau ambigu''' contrôle la mastication et a un rôle dans la parole. * Le second est le '''noyau salivaire inférieur''' contrôle la salivation par les glandes salivaires. * Et enfin, on y trouve aussi le '''noyau du nerf vague'''. Le myélencéphale contient aussi l''''olive bulbaire''', composée d'une olive inférieure et supérieure : l'olive supérieure a un rôle de traitement des informations auditives alors que l'olive inférieure reçoit des informations via des axones en provenance du cortex moteur et envoie des axones dans le métencéphale. Les autres noyaux de la moelle allongée sont surtout ceux du système nerveux périphérique autonome, qu'il soit sympathique ou parasympathique. Ils gèrent ainsi la pression sanguine, le rythme cardiaque, la respiration, ainsi que les réflexes de salivation, vomissement, mastication et autres. On y trouve le '''centre respiratoire''', un noyau qui s'occupe de l'inspiration et de l'expiration. Ce centre reçoit des informations comme le PH et le taux de dioxyde de carbone dissous du sang, histoire d'augmenter la fréquence de respiration suivant les besoins. Le '''noyau du tractus solitaire''' est impliqué dans le gout, la régulation cardiovasculaire et la sensibilité digestive. Il forme une colonne de neurones anatomiquement unie, que son organisation fonctionnelle segmente en trois parties. Il est segmenté en un segment gustatif spécialisé pour le gout, un centre pour le vomissement et un segment de neurones dédiés à la régulation cardiovasculaire et respiratoire. Il émet des axones regroupés dans un faisceau appelé le '''faisceau solitaire''', qui fait synapse avec un grand nombre d'aires cérébrales : thalamus, hypothalamus, formation réticulée, locus coeruleus, noyaux du Raphé, noyaux parasympathiques, quelques nerfs crâniens, etc. Il reçoit des afférences en provenance du nerf vague, du nerf facial et du nerf glossopharyngien. Ces afférences ne se mélangent pas et atterrissent dans des segments séparés. * Le nerf facial et le nerf glossopharyngien lui envoient les sensation du gout, des perceptions captées par la langue. Elles atterrissent dans un noyau spécialisé : le ''noyau gustatif''. * Le nerf vague transmet des sensations corporelles inconscientes, comme l'état du tube digestif, la teneur en oxygène du sang, la pression artérielle, etc. Elles proviennent de récepteurs sensoriels dispersés dans le tube digestif, de capteurs respiratoires et cardiaques dispersés dans l'organisme, ainsi que de la langue. Pour le nerf vague, les axones sont triés selon qu'ils viennent du tube digestif ou du reste. Les afférences provenant du tube digestif font synapse avec plusieurs noyaux responsables de l'innervation parasympathique du tube digestif, ainsi qu'avec le ''centre du vomissement'', dont le nom parle de lui-même. Les afférences provenant des capteurs respiratoires et cardiaques perçoivent la teneur en CO2 ou en O2 du sang, son pH, la pression sanguine, etc. Ils sont surtout localisés dans l'aorte et les carotides, mais on peut parfois en trouver ailleurs (dans les méninges, par exemple). Ils font synapses sur une colonne de noyaux qui s'occupent de réguler la respiration et le rythme cardiaque, ainsi que la pression sanguine. Ces noyaux sont donc un centre de régulation cardiovasculaire cérébral. Ils communiquent avec d'autres noyaux impliqués dans la respiration, avec le nerf vague, et bien d'autres encore. ===Le métencéphale=== Le pont de Varole contient divers noyaux, dont le plus connu est manifestement le '''locus coerulus'''. Il s'agit d'un noyau qui projette des axones à noradrénaline et à acétylcholine dans tous le cerveau. Fait étrange, les neurones de ce noyau sont remplis d'une substance de couleur noire, la mélanine. Sa présence en faible quantité fait que le locus coerulus a une belle couleur bleue, qui lui a donné son nom (noyau coloré en latin). L'activité de ce noyau est liée à l'attention et la vigilance. Ce noyau subit des pointes d'activité transitoires lors de la présentation de stimulus salients. Il est aussi impliqué dans les réponses au stress ou à la peur. La stimulation de ce noyau a tendance à entrainer de l'anxiété. Les anxiolytiques diminuent l'activité cérébrale dans ce noyau, de même que les antidépresseurs noradrénergiques. Il est totalement inactivé dans certaines phases du sommeil, le sommeil paradoxal pour être précis. Les '''noyaux pédiculopontiques''' sont un ensemble de noyaux qui émettent de l'acétylcholine dans l'ensemble du cerveau. Ils sont impliqués dans diverses fonctions, la principale étant l'activité cholinergique liée à l'éveil ou au sommeil paradoxal. Enfin, on trouve aussi d'autres noyaux qui communiquent avec le cervelet : les '''pédoncules cérébrelleux'''. Nous verrons ceux-ci plus en détail dans le chapitre sur le cervelet. ===Le mésencéphale=== On a vu plus haut que le tronc cérébral est traditionnellement divisé en une région ventrale et un tegmentum. Au niveau du mésencéphale, il faut y ajouter une troisième subdivision : le '''tectum'''. Ce dernier n'est présent que dans le mésencéphale, là où le tegmentum est présent dans toute la longueur du tronc cérébral. [[File:Cn3nucleus-fr.svg|centre|vignette|upright=1.5|Schéma simplifié du mésencéphale.]] Le tectum prend en charge un certain nombre de réflexes oculaires et auditifs, comme les réflexes liés à la pupille ou aux muscles du tympan. Le reste du mésencéphale est appelé l'ensemble des pédoncules cérébraux. Il est découpé en deux parties : le '''colliculus inférieur''' et le '''colliculus supérieur'''. Le premier fait partie du système auditif, avec de nombreuses autres aires cérébrales. Sa fonction, très spécialisée, est de localiser la source d'un son, sa position dans l'espace. Le colliculus supérieur a une fonction totalement différente : il ne fait même pas partie du système auditif, mais appartient aux systèmes visuel et moteur. Sa fonction principale est de diriger le regard vers les objets à regarder. Pour cela, il commande les saccades oculaires (les mouvements des yeux). Le tegmentum et la région ventrale sont séparés par la substance noire, une structure de couleur noire en forme de bandes. La région ventrale est appelée le pied, ou encore le '''crux cerebis'''. Le pied est surtout composé de faisceaux qui prennent naissance dans le cortex cérébral, les plus importants étant les faisceaux moteurs (pyramidaux). La '''substance noire''' est un amas de neurones dopaminergiques impliqué dans la motricité, connue pour être l'aire dont la dégénérescence cause la maladie de Parkinson. Chez le parkinsonien, les neurones de cette zone meurent prématurément, ce qui se traduit par des gestes rigides et saccadés, des mouvements lents et rares, des tremblements, et parfois des troubles intellectuels légers et une baisse de la mémoire ou de la motivation. Elle est divisée en une pars compacta et une pars reticula. Nous verrons cette aire plus en détail dans le chapitre sur les ganglions de la base. Le tegmentum regroupe divers noyaux. Parmi ceux-ci, il nous faut citer le '''noyau rouge''', appelé ainsi à cause de sa couleur légèrement rougeâtre, qui vient de la présence de pigments ferriques dans ses neurones. Ce noyau reçoit des axones provenant du cervelet et des aires motrices du cortex cérébral. On devine ainsi qu'il s'agit d'un noyau essentiellement moteur. Il émet des axones qui se rassemblent pour former un des faisceaux de la moelle épinière : le faisceau rubro-spinal de la substance blanche, appartenant à la voie extra-pyramidale. Le noyau rouge commande les muscles de la posture et joue donc un rôle certain dans le maintien de l'équilibre. Chez les vertébrés "peu évolués", le noyau rouge est la seule aire cérébrale qui prend en charge l'équilibre et le maintien de la posture. Mais chez les animaux "plus évolués", ce rôle est maintenant dévolu principalement au cervelet, le noyau rouge ayant un rôle mineur et/ou plus spécialisé. [[File:Mesencephalus colliculus sup.jpg|centre|vignette|upright=2.5|Schéma plus détaillé du mésencéphale.]] Il faut aussi parler d'un noyau de neurones dopaminergiques qui gère le plaisir suite à une récompense : l''''aire tegmentale ventrale'''. Elle contient de nombreux neurones dopaminergiques qui envoient des axones dans tout le cerveau. Elle est impliquée dans la motivation et la cognition. Elle jouerait un rôle dans les addictions, la dépression, et potentiellement dans les troubles bipolaires et la schizophrénie. ==Les syndromes et maladies du tronc cérébral== Le tronc cérébral est une structure anatomique assez vaste, aux fonctions multiples et variées. Toute lésion dans le tronc cérébral a des conséquences très variables, qui mêlent symptômes moteurs, sensoriels, végétatifs, et autres. En théorie, les fonctions cognitives et intellectuelles sont épargnées après une lésion du tronc cérébral, peu importe sa localisation. Mais on ne peut en dire autant du reste. Précisons que les syndromes du tronc cérébral sont très nombreux, et qu'il n'est pas pertinent de tous les aborder. Raison pour laquelle, dans cette section, nous allons voir quels sont les syndromes les plus communs que l'on peut observer après une lésion du tronc cérébral. Rarement, un noyau ou un faisceau est atteint de manière isolée, sans que les autres structures du tronc soient touchées. Les déficits induits par cette lésion sont alors assez clairs et précis et donnent ce qu'on appelle des ''syndromes purs''. Mais dans la réalité, plusieurs noyaux et/ou faisceaux sont touchés lors d'un AVC ou d'un traumatisme important, ce qui fait que la symptomatologie d'une lésion est la somme de plusieurs syndromes purs. En général, les lésions sont causées par un AVC, de type ischémique, ce qui fait que c'est le territoire de perfusion d'une artère qui est lésé. Les déficits obtenus sont la somme des syndromes associés aux noyaux et faisceaux perfusés par l'artère. Dans ce qui suit, nous allons étudier les syndromes purs, puis les syndromes combinés. ===L'atteinte isolée d'un noyau/faisceau du tronc cérébral=== Les symptômes qui font suite à une lésion du tronc cérébral peuvent se déduire assez facilement quand on sait ce que contient le tronc cérébral : les noyaux des nerfs crâniens, les faisceaux ascendants/descendants, et la formation réticulée. Ces trois structures peuvent être atteintes suite à une lésion, donnant des symptômes divers. {|class="wikitable" |+Symptômes d'une atteinte du tronc cérébral |- !Noyaux des nerfs crâniens |Symptômes dépendants du noyau touché. * Vertiges (noyaux cochléovestibulaire). * Troubles des mouvements oculaires (noyau du nerf oculomoteur commun, noyau du nerf pathétique, noyau du nerf oculomoteur externe). * Déviation de la langue, paralysie linguale (noyau du nerf hypoglosse). * Perte de la sensibilité et de la motricité du visage (noyau du nerf trijumeau ou du nerf facial, ...). * Dysarthrie (difficultés à articuler), dysphagie (difficultés pour avaler). * Diplopie, cécité, troubles visuels. * ... |- !Faisceaux ascendants | * Perte de la sensibilité du corps (sur une moitié du corps pour une lésion unilatérale, sur la totalité du corps pour une lésion bilatérale). |- !Faisceaux descendants | * Paralysie : hémiplégie, quadriplégie * Faiblesse musculaire, dans les cas les moins graves. * Ataxie, dans les cas les moins graves. |- !Formation réticulée | * Troubles de la vigilance/conscience * Perturbations motrices : paralysie, hémiplégie, ... |} Une lésion des noyaux de nerfs crâniens peut causer des troubles de la motricité et de la sensibilité du visage, alors que les lésions des faisceaux tendent à obérer la motricité/sensibilité du corps. Avec les lésions unilatérales (d'un côté du tronc), la perte de sensibilité/motricité ne touche qu'un seul côté du visage/corps, donnant une hémiplégie/hémiparésie ou la perte de la sensibilité sur une moitié du corps/visage. Les lésions de la formation réticulée donnent des troubles de la conscience, pouvant aller jusqu’au coma, parfois accompagnés de troubles moteurs caractéristiques. ===Les syndromes alternes du tronc cérébral=== Les lésions unilatérales du tronc cérébral causent une lésion de plusieurs noyaux de nerfs crâniens, couplée à la lésion d'un faisceau ascendant/descendant. Elles ont une symptomatologie assez particulière au niveau sensitif/moteur, qui leur vaut le nom de '''syndromes alternes'''. Elles se caractérisent par au moins un des deux symptômes suivants : * Une perte des sensations controlatérale pour le corps, mais ipsilatérale pour le visage. * Une perte de la motricité (paralysie, faiblesse musculaire) controlatérale pour le corps, mais ipsilatérale pour le visage. Comme on le voit, le côté du corps touché n'est pas le même que le côté du visage atteint. Cela s'explique par les faits suivants. C'est dans le tronc cérébral que les faisceaux/nerfs changent de côté pour innerver le corps. Par exemple, les fibres sensorielles ascendantes provenant du côté droit du corps vont passer du côté gauche, et réciproquement (même chose pour les fibres motrices). Par contre, ce n'est pas le cas pour les nerfs crâniens, qui restent du même côté du corps. Toute lésion unilatérale (d'un côté) du tronc cérébral va donc léser le faisceau innervant la moitié du corps, alors que les noyaux des nerfs crâniens innervent le visage de l'autre côté. ====Le syndrome latéral médullaire (de Wallenberg)==== [[File:Stickerman Wallenberg.png|vignette|Syndrome de Wallenberg.]] Le syndrome alterne le plus fréquemment rencontré par les neurologues est le '''syndrome de Wallenberg'''. Il apparaît quand un AVC a lieu dans l'artère cérébelleuse inféro-postérieure, qui alimente en sang le cervelet et la moelle allongée, ou dans une artère proche. Les noyaux et faisceaux suivants sont touchés : le faisceau spino-thalamique, les faisceaux sympathiques, le noyau vestibulaire, le cervelet, et quelques autres noyaux. En général, le syndrome n'est pas complet chez la plupart des patients, qui ne montrent qu'une partie des symptômes. Mais sa présentation classique est la suivante : {|class="wikitable" |- ! colspan="2" | Du côté de la lésion (côté ipsilatéral) ! colspan="2" | Du côté opposé à la lésion (côté controlatéral) |- !Symptôme !Localisation de la lésion (ipsilatérale) !Symptôme !Localisation de la lésion (controlatérale) |- |Syndrome de Claude-Bernard-Horner. |Fibres sympathiques |- |Syndrome vestibulaire (vertiges, perte de l'équilibre, chutes, nausées, vomissements). Syndrome cérébelleux (ataxie, nystagmus, pertes d'équilibre, vertiges, ...). |Noyau vestibulaire Cervelet |- |Paralysie de l'hémi-voile, l'hémi-pharynx et de la corde vocale, qui entraîne dysphagie, dysphonie et dysarthrie. |Noyau ambigu |- |Anesthésie thermoalgique (perte de la douleur et des sensations de chaleur/froid) sur le visage. |Noyau du trijumeau | rowspan="5" | Anesthésie thermoalgique (perte de la douleur et des sensations de chaleur/froid) sur le corps. | rowspan="5" | Faisceau spino-thalamique. |} : Parfois, ce syndrome se complète d'une hémiplégie/hémiparésie controlatérale. On parle alors d'un '''syndrome de Babinski-Nageotte'''. Plus rarement on peut observer, du côté de la lésion : * Des douleurs dans le visage, avec une perte de sensibilité facile - atteinte du noyau du trijumeau. * Une réduction du goût, voir une agueusie (perte totale du goût) - atteinte du noyau gustatif. * Une paralysie de la langue - atteinte du noyau hypoglosse. ====Le syndrome médullaire médian (de Dejerine)==== Le '''syndrome de Dejerine ''' se caractérise par une lésion du noyau hypoglosse, du faisceau pyramidal, et du lemnisque médian (faisceau qui suit les colonnes dorsales de la moelle épinière). Il se traduit par : * une déviation de la langue du côté de la lésion, causée par la lésion du noyau hypoglosse ; * une faiblesse musculaire/paralysie du côté controlatéral à la lésion, causée par la lésion du faisceau pyramidal ; * et enfin une perte des sensations tactiles sur le côté controlatéral du corps, causé par la lésion du lemnisque médian. : Il est souvent accompagné d'un syndrome de Wallenberg. ====Les syndromes pontiques==== Les syndromes de cette section ont pour origine une atteinte du métencéphale (le pont de Varole). Ils sont assez nombreux, aussi nous allons nous concentrer sur les principaux. Pour la plupart, ils ont pour symptôme une paralysie ou du moins une parésie, controlatérale. Sa cause est une lésion du faisceau pyramidal, qui traverse le pont de Varole. Précisons d'ailleurs que tout syndrome du tronc cérébral peut entraîner une paralysie par section du faisceau pyramidal, et pas seulement les syndromes pontiques. Même chose pour les autres faisceaux spino-thalamique et spino-corticaux, ce qui peut entraîner une anesthésie tactile et/ou thermoalgique, controlatérale pour le corps, mais ipsilatérale pour le visage. Un syndrome de Horner est aussi possible, vu que les fibres sympathiques parcourent le pont de Varole. La différence entre les syndromes pontiques se fait sur les noyaux des nerfs crâniens touchés par la lésion. Le '''syndrome de Millard-Gubler ''', aussi appelé syndrome du pont ventral, est causé par une lésion des noyaux des nerfs crâniens moteurs 6 et 7, en plus de la lésion du faisceau pyramidal. Le syndrome pyramidal se couple donc avec d'autres symptômes moteurs. La lésion du noyau 6 entraîne une paralysie oculaire pour les mouvements latéraux, une diplopie et un strabisme. La lésion du noyau 7 cause une paralysie facile ipsilatérale. ====Les syndromes mésencéphaliques==== Les syndromes précédents surviennent suite à des lésions dans le pont de Varole ou la moelle allongée, éventuellement accompagnées de lésions au cervelet. Dans cette section, nous allons aborder les syndromes causés par une lésion du mésencéphale. Nous les voyons dans une même section, car ils sont tous très ressemblants et qu'ils partagent des symptômes communs, au point qu'il est très difficile de les distinguer. Il s'agit des syndromes de Claude, Benedict et Weber. Les autres syndromes mésencéphaliques sont beaucoup plus rares que le syndrome de Weber, avec une prévalence bien plus faible. Dans tous les symptômes mésencéphaliques, on observe un même ensemble commun de symptômes, qui regroupe : * Des troubles oculomoteurs ipsilatéraux : paralysie oculaire, pupilles dilatées, chute des paupières (ptosis), etc. * Des troubles moteurs controlatéraux, qui dépendent du syndrome considéré : ataxie, tremblements ou hémiparésie/hémiplégie. Les trois syndromes considérés se traduisent par des lésions des noyaux oculomoteurs, qui entraînent les troubles oculaires mentionnés plus haut. Les symptômes moteurs dépendent du syndrome. Dans le '''syndrome de Weber''', le trouble moteur de ce syndrome est une hémiparésie/hémiplégie corporelle (les membres et éventuellement le reste du corps est paralysé), controlatérale. Il est causé par une atteinte des noyaux oculomoteurs couplée à une lésion du faisceau pyramidal, sans atteinte supplémentaire. Dans le '''syndrome de Claude''' et le '''syndrome de Benedict''', on observe des lésions du noyau rouge et du cervelet, en plus des lésions des noyaux oculomoteurs. Cela se traduit par l'apparition d'une ataxie et/ou de tremblements, complémentaires de l’hémiparésie/hémiplégie. La différence entre les trois syndromes se fait sur le symptôme moteur prédominant : hémiparésie pour le syndrome de Weber, ataxie pour le syndrome de Benedict, tremblement pour le syndrome de Benedict. {|class="wikitable" |+ Syndromes mésencéphaliques |- ! ! Syndrome de Weber ! Syndrome de Claude ! Syndrome de Benedict ! Localisation de la lésion responsable du symptôme |- ! rowspan="4" | Symptômes | colspan="3" | Troubles oculomoteurs : paralysie oculaire, ptosis, pupilles dilatées, etc. | Noyaux des nerfs crâniens oculomoteurs |- | Hémiplégie/hémiparésie controlatérale | | | Faisceau cortico-spinal (pyramidal) |- | | Ataxie | | Cervelet, noyau rouge |- | | | Tremblements | Cervelet, noyau rouge |} Dans les faits, il n'est pas rare que d'autres manifestations neurologiques s'y ajoutent. Par exemple, on peut observer un syndrome parkinsonien, résultant d'une atteinte de la substance noire, proche des autres structures lésées. Ou encore, on peut observer des problèmes linguaux, causés par une atteinte du noyau hypoglosse et du faisceau cortico-bulbaire. Si le syndrome de Weber est un très mauvais signe à court-terme, les patients qui survivent récupèrent bien dans le cas général, à condition que le syndrome de Weber soit pur (sans symptômes ajoutés). <noinclude> {{NavChapitre | book=Neurosciences | prev=Les nerfs crâniens | prevText=Les nerfs crâniens | next=Le cerveau antérieur | nextText=Le cerveau antérieur }}{{autoCat}} </noinclude> 12uit8m2um5yp8k9qqu9hsrg2x8abkv 762838 762837 2026-04-03T14:30:34Z Mewtow 31375 /* Le mésencéphale */ 762838 wikitext text/x-wiki [[File:Brain sagittal section stem highlighted.svg|vignette|Tronc cérébral.]] Le tronc cérébral est une partie du cerveau qu'il est très difficile de décrire simplement. Sa complexité fait qu'il vaut mieux lui dédier un chapitre complet. Le tronc cérébral contient à la fois de la substance grise (neurones) que de la substance blanche (axones myélinisés). La substance blanche est surtout composée de faisceaux qui poursuivent les voies ascendantes/descendantes et traversent le tronc cérébral sur la majeure partie de sa longueur. La matière grise se limite à des noyaux enfouis dans la substance blanche et ne contient aucun cortex (couches de neurones). Et ces noyaux sont très nombreux. Les noyaux du tronc cérébral regroupent des noyaux moteurs, des noyaux sensoriels, des noyaux mixtes sensimoteurs, des noyaux d'association, tous les types sont représentés. D'autres noyaux ont des rôles plus larges ou mal connus, avec une influence sur la cognition, le noyau du Raphé en étant un bon exemple. Ils contiennent aussi les '''noyaux des nerfs crâniens''', qui sont tous dans le tronc cérébral. Une bonne partie de ces noyaux sera étudié en détail dans les chapitres portant sur les sens ou la motricité. Nous allons cependant survoler les principaux noyaux, en donnant si possible quelques détails sur leur fonctionnement dans ce qui suit. ==L'anatomie en largeur (coupe-section) du tronc cérébral== [[File:Coupe section du tronc cérébral.png|vignette|Coupe section du tronc cérébral.]] Dans cette section suivante, nous verrons comment s'organise le tronc cérébral sur sa largeur, en coupe-section. On s’aperçoit alors que le tronc cérébral est composé de deux à trois subdivisions selon l'étage. On distingue une '''région ventrale''', une région dorsale appelée le '''tegmentum''', et une région qui n’apparaît que dans le mésencéphale : le '''tectum'''. La région ventrale est séparée du tegmentum par l’aqueduc de Sylvius, une sorte de canal qui relie le 3ème et le 4ème ventricule (on abordera les ventricules plus tard, dans un futur chapitre). La région ventrale contient surtout des faisceaux de matière blanche, dont la plupart se prolongent dans la moelle épinière. Tel est le cas du faisceau cortico-spinal, qui transmet les commandes motrices volontaires. Le tegmentum est lui-même composé de plusieurs formations. Parmi celles-ci, on trouve la formation réticulée, un lacis de noyaux et de fibres nerveuses assez complexe, lui-même formé de trois colonnes. ===La formation réticulée=== La '''formation réticulée''' est une structure complexe, qui mélange divers noyaux et un ensemble de fibres entrelacées très difficile à décrire. La formation réticulée a la forme d'une bande grise qui court sur toute la longueur du tronc cérébral. Elle est entremêlée avec divers noyaux, dont les noyaux des nerfs crâniens. Cette formation émet des axones dans tout le cerveau, et notamment dans la totalité du télencéphale. La formation réticulée est divisée en trois colonnes : une colonne centrale qui contient les noyaux du raphé, une colonne médiane et une colonne latérale. La colonne médiane est composée de neurones de grande taille, alors que la colonne latérale est composée de neurones de petite taille. Les scientifiques ont depuis longtemps subdivisé la formation réticulée en deux systèmes fonctionnels distincts : un système réticulé ascendant/activateur qui maintient l'état d'éveil, et un système réticulé descendant. Le '''système réticulaire descendant''' a plusieurs fonctions, qui vont de la modulation de la douleur à la commande de la motricité extrapyramidale. Il est à l'origine d'un des faisceaux de la substance blanche de la moelle épinière, le faisceau réticulospinal, une sous-portion du faisceau extrapyramidal (n'hésitez pas à consulter le chapitre sur la moelle épinière pour des rappels). La formation réticulée descendante commande les muscles du maintien de la posture, ainsi que les muscles qui gèrent les mouvements fins, précis. D'autres portions de ce faisceau jouent un rôle dans la modulation de la douleur. En comparaison, le '''système réticulaire ascendant''' joue un grand rôle dans l'état de veille, l'attention, la vigilance et l'activation physiologique (l'intensité de l'état de veille). Lors de l'éveil, ces axones excitent les aires cérébrales de destination, donnant naissance à une activité électrique minimale au repos, ce qui permet de maintenir le sujet éveillé ou conscient. Lors du sommeil, cette formation diminue son activité, mais ne l'annule pas totalement. Une lésion de cette structure peut causer un coma irréversible, alors que sa stimulation induit un éveil soutenu. Outre son rôle dans la vigilance et l'éveil, elle influence aussi la perception de la douleur ainsi que la motricité. Nous reparlerons plus loin de ces deux systèmes, dans les chapitres sur la motricité extrapyramidale et le sommeil. ====La colonne centrale : les noyaux du Raphé==== Les '''noyaux du Raphé''' sont des noyaux regroupés dans la colonne centrale de la formation réticulée, cette colonne contenant plusieurs noyaux. Suivant les nomenclatures, le nombre de noyaux du Raphé varient, allant de seulement deux à trois noyaux à plus de neufs noyaux notés B1, B2, ... , B9. Certains de ces noyaux ont un nom plus commun : le noyau obscurus, le noyau pallidus, le noyau magnus, le noyau pontin, le noyau central supérieur et le noyau dorsal. Les noyaux localisés dans la moelle allongée (bulbe rachidien) innervent la moelle épinière, et notamment les interneurones qui transmettent la douleur. Ces innervations permettent de contrôler les sensations douloureuses, leur intensité. Les noyaux localisés dans le reste du tronc cérébral secrètent de la sérotonine dans tout le cerveau. Les axones de ces noyaux innervent aussi bien le cervelet que le cortex ou d'autres structures cérébrales. Ces axones qui innervent le cerveau ont un effet stimulant sur celui-ci, qui maintient l'état d'éveil. Par contre, une lésion de ces centres induit une insomnie. Vu leur rôle, on devine rapidement qu'ils font partie intégrante du système réticulée ascendant. Ils sont aussi impliqués dans la régulation de l'humeur, avec un effet indirect sur l'impulsivité et l'agressivité. La raison à cela est que la sérotonine, émise en grande partie (mais pas seulement) par les noyaux du raphé, a un rôle prédominant dans la régulation de l'humeur. [[File:Serotonergic neurons.svg|centre|vignette|upright=1.5|Cette image montre les axones émis par les noyaux du Raphé. On voit que ceux-ci innervent la quasi-totalité du télencéphale, ainsi que le cervelet et certaines parties du diencéphale.]] ===Les noyaux des nerfs crâniens=== [[File:Gray697.png|vignette|Nerfs crâniens moteurs.]] [[File:Gray698.png|vignette|Nerfs crâniens sensoriels.]] Le tronc cérébral contient aussi les noyaux des nerfs crâniens, à l'exception des nerfs olfactifs et optiques. Les nerfs optiques et olfactifs forment un groupe à part, vu qu'ils sont dans le cerveau antérieur (le diencéphale, pour être précis) et qu'ils font partie du système nerveux central. Les noyaux des nerfs crâniens sont classés en plusieurs groupes, selon leur origine embryonnaire/évolutive. Les nerfs hypoglosse et vestibulo-cochléaire sont chacun une catégorie à part. Le reste sépare les nerfs oculomoteur et branchiaux. Les cinq '''nerfs branchiaux''' s’appellent ainsi car ils sont l'équivalent modernes des nerfs qui innervent les branchies des poissons. {|class="wikitable" |+ Nerfs crâniens du système nerveux périphérique |- !rowspan="3"| Nerfs oculo-moteurs |Nerf oculo-moteur commun (III) |- |Nerf trochléaire ou pathétique (IV) |- |Nerf oculo-moteur externe ou abducens (VI) |- !rowspan="5"| Nerfs branchiaux |Nerf trijumeau (V) |- |Nerf facial (VII) |- |Nerf glosso-pharyngien (IX) |- |Nerf vague ou pneumogastrique (X) |- |Nerf spinal ou accessoire (XI) |- !colspan="2"| Nerf hypoglosse (XII) |- !colspan="2"| Nerf vestibulo-cochléaire ou auditif (VIII) |} [[File:Brain stem sagittal.svg|centre|vignette|upright=2|Section du tronc cérébral avec la position des noyaux des nerfs crâniens.]] Les noyaux des nerfs crâniens sont regroupés en plusieurs colonnes de noyaux alignés sur la longueur du tronc cérébral. De plus, les colonnes de noyau sont spécialisées, avec des colonnes composées uniquement de noyaux moteurs, d'autres uniquement de noyaux sensoriels, une autre uniquement de noyaux parasympathiques, etc. Cette organisation dérive de l'évolution embryonnaire du cerveau. Le système nerveux embryonnaire démarre sous la forme d'un tube qui ressemble à une ébauche de moelle épinière : le tube neural. Par la suite, le tube neural se scinde en plusieurs colonnes, qui elles-mêmes se subdivisent en noyaux sur leur longueur. Une fois formés, les noyaux des nerfs crâniens ne migrent pas et restent en place. Leur position conserve donc l'organisation en colonnes sur une bonne partie du tronc cérébral. [[File:Cranial Nerves Apparent Orrigins.jpg|centre|vignette|upright=1.5|Origines apparentes des nerfs crâniens.]] Il existe en tout 6 colonnes de noyaux pour les nerfs crâniens. Il faut faire la différence entre les colonnes motrices et les colonnes sensorielles. Il y a aussi une différence entre les colonnes somatiques et viscérales. Les colonnes somatiques sont pour la motricité et la perception consciente, alors que les colonnes viscérales s'occupent de la motricité/sensibilité inconsciente. En tout, cela fait quatre colonnes, dont deux sont en doubles. Les colonnes motrices regroupent trois colonnes, une colonne somatique, une colonne viscérale non-spécialisée, et une colonne parasympathique. {|class="wikitable" |+ Colonnes motrices |- ! Colonne motrice somatique GSE ! Colonne motrice parasympathique GVE ! Colonne motrice viscérale SVE |- | Noyaux oculomoteurs : * Noyau oculomoteur * Noyau trochléaire * Noyau abducens | Noyaux des nerfs brachiaux : * Noyau moteur du nerf trigéminal * Noyau moteur du nerf facial * Noyau ambigu (nerfs glossopharyngéal, vague et accessoire) | Noyau oculomoteur accessoire d'Edinger-Westphal |- | Noyau hypoglosse || || Noyau salivaire |- | || || Noyau dorsal du nerf vague |} Les colonnes sensorielles sont elles aussi au nombre de trois. {|class="wikitable" |+ Colonnes sensorielles |- ! Colonne sensorielle somatique générale ! Colonne sensorielle somatique spéciale ! Colonne sensorielle viscérale |- | rowspan="3" | Nerfs du nerf trigéminal : * Noyau sensoriel principal du nerf trigéminal * Noyau spinal du nerf trigéminal * Noyau mésencéphalique du nerf trigéminal | rowspan="3" | Noyaux du nerf cochléo-vestibulaire * Noyau cochléaire * Noyau vestibulaire | Noyau du tractus solitaire |- | Noyau dorsal du nerf vague |- | Noyau gustatif |} ==L'anatomie en longueur du tronc cérébral== Le cerveau est subdivisé en cinq grandes aires : le myélencéphale, le métencéphale, le mésencéphale, le diencéphale et le télencéphale. Cette subdivision se développe d'abord dans l'embryon, où ces 5 subdivisons arrivent en premier, avant de se subdiviser en aires cérébrales plus petites. Le tronc cérébral regroupe myélencéphale, métencéphale et mésencéphale, tandis que le reste du cerveau contient le diencéphale et le télencéphale. On a ainsi une première subdivision du tronc cérébral en trois étages, sur sa longueur. * Le myélencéphale, aussi appelé le '''bulbe rachidien''', ou encore la moelle allongée. Comme pour la moelle épinière, la matière grise est localisée au centre de la moelle allongée, la substance blanche entourant celle-ci. La différence avec la moelle épinière tient dans la présence de quelques noyaux, sources de nerfs crâniens divers. * Le '''pont de Varole''', ou métencéphale, se situe en dessous du cervelet. Il ressemble à une sorte de renflement sur la face ventrale du tronc cérébral. Il sert de relai aux axones moteurs et sensoriels et prend en charge certaines fonctions autonomes. De nombreux noyaux de nerfs crâniens sont présents dans le pont, comme les noyaux des nerfs vestibulaires, salivaires, etc. * Le '''mésencéphale''' est une structure beaucoup plus complexe, qui surmonte toutes les autres. [[File:Tronc-cerebral-ventrale.jpg|centre|vignette|upright=2.0|Tronc cérébral.]] Dans ce qui va suivre, nous ne reparlerons pas des noyaux de nerfs crâniens ou de la formation réticulée, mais verrons les noyaux qui n'appartiennent pas à ces structures. ===Le myélencéphale=== C'est dans la moelle allongée qu'on trouve les noyaux des nerfs crâniens qui transmettent la sensibilité du visage et en gèrent la motricité. * Le premier est le '''noyau ambigu''' contrôle la mastication et a un rôle dans la parole. * Le second est le '''noyau salivaire inférieur''' contrôle la salivation par les glandes salivaires. * Et enfin, on y trouve aussi le '''noyau du nerf vague'''. Le myélencéphale contient aussi l''''olive bulbaire''', composée d'une olive inférieure et supérieure : l'olive supérieure a un rôle de traitement des informations auditives alors que l'olive inférieure reçoit des informations via des axones en provenance du cortex moteur et envoie des axones dans le métencéphale. Les autres noyaux de la moelle allongée sont surtout ceux du système nerveux périphérique autonome, qu'il soit sympathique ou parasympathique. Ils gèrent ainsi la pression sanguine, le rythme cardiaque, la respiration, ainsi que les réflexes de salivation, vomissement, mastication et autres. On y trouve le '''centre respiratoire''', un noyau qui s'occupe de l'inspiration et de l'expiration. Ce centre reçoit des informations comme le PH et le taux de dioxyde de carbone dissous du sang, histoire d'augmenter la fréquence de respiration suivant les besoins. Le '''noyau du tractus solitaire''' est impliqué dans le gout, la régulation cardiovasculaire et la sensibilité digestive. Il forme une colonne de neurones anatomiquement unie, que son organisation fonctionnelle segmente en trois parties. Il est segmenté en un segment gustatif spécialisé pour le gout, un centre pour le vomissement et un segment de neurones dédiés à la régulation cardiovasculaire et respiratoire. Il émet des axones regroupés dans un faisceau appelé le '''faisceau solitaire''', qui fait synapse avec un grand nombre d'aires cérébrales : thalamus, hypothalamus, formation réticulée, locus coeruleus, noyaux du Raphé, noyaux parasympathiques, quelques nerfs crâniens, etc. Il reçoit des afférences en provenance du nerf vague, du nerf facial et du nerf glossopharyngien. Ces afférences ne se mélangent pas et atterrissent dans des segments séparés. * Le nerf facial et le nerf glossopharyngien lui envoient les sensation du gout, des perceptions captées par la langue. Elles atterrissent dans un noyau spécialisé : le ''noyau gustatif''. * Le nerf vague transmet des sensations corporelles inconscientes, comme l'état du tube digestif, la teneur en oxygène du sang, la pression artérielle, etc. Elles proviennent de récepteurs sensoriels dispersés dans le tube digestif, de capteurs respiratoires et cardiaques dispersés dans l'organisme, ainsi que de la langue. Pour le nerf vague, les axones sont triés selon qu'ils viennent du tube digestif ou du reste. Les afférences provenant du tube digestif font synapse avec plusieurs noyaux responsables de l'innervation parasympathique du tube digestif, ainsi qu'avec le ''centre du vomissement'', dont le nom parle de lui-même. Les afférences provenant des capteurs respiratoires et cardiaques perçoivent la teneur en CO2 ou en O2 du sang, son pH, la pression sanguine, etc. Ils sont surtout localisés dans l'aorte et les carotides, mais on peut parfois en trouver ailleurs (dans les méninges, par exemple). Ils font synapses sur une colonne de noyaux qui s'occupent de réguler la respiration et le rythme cardiaque, ainsi que la pression sanguine. Ces noyaux sont donc un centre de régulation cardiovasculaire cérébral. Ils communiquent avec d'autres noyaux impliqués dans la respiration, avec le nerf vague, et bien d'autres encore. ===Le métencéphale=== Le pont de Varole contient divers noyaux, dont le plus connu est manifestement le '''locus coerulus'''. Il s'agit d'un noyau qui projette des axones à noradrénaline et à acétylcholine dans tous le cerveau. Fait étrange, les neurones de ce noyau sont remplis d'une substance de couleur noire, la mélanine. Sa présence en faible quantité fait que le locus coerulus a une belle couleur bleue, qui lui a donné son nom (noyau coloré en latin). L'activité de ce noyau est liée à l'attention et la vigilance. Ce noyau subit des pointes d'activité transitoires lors de la présentation de stimulus salients. Il est aussi impliqué dans les réponses au stress ou à la peur. La stimulation de ce noyau a tendance à entrainer de l'anxiété. Les anxiolytiques diminuent l'activité cérébrale dans ce noyau, de même que les antidépresseurs noradrénergiques. Il est totalement inactivé dans certaines phases du sommeil, le sommeil paradoxal pour être précis. Les '''noyaux pédiculopontiques''' sont un ensemble de noyaux qui émettent de l'acétylcholine dans l'ensemble du cerveau. Ils sont impliqués dans diverses fonctions, la principale étant l'activité cholinergique liée à l'éveil ou au sommeil paradoxal. Enfin, on trouve aussi d'autres noyaux qui communiquent avec le cervelet : les '''pédoncules cérébrelleux'''. Nous verrons ceux-ci plus en détail dans le chapitre sur le cervelet. ===Le mésencéphale=== On a vu plus haut que le tronc cérébral est traditionnellement divisé en une région ventrale et un tegmentum. Au niveau du mésencéphale, il faut y ajouter une troisième subdivision : le '''tectum'''. Ce dernier n'est présent que dans le mésencéphale, là où le tegmentum est présent dans toute la longueur du tronc cérébral. [[File:Cn3nucleus-fr.svg|centre|vignette|upright=1.5|Schéma simplifié du mésencéphale.]] Le tectum prend en charge un certain nombre de réflexes oculaires et auditifs, comme les réflexes liés à la pupille ou aux muscles du tympan. Le reste du mésencéphale est appelé l'ensemble des pédoncules cérébraux. Il est découpé en deux parties : le '''colliculus inférieur''' et le '''colliculus supérieur'''. Le premier fait partie du système auditif, avec de nombreuses autres aires cérébrales. Sa fonction, très spécialisée, est de localiser la source d'un son, sa position dans l'espace. Le colliculus supérieur a une fonction totalement différente : il ne fait même pas partie du système auditif, mais appartient aux systèmes visuel et moteur. Sa fonction principale est de diriger le regard vers les objets à regarder. Pour cela, il commande les saccades oculaires (les mouvements des yeux). Le tegmentum et la région ventrale sont séparés par une structure de couleur noire en forme de bandes, appelée la substance noire. La '''substance noire''' est un amas de neurones dopaminergiques impliqué dans la motricité, connue pour être l'aire dont la dégénérescence cause la maladie de Parkinson. Nous verrons cette aire plus en détail dans le chapitre sur les ganglions de la base. La région ventrale est appelée le pied, le crus cérébral, ou encore le '''crux cerebis'''. Elle est surtout composés de faisceaux qui prennent naissance dans le cortex cérébral, les plus importants étant les faisceaux moteurs (pyramidaux). Le tegmentum regroupe divers noyaux. Parmi ceux-ci, il nous faut citer le '''noyau rouge''', appelé ainsi à cause de sa couleur légèrement rougeâtre, qui vient de la présence de pigments ferriques dans ses neurones. Ce noyau reçoit des axones provenant du cervelet et des aires motrices du cortex cérébral. Il émet des axones qui se rassemblent pour former un des faisceaux de la moelle épinière : le faisceau rubro-spinal de la substance blanche, appartenant à la voie extra-pyramidale. Le noyau rouge commande les muscles de la posture et joue donc un rôle certain dans le maintien de l'équilibre. Chez les vertébrés "peu évolués", le noyau rouge est la seule aire cérébrale qui prend en charge l'équilibre et le maintien de la posture. Mais chez les animaux "plus évolués", ce rôle est maintenant dévolu principalement au cervelet, le noyau rouge ayant un rôle mineur et/ou plus spécialisé. [[File:Mesencephalus colliculus sup.jpg|centre|vignette|upright=2.5|Schéma plus détaillé du mésencéphale.]] Il faut aussi parler d'un noyau de neurones dopaminergiques qui gère le plaisir suite à une récompense : l''''aire tegmentale ventrale'''. Elle contient de nombreux neurones dopaminergiques qui envoient des axones dans tout le cerveau. Elle est impliquée dans la motivation et la cognition. Elle jouerait un rôle dans les addictions, la dépression, et potentiellement dans les troubles bipolaires et la schizophrénie. ==Les syndromes et maladies du tronc cérébral== Le tronc cérébral est une structure anatomique assez vaste, aux fonctions multiples et variées. Toute lésion dans le tronc cérébral a des conséquences très variables, qui mêlent symptômes moteurs, sensoriels, végétatifs, et autres. En théorie, les fonctions cognitives et intellectuelles sont épargnées après une lésion du tronc cérébral, peu importe sa localisation. Mais on ne peut en dire autant du reste. Précisons que les syndromes du tronc cérébral sont très nombreux, et qu'il n'est pas pertinent de tous les aborder. Raison pour laquelle, dans cette section, nous allons voir quels sont les syndromes les plus communs que l'on peut observer après une lésion du tronc cérébral. Rarement, un noyau ou un faisceau est atteint de manière isolée, sans que les autres structures du tronc soient touchées. Les déficits induits par cette lésion sont alors assez clairs et précis et donnent ce qu'on appelle des ''syndromes purs''. Mais dans la réalité, plusieurs noyaux et/ou faisceaux sont touchés lors d'un AVC ou d'un traumatisme important, ce qui fait que la symptomatologie d'une lésion est la somme de plusieurs syndromes purs. En général, les lésions sont causées par un AVC, de type ischémique, ce qui fait que c'est le territoire de perfusion d'une artère qui est lésé. Les déficits obtenus sont la somme des syndromes associés aux noyaux et faisceaux perfusés par l'artère. Dans ce qui suit, nous allons étudier les syndromes purs, puis les syndromes combinés. ===L'atteinte isolée d'un noyau/faisceau du tronc cérébral=== Les symptômes qui font suite à une lésion du tronc cérébral peuvent se déduire assez facilement quand on sait ce que contient le tronc cérébral : les noyaux des nerfs crâniens, les faisceaux ascendants/descendants, et la formation réticulée. Ces trois structures peuvent être atteintes suite à une lésion, donnant des symptômes divers. {|class="wikitable" |+Symptômes d'une atteinte du tronc cérébral |- !Noyaux des nerfs crâniens |Symptômes dépendants du noyau touché. * Vertiges (noyaux cochléovestibulaire). * Troubles des mouvements oculaires (noyau du nerf oculomoteur commun, noyau du nerf pathétique, noyau du nerf oculomoteur externe). * Déviation de la langue, paralysie linguale (noyau du nerf hypoglosse). * Perte de la sensibilité et de la motricité du visage (noyau du nerf trijumeau ou du nerf facial, ...). * Dysarthrie (difficultés à articuler), dysphagie (difficultés pour avaler). * Diplopie, cécité, troubles visuels. * ... |- !Faisceaux ascendants | * Perte de la sensibilité du corps (sur une moitié du corps pour une lésion unilatérale, sur la totalité du corps pour une lésion bilatérale). |- !Faisceaux descendants | * Paralysie : hémiplégie, quadriplégie * Faiblesse musculaire, dans les cas les moins graves. * Ataxie, dans les cas les moins graves. |- !Formation réticulée | * Troubles de la vigilance/conscience * Perturbations motrices : paralysie, hémiplégie, ... |} Une lésion des noyaux de nerfs crâniens peut causer des troubles de la motricité et de la sensibilité du visage, alors que les lésions des faisceaux tendent à obérer la motricité/sensibilité du corps. Avec les lésions unilatérales (d'un côté du tronc), la perte de sensibilité/motricité ne touche qu'un seul côté du visage/corps, donnant une hémiplégie/hémiparésie ou la perte de la sensibilité sur une moitié du corps/visage. Les lésions de la formation réticulée donnent des troubles de la conscience, pouvant aller jusqu’au coma, parfois accompagnés de troubles moteurs caractéristiques. ===Les syndromes alternes du tronc cérébral=== Les lésions unilatérales du tronc cérébral causent une lésion de plusieurs noyaux de nerfs crâniens, couplée à la lésion d'un faisceau ascendant/descendant. Elles ont une symptomatologie assez particulière au niveau sensitif/moteur, qui leur vaut le nom de '''syndromes alternes'''. Elles se caractérisent par au moins un des deux symptômes suivants : * Une perte des sensations controlatérale pour le corps, mais ipsilatérale pour le visage. * Une perte de la motricité (paralysie, faiblesse musculaire) controlatérale pour le corps, mais ipsilatérale pour le visage. Comme on le voit, le côté du corps touché n'est pas le même que le côté du visage atteint. Cela s'explique par les faits suivants. C'est dans le tronc cérébral que les faisceaux/nerfs changent de côté pour innerver le corps. Par exemple, les fibres sensorielles ascendantes provenant du côté droit du corps vont passer du côté gauche, et réciproquement (même chose pour les fibres motrices). Par contre, ce n'est pas le cas pour les nerfs crâniens, qui restent du même côté du corps. Toute lésion unilatérale (d'un côté) du tronc cérébral va donc léser le faisceau innervant la moitié du corps, alors que les noyaux des nerfs crâniens innervent le visage de l'autre côté. ====Le syndrome latéral médullaire (de Wallenberg)==== [[File:Stickerman Wallenberg.png|vignette|Syndrome de Wallenberg.]] Le syndrome alterne le plus fréquemment rencontré par les neurologues est le '''syndrome de Wallenberg'''. Il apparaît quand un AVC a lieu dans l'artère cérébelleuse inféro-postérieure, qui alimente en sang le cervelet et la moelle allongée, ou dans une artère proche. Les noyaux et faisceaux suivants sont touchés : le faisceau spino-thalamique, les faisceaux sympathiques, le noyau vestibulaire, le cervelet, et quelques autres noyaux. En général, le syndrome n'est pas complet chez la plupart des patients, qui ne montrent qu'une partie des symptômes. Mais sa présentation classique est la suivante : {|class="wikitable" |- ! colspan="2" | Du côté de la lésion (côté ipsilatéral) ! colspan="2" | Du côté opposé à la lésion (côté controlatéral) |- !Symptôme !Localisation de la lésion (ipsilatérale) !Symptôme !Localisation de la lésion (controlatérale) |- |Syndrome de Claude-Bernard-Horner. |Fibres sympathiques |- |Syndrome vestibulaire (vertiges, perte de l'équilibre, chutes, nausées, vomissements). Syndrome cérébelleux (ataxie, nystagmus, pertes d'équilibre, vertiges, ...). |Noyau vestibulaire Cervelet |- |Paralysie de l'hémi-voile, l'hémi-pharynx et de la corde vocale, qui entraîne dysphagie, dysphonie et dysarthrie. |Noyau ambigu |- |Anesthésie thermoalgique (perte de la douleur et des sensations de chaleur/froid) sur le visage. |Noyau du trijumeau | rowspan="5" | Anesthésie thermoalgique (perte de la douleur et des sensations de chaleur/froid) sur le corps. | rowspan="5" | Faisceau spino-thalamique. |} : Parfois, ce syndrome se complète d'une hémiplégie/hémiparésie controlatérale. On parle alors d'un '''syndrome de Babinski-Nageotte'''. Plus rarement on peut observer, du côté de la lésion : * Des douleurs dans le visage, avec une perte de sensibilité facile - atteinte du noyau du trijumeau. * Une réduction du goût, voir une agueusie (perte totale du goût) - atteinte du noyau gustatif. * Une paralysie de la langue - atteinte du noyau hypoglosse. ====Le syndrome médullaire médian (de Dejerine)==== Le '''syndrome de Dejerine ''' se caractérise par une lésion du noyau hypoglosse, du faisceau pyramidal, et du lemnisque médian (faisceau qui suit les colonnes dorsales de la moelle épinière). Il se traduit par : * une déviation de la langue du côté de la lésion, causée par la lésion du noyau hypoglosse ; * une faiblesse musculaire/paralysie du côté controlatéral à la lésion, causée par la lésion du faisceau pyramidal ; * et enfin une perte des sensations tactiles sur le côté controlatéral du corps, causé par la lésion du lemnisque médian. : Il est souvent accompagné d'un syndrome de Wallenberg. ====Les syndromes pontiques==== Les syndromes de cette section ont pour origine une atteinte du métencéphale (le pont de Varole). Ils sont assez nombreux, aussi nous allons nous concentrer sur les principaux. Pour la plupart, ils ont pour symptôme une paralysie ou du moins une parésie, controlatérale. Sa cause est une lésion du faisceau pyramidal, qui traverse le pont de Varole. Précisons d'ailleurs que tout syndrome du tronc cérébral peut entraîner une paralysie par section du faisceau pyramidal, et pas seulement les syndromes pontiques. Même chose pour les autres faisceaux spino-thalamique et spino-corticaux, ce qui peut entraîner une anesthésie tactile et/ou thermoalgique, controlatérale pour le corps, mais ipsilatérale pour le visage. Un syndrome de Horner est aussi possible, vu que les fibres sympathiques parcourent le pont de Varole. La différence entre les syndromes pontiques se fait sur les noyaux des nerfs crâniens touchés par la lésion. Le '''syndrome de Millard-Gubler ''', aussi appelé syndrome du pont ventral, est causé par une lésion des noyaux des nerfs crâniens moteurs 6 et 7, en plus de la lésion du faisceau pyramidal. Le syndrome pyramidal se couple donc avec d'autres symptômes moteurs. La lésion du noyau 6 entraîne une paralysie oculaire pour les mouvements latéraux, une diplopie et un strabisme. La lésion du noyau 7 cause une paralysie facile ipsilatérale. ====Les syndromes mésencéphaliques==== Les syndromes précédents surviennent suite à des lésions dans le pont de Varole ou la moelle allongée, éventuellement accompagnées de lésions au cervelet. Dans cette section, nous allons aborder les syndromes causés par une lésion du mésencéphale. Nous les voyons dans une même section, car ils sont tous très ressemblants et qu'ils partagent des symptômes communs, au point qu'il est très difficile de les distinguer. Il s'agit des syndromes de Claude, Benedict et Weber. Les autres syndromes mésencéphaliques sont beaucoup plus rares que le syndrome de Weber, avec une prévalence bien plus faible. Dans tous les symptômes mésencéphaliques, on observe un même ensemble commun de symptômes, qui regroupe : * Des troubles oculomoteurs ipsilatéraux : paralysie oculaire, pupilles dilatées, chute des paupières (ptosis), etc. * Des troubles moteurs controlatéraux, qui dépendent du syndrome considéré : ataxie, tremblements ou hémiparésie/hémiplégie. Les trois syndromes considérés se traduisent par des lésions des noyaux oculomoteurs, qui entraînent les troubles oculaires mentionnés plus haut. Les symptômes moteurs dépendent du syndrome. Dans le '''syndrome de Weber''', le trouble moteur de ce syndrome est une hémiparésie/hémiplégie corporelle (les membres et éventuellement le reste du corps est paralysé), controlatérale. Il est causé par une atteinte des noyaux oculomoteurs couplée à une lésion du faisceau pyramidal, sans atteinte supplémentaire. Dans le '''syndrome de Claude''' et le '''syndrome de Benedict''', on observe des lésions du noyau rouge et du cervelet, en plus des lésions des noyaux oculomoteurs. Cela se traduit par l'apparition d'une ataxie et/ou de tremblements, complémentaires de l’hémiparésie/hémiplégie. La différence entre les trois syndromes se fait sur le symptôme moteur prédominant : hémiparésie pour le syndrome de Weber, ataxie pour le syndrome de Benedict, tremblement pour le syndrome de Benedict. {|class="wikitable" |+ Syndromes mésencéphaliques |- ! ! Syndrome de Weber ! Syndrome de Claude ! Syndrome de Benedict ! Localisation de la lésion responsable du symptôme |- ! rowspan="4" | Symptômes | colspan="3" | Troubles oculomoteurs : paralysie oculaire, ptosis, pupilles dilatées, etc. | Noyaux des nerfs crâniens oculomoteurs |- | Hémiplégie/hémiparésie controlatérale | | | Faisceau cortico-spinal (pyramidal) |- | | Ataxie | | Cervelet, noyau rouge |- | | | Tremblements | Cervelet, noyau rouge |} Dans les faits, il n'est pas rare que d'autres manifestations neurologiques s'y ajoutent. Par exemple, on peut observer un syndrome parkinsonien, résultant d'une atteinte de la substance noire, proche des autres structures lésées. Ou encore, on peut observer des problèmes linguaux, causés par une atteinte du noyau hypoglosse et du faisceau cortico-bulbaire. Si le syndrome de Weber est un très mauvais signe à court-terme, les patients qui survivent récupèrent bien dans le cas général, à condition que le syndrome de Weber soit pur (sans symptômes ajoutés). <noinclude> {{NavChapitre | book=Neurosciences | prev=Les nerfs crâniens | prevText=Les nerfs crâniens | next=Le cerveau antérieur | nextText=Le cerveau antérieur }}{{autoCat}} </noinclude> 4lgea4euxdliazpu2cv1fimekcru4xp 762839 762838 2026-04-03T15:53:18Z Mewtow 31375 /* Les syndromes et maladies du tronc cérébral */ 762839 wikitext text/x-wiki [[File:Brain sagittal section stem highlighted.svg|vignette|Tronc cérébral.]] Le tronc cérébral est une partie du cerveau qu'il est très difficile de décrire simplement. Sa complexité fait qu'il vaut mieux lui dédier un chapitre complet. Le tronc cérébral contient à la fois de la substance grise (neurones) que de la substance blanche (axones myélinisés). La substance blanche est surtout composée de faisceaux qui poursuivent les voies ascendantes/descendantes et traversent le tronc cérébral sur la majeure partie de sa longueur. La matière grise se limite à des noyaux enfouis dans la substance blanche et ne contient aucun cortex (couches de neurones). Et ces noyaux sont très nombreux. Les noyaux du tronc cérébral regroupent des noyaux moteurs, des noyaux sensoriels, des noyaux mixtes sensimoteurs, des noyaux d'association, tous les types sont représentés. D'autres noyaux ont des rôles plus larges ou mal connus, avec une influence sur la cognition, le noyau du Raphé en étant un bon exemple. Ils contiennent aussi les '''noyaux des nerfs crâniens''', qui sont tous dans le tronc cérébral. Une bonne partie de ces noyaux sera étudié en détail dans les chapitres portant sur les sens ou la motricité. Nous allons cependant survoler les principaux noyaux, en donnant si possible quelques détails sur leur fonctionnement dans ce qui suit. ==L'anatomie en largeur (coupe-section) du tronc cérébral== [[File:Coupe section du tronc cérébral.png|vignette|Coupe section du tronc cérébral.]] Dans cette section suivante, nous verrons comment s'organise le tronc cérébral sur sa largeur, en coupe-section. On s’aperçoit alors que le tronc cérébral est composé de deux à trois subdivisions selon l'étage. On distingue une '''région ventrale''', une région dorsale appelée le '''tegmentum''', et une région qui n’apparaît que dans le mésencéphale : le '''tectum'''. La région ventrale est séparée du tegmentum par l’aqueduc de Sylvius, une sorte de canal qui relie le 3ème et le 4ème ventricule (on abordera les ventricules plus tard, dans un futur chapitre). La région ventrale contient surtout des faisceaux de matière blanche, dont la plupart se prolongent dans la moelle épinière. Tel est le cas du faisceau cortico-spinal, qui transmet les commandes motrices volontaires. Le tegmentum est lui-même composé de plusieurs formations. Parmi celles-ci, on trouve la formation réticulée, un lacis de noyaux et de fibres nerveuses assez complexe, lui-même formé de trois colonnes. ===La formation réticulée=== La '''formation réticulée''' est une structure complexe, qui mélange divers noyaux et un ensemble de fibres entrelacées très difficile à décrire. La formation réticulée a la forme d'une bande grise qui court sur toute la longueur du tronc cérébral. Elle est entremêlée avec divers noyaux, dont les noyaux des nerfs crâniens. Cette formation émet des axones dans tout le cerveau, et notamment dans la totalité du télencéphale. La formation réticulée est divisée en trois colonnes : une colonne centrale qui contient les noyaux du raphé, une colonne médiane et une colonne latérale. La colonne médiane est composée de neurones de grande taille, alors que la colonne latérale est composée de neurones de petite taille. Les scientifiques ont depuis longtemps subdivisé la formation réticulée en deux systèmes fonctionnels distincts : un système réticulé ascendant/activateur qui maintient l'état d'éveil, et un système réticulé descendant. Le '''système réticulaire descendant''' a plusieurs fonctions, qui vont de la modulation de la douleur à la commande de la motricité extrapyramidale. Il est à l'origine d'un des faisceaux de la substance blanche de la moelle épinière, le faisceau réticulospinal, une sous-portion du faisceau extrapyramidal (n'hésitez pas à consulter le chapitre sur la moelle épinière pour des rappels). La formation réticulée descendante commande les muscles du maintien de la posture, ainsi que les muscles qui gèrent les mouvements fins, précis. D'autres portions de ce faisceau jouent un rôle dans la modulation de la douleur. En comparaison, le '''système réticulaire ascendant''' joue un grand rôle dans l'état de veille, l'attention, la vigilance et l'activation physiologique (l'intensité de l'état de veille). Lors de l'éveil, ces axones excitent les aires cérébrales de destination, donnant naissance à une activité électrique minimale au repos, ce qui permet de maintenir le sujet éveillé ou conscient. Lors du sommeil, cette formation diminue son activité, mais ne l'annule pas totalement. Une lésion de cette structure peut causer un coma irréversible, alors que sa stimulation induit un éveil soutenu. Outre son rôle dans la vigilance et l'éveil, elle influence aussi la perception de la douleur ainsi que la motricité. Nous reparlerons plus loin de ces deux systèmes, dans les chapitres sur la motricité extrapyramidale et le sommeil. ====La colonne centrale : les noyaux du Raphé==== Les '''noyaux du Raphé''' sont des noyaux regroupés dans la colonne centrale de la formation réticulée, cette colonne contenant plusieurs noyaux. Suivant les nomenclatures, le nombre de noyaux du Raphé varient, allant de seulement deux à trois noyaux à plus de neufs noyaux notés B1, B2, ... , B9. Certains de ces noyaux ont un nom plus commun : le noyau obscurus, le noyau pallidus, le noyau magnus, le noyau pontin, le noyau central supérieur et le noyau dorsal. Les noyaux localisés dans la moelle allongée (bulbe rachidien) innervent la moelle épinière, et notamment les interneurones qui transmettent la douleur. Ces innervations permettent de contrôler les sensations douloureuses, leur intensité. Les noyaux localisés dans le reste du tronc cérébral secrètent de la sérotonine dans tout le cerveau. Les axones de ces noyaux innervent aussi bien le cervelet que le cortex ou d'autres structures cérébrales. Ces axones qui innervent le cerveau ont un effet stimulant sur celui-ci, qui maintient l'état d'éveil. Par contre, une lésion de ces centres induit une insomnie. Vu leur rôle, on devine rapidement qu'ils font partie intégrante du système réticulée ascendant. Ils sont aussi impliqués dans la régulation de l'humeur, avec un effet indirect sur l'impulsivité et l'agressivité. La raison à cela est que la sérotonine, émise en grande partie (mais pas seulement) par les noyaux du raphé, a un rôle prédominant dans la régulation de l'humeur. [[File:Serotonergic neurons.svg|centre|vignette|upright=1.5|Cette image montre les axones émis par les noyaux du Raphé. On voit que ceux-ci innervent la quasi-totalité du télencéphale, ainsi que le cervelet et certaines parties du diencéphale.]] ===Les noyaux des nerfs crâniens=== [[File:Gray697.png|vignette|Nerfs crâniens moteurs.]] [[File:Gray698.png|vignette|Nerfs crâniens sensoriels.]] Le tronc cérébral contient aussi les noyaux des nerfs crâniens, à l'exception des nerfs olfactifs et optiques. Les nerfs optiques et olfactifs forment un groupe à part, vu qu'ils sont dans le cerveau antérieur (le diencéphale, pour être précis) et qu'ils font partie du système nerveux central. Les noyaux des nerfs crâniens sont classés en plusieurs groupes, selon leur origine embryonnaire/évolutive. Les nerfs hypoglosse et vestibulo-cochléaire sont chacun une catégorie à part. Le reste sépare les nerfs oculomoteur et branchiaux. Les cinq '''nerfs branchiaux''' s’appellent ainsi car ils sont l'équivalent modernes des nerfs qui innervent les branchies des poissons. {|class="wikitable" |+ Nerfs crâniens du système nerveux périphérique |- !rowspan="3"| Nerfs oculo-moteurs |Nerf oculo-moteur commun (III) |- |Nerf trochléaire ou pathétique (IV) |- |Nerf oculo-moteur externe ou abducens (VI) |- !rowspan="5"| Nerfs branchiaux |Nerf trijumeau (V) |- |Nerf facial (VII) |- |Nerf glosso-pharyngien (IX) |- |Nerf vague ou pneumogastrique (X) |- |Nerf spinal ou accessoire (XI) |- !colspan="2"| Nerf hypoglosse (XII) |- !colspan="2"| Nerf vestibulo-cochléaire ou auditif (VIII) |} [[File:Brain stem sagittal.svg|centre|vignette|upright=2|Section du tronc cérébral avec la position des noyaux des nerfs crâniens.]] Les noyaux des nerfs crâniens sont regroupés en plusieurs colonnes de noyaux alignés sur la longueur du tronc cérébral. De plus, les colonnes de noyau sont spécialisées, avec des colonnes composées uniquement de noyaux moteurs, d'autres uniquement de noyaux sensoriels, une autre uniquement de noyaux parasympathiques, etc. Cette organisation dérive de l'évolution embryonnaire du cerveau. Le système nerveux embryonnaire démarre sous la forme d'un tube qui ressemble à une ébauche de moelle épinière : le tube neural. Par la suite, le tube neural se scinde en plusieurs colonnes, qui elles-mêmes se subdivisent en noyaux sur leur longueur. Une fois formés, les noyaux des nerfs crâniens ne migrent pas et restent en place. Leur position conserve donc l'organisation en colonnes sur une bonne partie du tronc cérébral. [[File:Cranial Nerves Apparent Orrigins.jpg|centre|vignette|upright=1.5|Origines apparentes des nerfs crâniens.]] Il existe en tout 6 colonnes de noyaux pour les nerfs crâniens. Il faut faire la différence entre les colonnes motrices et les colonnes sensorielles. Il y a aussi une différence entre les colonnes somatiques et viscérales. Les colonnes somatiques sont pour la motricité et la perception consciente, alors que les colonnes viscérales s'occupent de la motricité/sensibilité inconsciente. En tout, cela fait quatre colonnes, dont deux sont en doubles. Les colonnes motrices regroupent trois colonnes, une colonne somatique, une colonne viscérale non-spécialisée, et une colonne parasympathique. {|class="wikitable" |+ Colonnes motrices |- ! Colonne motrice somatique GSE ! Colonne motrice parasympathique GVE ! Colonne motrice viscérale SVE |- | Noyaux oculomoteurs : * Noyau oculomoteur * Noyau trochléaire * Noyau abducens | Noyaux des nerfs brachiaux : * Noyau moteur du nerf trigéminal * Noyau moteur du nerf facial * Noyau ambigu (nerfs glossopharyngéal, vague et accessoire) | Noyau oculomoteur accessoire d'Edinger-Westphal |- | Noyau hypoglosse || || Noyau salivaire |- | || || Noyau dorsal du nerf vague |} Les colonnes sensorielles sont elles aussi au nombre de trois. {|class="wikitable" |+ Colonnes sensorielles |- ! Colonne sensorielle somatique générale ! Colonne sensorielle somatique spéciale ! Colonne sensorielle viscérale |- | rowspan="3" | Nerfs du nerf trigéminal : * Noyau sensoriel principal du nerf trigéminal * Noyau spinal du nerf trigéminal * Noyau mésencéphalique du nerf trigéminal | rowspan="3" | Noyaux du nerf cochléo-vestibulaire * Noyau cochléaire * Noyau vestibulaire | Noyau du tractus solitaire |- | Noyau dorsal du nerf vague |- | Noyau gustatif |} ==L'anatomie en longueur du tronc cérébral== Le cerveau est subdivisé en cinq grandes aires : le myélencéphale, le métencéphale, le mésencéphale, le diencéphale et le télencéphale. Cette subdivision se développe d'abord dans l'embryon, où ces 5 subdivisons arrivent en premier, avant de se subdiviser en aires cérébrales plus petites. Le tronc cérébral regroupe myélencéphale, métencéphale et mésencéphale, tandis que le reste du cerveau contient le diencéphale et le télencéphale. On a ainsi une première subdivision du tronc cérébral en trois étages, sur sa longueur. * Le myélencéphale, aussi appelé le '''bulbe rachidien''', ou encore la moelle allongée. Comme pour la moelle épinière, la matière grise est localisée au centre de la moelle allongée, la substance blanche entourant celle-ci. La différence avec la moelle épinière tient dans la présence de quelques noyaux, sources de nerfs crâniens divers. * Le '''pont de Varole''', ou métencéphale, se situe en dessous du cervelet. Il ressemble à une sorte de renflement sur la face ventrale du tronc cérébral. Il sert de relai aux axones moteurs et sensoriels et prend en charge certaines fonctions autonomes. De nombreux noyaux de nerfs crâniens sont présents dans le pont, comme les noyaux des nerfs vestibulaires, salivaires, etc. * Le '''mésencéphale''' est une structure beaucoup plus complexe, qui surmonte toutes les autres. [[File:Tronc-cerebral-ventrale.jpg|centre|vignette|upright=2.0|Tronc cérébral.]] Dans ce qui va suivre, nous ne reparlerons pas des noyaux de nerfs crâniens ou de la formation réticulée, mais verrons les noyaux qui n'appartiennent pas à ces structures. ===Le myélencéphale=== C'est dans la moelle allongée qu'on trouve les noyaux des nerfs crâniens qui transmettent la sensibilité du visage et en gèrent la motricité. * Le premier est le '''noyau ambigu''' contrôle la mastication et a un rôle dans la parole. * Le second est le '''noyau salivaire inférieur''' contrôle la salivation par les glandes salivaires. * Et enfin, on y trouve aussi le '''noyau du nerf vague'''. Le myélencéphale contient aussi l''''olive bulbaire''', composée d'une olive inférieure et supérieure : l'olive supérieure a un rôle de traitement des informations auditives alors que l'olive inférieure reçoit des informations via des axones en provenance du cortex moteur et envoie des axones dans le métencéphale. Les autres noyaux de la moelle allongée sont surtout ceux du système nerveux périphérique autonome, qu'il soit sympathique ou parasympathique. Ils gèrent ainsi la pression sanguine, le rythme cardiaque, la respiration, ainsi que les réflexes de salivation, vomissement, mastication et autres. On y trouve le '''centre respiratoire''', un noyau qui s'occupe de l'inspiration et de l'expiration. Ce centre reçoit des informations comme le PH et le taux de dioxyde de carbone dissous du sang, histoire d'augmenter la fréquence de respiration suivant les besoins. Le '''noyau du tractus solitaire''' est impliqué dans le gout, la régulation cardiovasculaire et la sensibilité digestive. Il forme une colonne de neurones anatomiquement unie, que son organisation fonctionnelle segmente en trois parties. Il est segmenté en un segment gustatif spécialisé pour le gout, un centre pour le vomissement et un segment de neurones dédiés à la régulation cardiovasculaire et respiratoire. Il émet des axones regroupés dans un faisceau appelé le '''faisceau solitaire''', qui fait synapse avec un grand nombre d'aires cérébrales : thalamus, hypothalamus, formation réticulée, locus coeruleus, noyaux du Raphé, noyaux parasympathiques, quelques nerfs crâniens, etc. Il reçoit des afférences en provenance du nerf vague, du nerf facial et du nerf glossopharyngien. Ces afférences ne se mélangent pas et atterrissent dans des segments séparés. * Le nerf facial et le nerf glossopharyngien lui envoient les sensation du gout, des perceptions captées par la langue. Elles atterrissent dans un noyau spécialisé : le ''noyau gustatif''. * Le nerf vague transmet des sensations corporelles inconscientes, comme l'état du tube digestif, la teneur en oxygène du sang, la pression artérielle, etc. Elles proviennent de récepteurs sensoriels dispersés dans le tube digestif, de capteurs respiratoires et cardiaques dispersés dans l'organisme, ainsi que de la langue. Pour le nerf vague, les axones sont triés selon qu'ils viennent du tube digestif ou du reste. Les afférences provenant du tube digestif font synapse avec plusieurs noyaux responsables de l'innervation parasympathique du tube digestif, ainsi qu'avec le ''centre du vomissement'', dont le nom parle de lui-même. Les afférences provenant des capteurs respiratoires et cardiaques perçoivent la teneur en CO2 ou en O2 du sang, son pH, la pression sanguine, etc. Ils sont surtout localisés dans l'aorte et les carotides, mais on peut parfois en trouver ailleurs (dans les méninges, par exemple). Ils font synapses sur une colonne de noyaux qui s'occupent de réguler la respiration et le rythme cardiaque, ainsi que la pression sanguine. Ces noyaux sont donc un centre de régulation cardiovasculaire cérébral. Ils communiquent avec d'autres noyaux impliqués dans la respiration, avec le nerf vague, et bien d'autres encore. ===Le métencéphale=== Le pont de Varole contient divers noyaux, dont le plus connu est manifestement le '''locus coerulus'''. Il s'agit d'un noyau qui projette des axones à noradrénaline et à acétylcholine dans tous le cerveau. Fait étrange, les neurones de ce noyau sont remplis d'une substance de couleur noire, la mélanine. Sa présence en faible quantité fait que le locus coerulus a une belle couleur bleue, qui lui a donné son nom (noyau coloré en latin). L'activité de ce noyau est liée à l'attention et la vigilance. Ce noyau subit des pointes d'activité transitoires lors de la présentation de stimulus salients. Il est aussi impliqué dans les réponses au stress ou à la peur. La stimulation de ce noyau a tendance à entrainer de l'anxiété. Les anxiolytiques diminuent l'activité cérébrale dans ce noyau, de même que les antidépresseurs noradrénergiques. Il est totalement inactivé dans certaines phases du sommeil, le sommeil paradoxal pour être précis. Les '''noyaux pédiculopontiques''' sont un ensemble de noyaux qui émettent de l'acétylcholine dans l'ensemble du cerveau. Ils sont impliqués dans diverses fonctions, la principale étant l'activité cholinergique liée à l'éveil ou au sommeil paradoxal. Enfin, on trouve aussi d'autres noyaux qui communiquent avec le cervelet : les '''pédoncules cérébrelleux'''. Nous verrons ceux-ci plus en détail dans le chapitre sur le cervelet. ===Le mésencéphale=== On a vu plus haut que le tronc cérébral est traditionnellement divisé en une région ventrale et un tegmentum. Au niveau du mésencéphale, il faut y ajouter une troisième subdivision : le '''tectum'''. Ce dernier n'est présent que dans le mésencéphale, là où le tegmentum est présent dans toute la longueur du tronc cérébral. [[File:Cn3nucleus-fr.svg|centre|vignette|upright=1.5|Schéma simplifié du mésencéphale.]] Le tectum prend en charge un certain nombre de réflexes oculaires et auditifs, comme les réflexes liés à la pupille ou aux muscles du tympan. Le reste du mésencéphale est appelé l'ensemble des pédoncules cérébraux. Il est découpé en deux parties : le '''colliculus inférieur''' et le '''colliculus supérieur'''. Le premier fait partie du système auditif, avec de nombreuses autres aires cérébrales. Sa fonction, très spécialisée, est de localiser la source d'un son, sa position dans l'espace. Le colliculus supérieur a une fonction totalement différente : il ne fait même pas partie du système auditif, mais appartient aux systèmes visuel et moteur. Sa fonction principale est de diriger le regard vers les objets à regarder. Pour cela, il commande les saccades oculaires (les mouvements des yeux). Le tegmentum et la région ventrale sont séparés par une structure de couleur noire en forme de bandes, appelée la substance noire. La '''substance noire''' est un amas de neurones dopaminergiques impliqué dans la motricité, connue pour être l'aire dont la dégénérescence cause la maladie de Parkinson. Nous verrons cette aire plus en détail dans le chapitre sur les ganglions de la base. La région ventrale est appelée le pied, le crus cérébral, ou encore le '''crux cerebis'''. Elle est surtout composés de faisceaux qui prennent naissance dans le cortex cérébral, les plus importants étant les faisceaux moteurs (pyramidaux). Le tegmentum regroupe divers noyaux. Parmi ceux-ci, il nous faut citer le '''noyau rouge''', appelé ainsi à cause de sa couleur légèrement rougeâtre, qui vient de la présence de pigments ferriques dans ses neurones. Ce noyau reçoit des axones provenant du cervelet et des aires motrices du cortex cérébral. Il émet des axones qui se rassemblent pour former un des faisceaux de la moelle épinière : le faisceau rubro-spinal de la substance blanche, appartenant à la voie extra-pyramidale. Le noyau rouge commande les muscles de la posture et joue donc un rôle certain dans le maintien de l'équilibre. Chez les vertébrés "peu évolués", le noyau rouge est la seule aire cérébrale qui prend en charge l'équilibre et le maintien de la posture. Mais chez les animaux "plus évolués", ce rôle est maintenant dévolu principalement au cervelet, le noyau rouge ayant un rôle mineur et/ou plus spécialisé. [[File:Mesencephalus colliculus sup.jpg|centre|vignette|upright=2.5|Schéma plus détaillé du mésencéphale.]] Il faut aussi parler d'un noyau de neurones dopaminergiques qui gère le plaisir suite à une récompense : l''''aire tegmentale ventrale'''. Elle contient de nombreux neurones dopaminergiques qui envoient des axones dans tout le cerveau. Elle est impliquée dans la motivation et la cognition. Elle jouerait un rôle dans les addictions, la dépression, et potentiellement dans les troubles bipolaires et la schizophrénie. ==Les syndromes et maladies du tronc cérébral== Le tronc cérébral regroupe des noyaux aux fonctions très variées. Ce n'est donc pas étonnant qu'une lésion dans le tronc cérébral aura des conséquences très variables, qui mêlent symptômes moteurs, sensoriels, végétatifs, et autres. En théorie, les fonctions cognitives et intellectuelles sont relativement épargnées après une lésion du tronc cérébral. Mais on ne peut en dire autant du reste. Précisons que les syndromes du tronc cérébral sont très nombreux, et qu'il n'est pas pertinent de tous les aborder. Raison pour laquelle, dans cette section, nous allons voir quels sont les syndromes les plus communs que l'on peut observer après une lésion du tronc cérébral. Il est rare qu'un noyau ou un faisceau soit atteint de manière isolée, sans que les autres structures du tronc soient touchées. Dans la quasi-totalité des cas, plusieurs noyaux et/ou faisceaux sont touchés lors d'un AVC ou d'un traumatisme. En général, les lésions sont causées par un AVC, de type ischémique, ce qui fait que c'est le territoire de perfusion d'une artère qui est lésé. Les déficits obtenus sont la somme des syndromes associés aux noyaux et faisceaux perfusés par l'artère. ===L'atteinte isolée d'un noyau/faisceau du tronc cérébral=== Les symptômes qui font suite à une lésion du tronc cérébral peuvent se déduire assez facilement quand on sait ce que contient le tronc cérébral : les noyaux des nerfs crâniens, les faisceaux ascendants/descendants, et la formation réticulée. Ces trois structures peuvent être atteintes suite à une lésion, donnant des symptômes divers. {|class="wikitable" |+Symptômes d'une atteinte du tronc cérébral |- !Noyaux des nerfs crâniens |Symptômes dépendants du noyau touché. * Vertiges (noyaux cochléovestibulaire). * Troubles des mouvements oculaires (noyau du nerf oculomoteur commun, noyau du nerf pathétique, noyau du nerf oculomoteur externe). * Déviation de la langue, paralysie linguale (noyau du nerf hypoglosse). * Perte de la sensibilité et de la motricité du visage (noyau du nerf trijumeau ou du nerf facial, ...). * Dysarthrie (difficultés à articuler), dysphagie (difficultés pour avaler). * Diplopie, cécité, troubles visuels. * ... |- !Faisceaux ascendants | * Perte de la sensibilité du corps (sur une moitié du corps pour une lésion unilatérale, sur la totalité du corps pour une lésion bilatérale). |- !Faisceaux descendants | * Paralysie : hémiplégie, quadriplégie * Faiblesse musculaire, dans les cas les moins graves. * Ataxie, dans les cas les moins graves. |- !Formation réticulée | * Troubles de la vigilance/conscience * Perturbations motrices : paralysie, hémiplégie, ... |} Une lésion des noyaux de nerfs crâniens peut causer des troubles de la motricité et de la sensibilité du visage, alors que les lésions des faisceaux tendent à obérer la motricité/sensibilité du corps. Avec les lésions unilatérales (d'un côté du tronc), la perte de sensibilité/motricité ne touche qu'un seul côté du visage/corps, donnant une hémiplégie/hémiparésie ou la perte de la sensibilité sur une moitié du corps/visage. Les lésions de la formation réticulée donnent des troubles de la conscience, pouvant aller jusqu’au coma, parfois accompagnés de troubles moteurs caractéristiques. ===Les syndromes alternes du tronc cérébral=== Les lésions unilatérales du tronc cérébral causent une lésion de plusieurs noyaux de nerfs crâniens, couplée à la lésion d'un faisceau ascendant/descendant. Elles ont une symptomatologie assez particulière au niveau sensitif/moteur, qui leur vaut le nom de '''syndromes alternes'''. Elles se caractérisent par au moins un des deux symptômes suivants : * Une perte des sensations controlatérale pour le corps, mais ipsilatérale pour le visage. * Une perte de la motricité (paralysie, faiblesse musculaire) controlatérale pour le corps, mais ipsilatérale pour le visage. Comme on le voit, le côté du corps touché n'est pas le même que le côté du visage atteint. Cela s'explique par les faits suivants. C'est dans le tronc cérébral que les faisceaux/nerfs changent de côté pour innerver le corps. Par exemple, les fibres sensorielles ascendantes provenant du côté droit du corps vont passer du côté gauche, et réciproquement (même chose pour les fibres motrices). Par contre, ce n'est pas le cas pour les nerfs crâniens, qui restent du même côté du corps. Toute lésion unilatérale (d'un côté) du tronc cérébral va donc léser le faisceau innervant la moitié du corps, alors que les noyaux des nerfs crâniens innervent le visage de l'autre côté. ====Le syndrome latéral médullaire (de Wallenberg)==== [[File:Stickerman Wallenberg.png|vignette|Syndrome de Wallenberg.]] Le syndrome alterne le plus fréquemment rencontré par les neurologues est le '''syndrome de Wallenberg'''. Il apparaît quand un AVC a lieu dans l'artère cérébelleuse inféro-postérieure, qui alimente en sang le cervelet et la moelle allongée, ou dans une artère proche. Les noyaux et faisceaux suivants sont touchés : le faisceau spino-thalamique, les faisceaux sympathiques, le noyau vestibulaire, le cervelet, et quelques autres noyaux. En général, le syndrome n'est pas complet chez la plupart des patients, qui ne montrent qu'une partie des symptômes. Mais sa présentation classique est la suivante : {|class="wikitable" |- ! colspan="2" | Du côté de la lésion (côté ipsilatéral) ! colspan="2" | Du côté opposé à la lésion (côté controlatéral) |- !Symptôme !Localisation de la lésion (ipsilatérale) !Symptôme !Localisation de la lésion (controlatérale) |- |Syndrome de Claude-Bernard-Horner. |Fibres sympathiques |- |Syndrome vestibulaire (vertiges, perte de l'équilibre, chutes, nausées, vomissements). Syndrome cérébelleux (ataxie, nystagmus, pertes d'équilibre, vertiges, ...). |Noyau vestibulaire Cervelet |- |Paralysie de l'hémi-voile, l'hémi-pharynx et de la corde vocale, qui entraîne dysphagie, dysphonie et dysarthrie. |Noyau ambigu |- |Anesthésie thermoalgique (perte de la douleur et des sensations de chaleur/froid) sur le visage. |Noyau du trijumeau | rowspan="5" | Anesthésie thermoalgique (perte de la douleur et des sensations de chaleur/froid) sur le corps. | rowspan="5" | Faisceau spino-thalamique. |} : Parfois, ce syndrome se complète d'une hémiplégie/hémiparésie controlatérale. On parle alors d'un '''syndrome de Babinski-Nageotte'''. Plus rarement on peut observer, du côté de la lésion : * Des douleurs dans le visage, avec une perte de sensibilité facile - atteinte du noyau du trijumeau. * Une réduction du goût, voir une agueusie (perte totale du goût) - atteinte du noyau gustatif. * Une paralysie de la langue - atteinte du noyau hypoglosse. ====Le syndrome médullaire médian (de Dejerine)==== Le '''syndrome de Dejerine ''' se caractérise par une lésion du noyau hypoglosse, du faisceau pyramidal, et du lemnisque médian (faisceau qui suit les colonnes dorsales de la moelle épinière). Il se traduit par : * une déviation de la langue du côté de la lésion, causée par la lésion du noyau hypoglosse ; * une faiblesse musculaire/paralysie du côté controlatéral à la lésion, causée par la lésion du faisceau pyramidal ; * et enfin une perte des sensations tactiles sur le côté controlatéral du corps, causé par la lésion du lemnisque médian. : Il est souvent accompagné d'un syndrome de Wallenberg. ====Les syndromes pontiques==== Les syndromes de cette section ont pour origine une atteinte du métencéphale (le pont de Varole). Ils sont assez nombreux, aussi nous allons nous concentrer sur les principaux. Pour la plupart, ils ont pour symptôme une paralysie ou du moins une parésie, controlatérale. Sa cause est une lésion du faisceau pyramidal, qui traverse le pont de Varole. Précisons d'ailleurs que tout syndrome du tronc cérébral peut entraîner une paralysie par section du faisceau pyramidal, et pas seulement les syndromes pontiques. Même chose pour les autres faisceaux spino-thalamique et spino-corticaux, ce qui peut entraîner une anesthésie tactile et/ou thermoalgique, controlatérale pour le corps, mais ipsilatérale pour le visage. Un syndrome de Horner est aussi possible, vu que les fibres sympathiques parcourent le pont de Varole. La différence entre les syndromes pontiques se fait sur les noyaux des nerfs crâniens touchés par la lésion. Le '''syndrome de Millard-Gubler ''', aussi appelé syndrome du pont ventral, est causé par une lésion des noyaux des nerfs crâniens moteurs 6 et 7, en plus de la lésion du faisceau pyramidal. Le syndrome pyramidal se couple donc avec d'autres symptômes moteurs. La lésion du noyau 6 entraîne une paralysie oculaire pour les mouvements latéraux, une diplopie et un strabisme. La lésion du noyau 7 cause une paralysie facile ipsilatérale. ====Les syndromes mésencéphaliques==== Les syndromes précédents surviennent suite à des lésions dans le pont de Varole ou la moelle allongée, éventuellement accompagnées de lésions au cervelet. Dans cette section, nous allons aborder les syndromes causés par une lésion du mésencéphale. Nous les voyons dans une même section, car ils sont tous très ressemblants et qu'ils partagent des symptômes communs, au point qu'il est très difficile de les distinguer. Il s'agit des syndromes de Claude, Benedict et Weber. Les autres syndromes mésencéphaliques sont beaucoup plus rares que le syndrome de Weber, avec une prévalence bien plus faible. Dans tous les symptômes mésencéphaliques, on observe un même ensemble commun de symptômes, qui regroupe : * Des troubles oculomoteurs ipsilatéraux : paralysie oculaire, pupilles dilatées, chute des paupières (ptosis), etc. * Des troubles moteurs controlatéraux, qui dépendent du syndrome considéré : ataxie, tremblements ou hémiparésie/hémiplégie. Les trois syndromes considérés se traduisent par des lésions des noyaux oculomoteurs, qui entraînent les troubles oculaires mentionnés plus haut. Les symptômes moteurs dépendent du syndrome. Dans le '''syndrome de Weber''', le trouble moteur de ce syndrome est une hémiparésie/hémiplégie corporelle (les membres et éventuellement le reste du corps est paralysé), controlatérale. Il est causé par une atteinte des noyaux oculomoteurs couplée à une lésion du faisceau pyramidal, sans atteinte supplémentaire. Dans le '''syndrome de Claude''' et le '''syndrome de Benedict''', on observe des lésions du noyau rouge et du cervelet, en plus des lésions des noyaux oculomoteurs. Cela se traduit par l'apparition d'une ataxie et/ou de tremblements, complémentaires de l’hémiparésie/hémiplégie. La différence entre les trois syndromes se fait sur le symptôme moteur prédominant : hémiparésie pour le syndrome de Weber, ataxie pour le syndrome de Benedict, tremblement pour le syndrome de Benedict. {|class="wikitable" |+ Syndromes mésencéphaliques |- ! ! Syndrome de Weber ! Syndrome de Claude ! Syndrome de Benedict ! Localisation de la lésion responsable du symptôme |- ! rowspan="4" | Symptômes | colspan="3" | Troubles oculomoteurs : paralysie oculaire, ptosis, pupilles dilatées, etc. | Noyaux des nerfs crâniens oculomoteurs |- | Hémiplégie/hémiparésie controlatérale | | | Faisceau cortico-spinal (pyramidal) |- | | Ataxie | | Cervelet, noyau rouge |- | | | Tremblements | Cervelet, noyau rouge |} Dans les faits, il n'est pas rare que d'autres manifestations neurologiques s'y ajoutent. Par exemple, on peut observer un syndrome parkinsonien, résultant d'une atteinte de la substance noire, proche des autres structures lésées. Ou encore, on peut observer des problèmes linguaux, causés par une atteinte du noyau hypoglosse et du faisceau cortico-bulbaire. Si le syndrome de Weber est un très mauvais signe à court-terme, les patients qui survivent récupèrent bien dans le cas général, à condition que le syndrome de Weber soit pur (sans symptômes ajoutés). <noinclude> {{NavChapitre | book=Neurosciences | prev=Les nerfs crâniens | prevText=Les nerfs crâniens | next=Le cerveau antérieur | nextText=Le cerveau antérieur }}{{autoCat}} </noinclude> m90d2q1f15zxlx8960510fs460mtu6n Électricité/Les circuits linéaires et leurs lois 0 70996 762840 703441 2026-04-03T15:54:38Z ~2026-20668-34 123412 /* Le théorème de Kennelly et les transformations étoile-mesh */ 762840 wikitext text/x-wiki <noinclude>{{Électricité}}</noinclude> Les lois de Kirchhoff permettent de démontrer d'autres lois très utiles pour analyser les circuits électriques. Parmi celles-ci, nous allons surtout étudier les lois qui valent pour ce qu'on appelle les '''circuits linéaires''', aussi appelés circuits résistifs. Pour simplifier, un circuit linéaire ne contient que des générateurs parfaits et des résistances. La définition plus formelle est qu'un circuit linéaire ne contient que des dipôles dits linéaires. Ceux-ci sont des composants pour lesquels la relation entre tension et intensité à leurs bornes est affine (et non pas linéaire). Pour le moment, les composants linéaires que nous connaissons se limitent aux résistances et aux générateurs parfaits combinés à des résistances (générateurs réels vus précédemment). ==Le principe de superposition== Le principe de superposition est de loin le plus simple à comprendre et à appliquer. Celui-ci s'applique pour les circuits linéaires, qui comprennent plusieurs générateurs. On pourrait croire que leur étude est très compliquée, mais le théorème de superposition simplifie grandement les calculs. Ce principe permet de calculer les tensions, courants et potentiels en un point avec une méthode très simple : il suffit de faire la somme des résultats obtenus en ne prenant en compte qu'un seul générateur. Pour un circuit avec N générateurs, on doit donc calculer N tensions différentes, avant d'en faire la somme. Chaque tension se calcul en ne gardant qu'un seul générateur, différent pour chaque tension. Les générateurs à ne pas prendre en compte sont simplement remplacés par un fil. ===Un premier exemple d'application=== [[Fichier:Superposition et circuit électrique.svg|vignette|Illustration du principe de superposition.]] Prenons par exemple un circuit avec deux générateurs, comme le circuit à votre droite, noté (a). Celui-ci contient deux générateurs, reliés aux bornes de deux résistances en série. On connaît la tension aux bornes des générateurs, ainsi que les valeurs des deux résistances. On souhaite connaître le potentiel au milieu du circuit, entre les deux résistances, que nous noterons <math>P</math>. Le théorème de superposition nous dit de calculer le potentiel deux fois : une première fois en ne prenant en compte que le premier générateur, et la seconde en ne gardant que le second. Ces deux étapes sont illustrées par les deux circuits notés(b) et (c). La première étape est illustrée par le circuit noté (b). Seul le second générateur est conservé, l'autre étant remplacé par un court-circuit. On peut alors calculer la tension P en appliquant un diviseur de tension. : <math>P_2 = V_2 \cdot \frac{R_1}{R_1 + R_2} = - 2,77 V</math> La seconde étape est illustrée par le circuit noté (c). Seul le premier générateur est conservé, l'autre étant remplacé par un court-circuit. On peut encore une fois calculer la tension P en appliquant un diviseur de tension. :<math>P_1 = V_1 \cdot \frac{R_2}{R_1 + R_2} = 8,88 V</math> On peut alors calculer la valeur du potentiel total en faisant la somme des deux potentiels précédents. On obtient : :<math>P = P_2 + P_1 = 8,88 V - 2,77 V = 6,11 V</math> ===Un cas particulier : la loi de Pouillet=== Quand un circuit ne contient qu'une seule maille, le principe de superposition permet de simplifier le circuit en réduisant drastiquement le nombre de générateurs et de résistances. Précisément, on peut fusionner tous les générateurs en un seul, dont la tension est la somme de celles des générateurs d'origine. Même chose pour les résistances, que l'on peut fusionner en une seule résistance, dont la valeur est la somme des résistances d'origine. Il s'agit d'une application des lois concernant les générateurs en série et les résistances en série. On peut alors utiliser ce résultat pour calculer le courant qui circule dans la maille, en divisant la tension fusionnée par la résistance équivalente. ==Le théorème de Millman== Le '''théorème de Millman''' est une reformulation de la loi des nœuds, écrite avec des potentiels et des résistances. Prenons un nœud sur lequel convergent/divergent plusieurs branches. La loi des nœuds dit que la somme des courants de chaque branche reliée au nœud est nulle : : <math>0 = I_0 + I_1 + I_2 + \cdot \cdot \cdot + I_n</math> Supposons que dans chaque branche numéro <math>i</math>, on trouve une résistance <math>R_i</math>. Il doit y avoir une tension <math>U_i</math> aux bornes de la branche. D'après la loi d'Ohm, on a : : <math>0 = \frac{U_0}{R_0} + \frac{U_1}{R_1} + \frac{U_2}{R_2} + \cdot \cdot \cdot + \frac{U_n}{R_n}</math> Chaque tension est la différence entre le potentiel au niveau du nœud <math>V_M</math> et les potentiels aux autres extrémités des branches <math>V_i</math>. On a donc : : <math>0 = \frac{V_0 - V_M}{R_0} + \frac{V_1 - V_M}{R_1} + \frac{V_2 - V_M}{R_2} + \cdot \cdot \cdot + \frac{V_n - V_M}{R_n}</math> Développons : : <math>0 = \frac{V_0}{R_0} - \frac{V_M}{R_0} + \frac{V_1}{R_1} - \frac{V_M}{R_1} + \frac{V_2}{R_2} - \frac{V_M}{R_2} + \cdot \cdot \cdot + \frac{V_n}{R_n} - \frac{V_M}{R_n}</math> : <math>\frac{V_0}{R_0} + \frac{V_1}{R_1} + \frac{V_2}{R_2} + \cdot \cdot \cdot + \frac{V_n}{R_n} = \frac{V_M}{R_0} + \frac{V_M}{R_1} + \frac{V_M}{R_2} + \cdot \cdot \cdot + \frac{V_M}{R_n}</math> : <math>\frac{V_0}{R_0} + \frac{V_1}{R_1} + \frac{V_2}{R_2} + \cdot \cdot \cdot + \frac{V_n}{R_n} = V_M \cdot \left( \frac{1}{R_0} + \frac{1}{R_1} + \frac{1}{R_2} + \cdot \cdot \cdot + \frac{1}{R_n} \right)</math> On peut alors calculer le potentiel au niveau du nœud : : <math>V_M = \frac{\frac{V_0}{R_0} + \frac{V_1}{R_1} + \frac{V_2}{R_2} + \cdot \cdot \cdot + \frac{V_n}{R_n}}{\frac{1}{R_0} + \frac{1}{R_1} + \frac{1}{R_2} + \cdot \cdot \cdot + \frac{1}{R_n}}</math> ===Loi de Millman proprement dite=== [[Fichier:Image du théorème de Millman.svg|vignette|Image du théorème de Millman]] Cela permet de calculer les tensions et courants dans les circuits similaires à celui présenté à votre droite. Il comprend plusieurs branches, chacune avec un générateur et une résistance (un récepteur linéaire, en toute généralité). Le courant qui passe dans une branche <math>M</math> est égal à la somme de tous les autres courants. : <math>I_M = \sum_i \frac{E_i}{R_i}</math> La tension s'obtient à partir du courant en multipliant par la résistance équivalente des autres branches (ou en divisant par la conductance équivalente). : <math>U_M = \sum_i \frac{E_i}{R_i} \cdot R_{eq} = \frac{\sum_i \frac{E_i}{R_i}}{G_{eq}}</math> Vu que ces branches sont en parallèle, on a : <math>\frac{1}{R_{eq}} = \sum_i \frac{1}{R_i}</math>, ou encore <math>G_{eq} = \sum_i G_i</math>, ce qui donne : : <math>U_M = \sum_i \frac{E_i}{R_i} \cdot \sum_i G_i = \frac{\sum_i \frac{E_i}{R_i}}{\sum_i \frac{1}{R_i}}</math> ===Exemple=== [[Fichier:Millman Illustration exemple.svg|vignette|Millman Illustration exemple]] Prenons le circuit à votre droite. La loi de Millman permet de calculer la tension entre les points a et b. L'application de la loi de Millman donne : : <math>V_{ab}=\frac{\dfrac{V_1}{R_1}+\dfrac{V_2}{R_2}+\dfrac{V_3}{R_3}}{\dfrac{1}{R_1}+\dfrac{1}{R_2}+\dfrac{1}{R_3}}</math> <math>=\frac{\dfrac{10}{5}+\dfrac{0}{8}+\dfrac{-12}{3}}{\dfrac{1}{5}+\dfrac{1}{8}+\dfrac{1}{3}} = -3V</math> ==Les théorèmes de Thévenin et Norton== Les théorèmes de Thévenin et de Norton, que nous allons voir dans ce qui suit, sont souvent appelés les théorèmes des générateurs. La raison est qu'ils permettent de remplacer un (morceau de) circuit linéaire par un dipôle équivalent à un générateur linéaire. Plus précisément, ils permettent de remplacer un circuit linéaire, ou une partie de circuit linéaire, par un générateur couplé avec une résistance. Cela n'est évidemment possible que si le circuit est localisé entre deux points d'un circuit, à savoir si ce (morceau de) circuit possède deux bornes. ===Le théorème de Thévenin=== [[Fichier:Thévenin theorem - illustration.svg|vignette|Exemple d'application du théorème de Thévenin. On voit que le circuit de gauche, vu entre deux points, est équivalent au circuit de droite composé d'un générateur en série avec une résistance. On peut remplacer le circuit de droite par celui de gauche, et réciproquement, sans changer la physique ou les calculs lors de l'étude du circuit.]] Le '''théorème de Thévenin''' dit qu'un (morceau de) circuit linéaire est équivalent à un générateur de tension en série avec une résistance. La tension produite par le générateur est appelé la tension de Thévenin et la résistance associée est appelée la résistance de Thévenin. La tension du générateur se calcule en prenant le circuit ouvert, alors que la résistance se calcule en remplaçant les générateurs par leur résistance interne. Pour en donner un exemple, étudions le circuit ci-dessous, noté (a) dans le schéma. Celui-ci est composé de quatre résistances et d'un générateur, avec une charge entre les points A et B. * Première étape : calculer la tension de Thévenin. Pour cela, on prend le circuit ouvert : on ne met rien entre les points A et B. On calcule alors la tension entre A et B : celle-ci est la tension de Thévenin. Cette étape est illustrée au schéma noté (b). Dans l'exemple étudié, un simple diviseur de tension suffit. Le calcul donne : : <math>U_{Thevenin} = U_{gen} \cdot \frac{R3 + R2}{R2 + R3 + R4} = 15 V \cdot \frac{1 k \Omega + 1 k \Omega}{1 k \Omega + 1 k \Omega + 2 k \Omega} = 7,5 V</math> * Seconde étape : calculer la résistance de Thévenin. Pour cela, on remplace les générateurs par leur résistance interne et on calcule la résistance équivalente du circuit. Cette étape est illustrée au schéma noté (c). On voit que R1 est en série avec un circuit composé de R4 en parallèle avec R2 et R3 (qui sont en série). Pour commencer, R2 et R3 sont en série, ce qui fait que leurs résistances s'additionnent. Puis, on doit calculer la résistance équivalente à R4 en parallèle avec R3 + R2, ce qui donne : : <math>R_{temp} = \frac{R4 \cdot (R3 + R2)}{R4 + R3 + R2} = 1 k \Omega</math> Reste à calculer la résistance équivalente totale qui est égale à la résistance précédente mise en série avec R1. l'addition donne : : <math>R_{eq} = 2 k \Omega</math> * Une fois cela fait, on peut remplacer le circuit par un générateur qui produit la tension calculée à la première étape, mis en série avec la résistance calculée à la seconde étape. [[Fichier:Thevenin demo2.svg|centre|Exemple d'application du théorème de Thévenin.]] ===Le théorème de Norton=== [[File:NortonEquivalentCircuits.png|vignette|Exemple d'application du théorème de Norton. On voit que le circuit de gauche, vu entre deux points, est équivalent au circuit de droite composé d'un générateur en parallèle avec une résistance. On peut remplacer le circuit de droite par celui de gauche, et réciproquement, sans changer la physique ou les calculs lors de l'étude du circuit.]] Le '''théorème de Norton''' dit qu'un (morceau de) circuit linéaire est équivalent à un générateur de courant en parallèle avec une résistance. Le courant produit par le générateur est appelé le courant de Norton et la résistance associée est appelée la résistance de Norton. Le courant se calcule en court-circuitant le circuit étudié, à savoir en reliant les deux bornes du (morceau de ) circuit. Par contre, la résistance se calcule en remplaçant les générateurs par leur résistance interne et en calculant la résistance équivalente du circuit obtenu. [[Fichier:Norton theorem demo.svg|vignette|gauche|Exemple d'application du théorème de Norton.]] Pour mieux comprendre, voici un exemple d'application. Nous allons étudier le circuit numéroté (a), illustré dans le schéma ci-contre, à gauche. * Première étape : calculer le courant de Norton. Pour cela, on court-circuite le circuit complet, en mettant un fil entre A et B. Il reste alors à calculer le courant qui circule dans ce fil, qui n'est autre que le courant de Norton. Dans l'exemple ci-dessous, le court-circuit donne le circuit numéro (b). On obtient alors un circuit proche du diviseur de courant, et les calculs donnent : :<math>I_{Norton} = 1,82 A</math> * Seconde étape : remplacer les générateurs par des fils et calculer la résistance équivalente entre A et B. Le circuit obtenu en remplaçant les générateurs est illustré à gauche : c'est le numéro (c). Les calculs donnent une résistance équivalente égale à 3,66 Ohms. : <math>R_{Norton} = 3,66 \Omega</math> * Enfin, on peut remplacer le circuit complet par un générateur qui produit le courant calculé à la première étape, en parallèle avec la résistance calculée à la seconde étape. ===Conversion entre Thévenin et Norton=== On a vu dans le chapitre sur les générateurs qu'il est possible de passer d'un générateur de Thévenin à un générateur de Norton assez facilement. Rappelons simplement les faits suivants : * Les deux résistances, de Norton et de Thévenin, sont identiques. En effet, elles se calculent de la même manière : ce sont toutes deux la résistance équivalente entre les points A et B, une fois les générateurs remplacés par leur résistance interne. * Le courant de Norton et la tension de Thévenin sont reliés entre eux par l'équation suivante, où <math>R_{Eq}</math> est la résistance équivalente mentionnée précédemment. : <math>I_{cc} = \frac{U_{vide}}{R_{Eq}}</math> ==Le théorème de Kennelly et les transformations étoile-''mesh''== La '''transformation étoile-''mesh''''' permet de passer d'un ensemble de résistances « en étoile » vers un ensemble « en ''mesh''». Les deux configurations sont illustrées ci-dessous. Dans la configuration en étoile, plusieurs résistances sont reliées à un point central commun. Une extrémité de chaque résistance est connectée au point central, l'autre extrémité est "libre". L'ensemble des extrémités libres forme un polygone. Dans la configuration en ''mesh'', chaque résistance relie deux sommets du polygone. [[File:Star-mesh transform.svg|centre|vignette|upright=2.0|Transformation étoile-''mesh''.]] Le passage de la configuration en étoile vers la configuration en ''mesh'' utilise l'équation suivante. Elle donne la valeur de la résistance entre deux points A et B du polygone, qui est notée <math>R_{ab}</math>. Pour cela, elle a besoin de la somme de toutes les résistances de l'étoile, notée <math>\sum R_i</math>, de la valeur de la résistance qui connecte A au centre (<math>R_a</math>) et la valeur de la résistance qui connecte B au centre (<math>R_b</math>). On a alors : : <math>R_{ab} = \frac{R_a \cdot R_b}{\sum R_i}</math> La transformation inverse, d'un ''mesh'' vers une étoile, n'est pas possible dans que l'on impose des contraintes additionnelles. Cela signifie qu'il n'y a pas de formule générale qui permette de faire les calculs. La raison est que la transformation d'une étoile vers un ''mesh'' augmente le nombre de résistance, alors que la transformation inverse le réduit. Autant calculer un grand nombre de résistances à partir d'un plus petit nombre ne pose pas de problème, autant calculer un petit nombre de résistances à partir de plus de résistances est bien moins facile. On peut deviner facilement que la transformation étoile-''mesh'' fait passer de <math>N</math> résistances à <math>N (N - 1) \over 2</math> résistances. ===Un cas particulier : le théorème de Kennelly=== L'application du théorème précédent dans le cas à trois résistances s'appelle le '''théorème de Kennelly'''. Il permet de passer d'un ensemble de résistances « en étoile » vers un ensemble « en triangle ». Dans la configuration en étoile, trois résistances sont reliées à un point central commun. Dans la configuration en triangle, elles sont reliées de manière à former un triangle. Les deux configurations sont illustrées ci-dessous. Le théorème de Kennelly est aussi appelé la transformation étoile-triangle. Précisons que dans ce cas particulier, on peut faire la conversion dans les deux sens : de l'étoile vers le ''mesh'', mais aussi du ''mesh'' vers l'étoile. C'est possible parce que, dans les deux cas, le nombre de résistances est conservé : il y a trois résistances dans les deux configurations. Pour comprendre comment passer de l'une à l'autre, nous allons utiliser les deux circuits ci-dessous, avec les résistances numérotées. [[File:Wye-delta-2.svg|centre|vignette|upright=1.5|Théorème de Kennelly.]] Le passage de la configuration en triangle vers la configuration en étoile utilise les trois équations suivantes : : <math>R_1 = \frac{R_b \cdot R_c}{R_a + R_b + R_c}</math> : <math>R_2 = \frac{R_a \cdot R_c}{R_a + R_b + R_c}</math> : <math>R_3 = \frac{R_a \cdot R_b}{R_a + R_b + R_c}</math> Le passage de la configuration en étoile vers la configuration en triangle utilise les trois équations suivantes : : <math>R_a = \frac{R_1 \cdot R_2 + R_2 \cdot R_3 + R_3 \cdot R_1}{R_1}</math> : <math>R_b = \frac{R_1 \cdot R_2 + R_2 \cdot R_3 + R_3 \cdot R_1}{R_2}</math> : <math>R_c = \frac{R_1 \cdot R_2 + R_2 \cdot R_3 + R_3 \cdot R_1}{R_3}</math> Ce théorème est utile pour simplifier certains circuits assez complexes. Deux exemples d'utilisation de ce théorème sont illustrés ci-dessous. On voit, dans ces deux exemples, que le théorème de Kennelly permet de passer d'un circuit assez complexe à un circuit plus simple à interpréter. {| |- |[[File:Wye-delta bridge simplification.svg|centre|vignette|upright=1.5|Exemple d'utilisation du théorème de Kennelly.]] |- |[[File:Delta-wye bridge simplification.svg|centre|vignette|upright=1.5|Exemple d'utilisation du théorème de Kennelly.]] |} lq6rtqg08nbqah6rcscepfuvr4c7har Neurosciences/Les nerfs et ganglions périphériques 0 81940 762833 762800 2026-04-03T13:52:58Z Mewtow 31375 /* La classification en type I, II, III et IV */ 762833 wikitext text/x-wiki Le système nerveux périphérique est la portion du système nerveux qui reste quand on retire le cerveau et la moelle épinière. Il est composé de nerfs, de petites fibres nerveuses et des ganglions (des amas de neurones, pour rappel). Il innerve la peau, les organes internes, les muscles et quelques autres structures anatomiques. Mais avant de voir le système nerveux périphérique, nous allons détailler ce qu'il y a à l'intérieur d'un nerf. ==Les nerfs périphériques== [[File:Transverse section of the optic nerve, from Leeuwenhoek, 1675 Wellcome L0001822.jpg|vignette|Section d'un nerf optique.]] Un '''nerf''' est un paquet d'axones, entourés par une pellicule externe appelée l''''épinèvre''', avec quelques vaisseaux sanguins entre les deux. Si vous regarder une coupe-section d'un nerf, vous verrez quelque chose de similaire à ce qu'il y a dans la photographie de droite. Vous vous dites sans doute que les zones blanches sont des axones, le noir étant du tissu de soutien. La partie noire regroupe du tissu de soutien, avec des gros vaisseaux sanguins pour alimenter le nerf en oxygène/nutriments. Les zones blanches sont des paquets d'axones, contenant plus d'un millier d'axones. Les paquets d'axones en question sont appelés des '''fascicules'''. Ils sont formés en entourant un paquet d'axone avec une pellicule appelée le '''périnèrve''', qui entoure le paquet d'axone. Le périnévre est composé de plusieurs couches de cellules "musculaires" (des myofibroblastes). Outre des axones, il y a aussi des vaisseaux sanguins de petite taille dans un fascicule. Les vaisseaux sanguins d'un fascicule sont des ramifications de vaisseaux sanguins plus gros, présents dans le tissu de soutien du nerf. En clair, les axones sont regroupés en fascicules, qui sont eux-même regroupés en nerfs. L'ensemble est illustré ci-contre, et les illustrations ci-dessous montrent une section d'un nerf optique. [[File:Nerf detaillé..png|centre|vignette|upright=2.0|Section d'un nerf.]] Les axones sont entourés par une pellicule protectrice, l''''endonèvre''', qui entoure la gaine de myéline des axones. L'endonèvre est composé de deux couches : une couche de glycocalyx, entourée de fibres de collagène. L'endonèvre n'est pas directement posé sur l'axone, il y a un peu de '''liquide endonévrial''' entre les deux. Il a un rôle similaire au liquide cérébrospinal, à savoir que c'est un liquide qui protège les neurones contre toute agression extérieure. Il y a aussi des vaisseaux sanguins dans l'endonèvre, et précisément des capillaires sanguins de très petite taille. Un point important est que l'endonèvre ne laisse pas passer grand chose : la plupart des molécules extérieures ne peuvent pas rentrer dans le neurone, à cause de lui. Il protège les neurones contre la présence de toxiques, mais surtout : il régule l'équilibre ionique à la surface de l'axone. Il garantit qu'il n'y a pas d'excès ou de déficiences en ions potassium, sodium, calcium, chlore et autres ; qui sont impliqués dans le potentiel de membrane. Et il ne laisse pas non plus passer les virus et bactéries, du moins pas facilement. De par sa fonction de tampon chimique et de protection immunitaire, il est l'équivalent pour les nerfs de la barrière hématoencéphalique, qui protège le cerveau contre les agressions extérieures. ==Les axones du système nerveux périphérique== Le système nerveux périphérique comprend des axones, appelés les '''fibres périphériques''', ou encore fibres. Les fibres périphériques sont à distinguer des nerfs, les différences étant qu'un nerf est un regroupement d'axone, alors que les fibres sont des axones seuls. Les axones sensoriels et moteurs ne se mélangent pas, ou rarement, dans le système nerveux périphérique. Il existe plusieurs types de fibres périphériques, qui se distinguent essentiellement par leur caractère moteur/sensoriel/autonomique, par leur vitesse de conduction, etc. Dans ce qui suit, nous allons voir les classifications principales des fibres périphériques. ===Les axones afférents et efférents=== Avant de poursuivre, parlons rapidement de la notion d'axones afférents et efférents. Les axones dits afférent partent de la périphérie et rentrent dans le système nerveux central, alors que les axones efférents en sortent pour innerver la périphérie. Les axones afférents sont des axones de neurone sensoriel. De même, les axones efférents sont presque toujours des axones de motoneurones. Il existe cependant des axones efférents qui ne sont pas du tout moteurs. Par exemple, de nombreux axones efférents servent à réguler la sensibilité des neurones sensoriels, notamment pour réguler la sensibilité à la douleur. Mais nous en reparlerons plus tard. [[File:Afferent and efferent neurons en.svg|centre|vignette|upright=2|Axones afférents et éfférents.]] [[File:202101 Sensory neuron.svg|vignette|Neurone sensoriel pseudo-unipolaire.]] Il y a plusieurs différences entre axones afférent/sensitifs et efférents/moteurs. Premièrement, les axones sensitifs naissent d'un neurone unipolaire, à savoir que son axone se divise en deux branches allant dans des directions opposées. L'une innerve la peau ou l'organe, l'autre fait synapse avec un neurone dans le système nerveux central. Les motoneurones sont eux des neurones multipolaires, avec plusieurs dendrites et un seul axone. Il y a quelques exceptions, mais c'est une règle assez fiable : neurones unipolaires dans le SNP, multipolaires dans le SNC. Les motoneurones émettent des axones en direction des muscles (ou des organes dans certains cas précis). Ils se regroupent généralement pour former des nerf moteurs. Le bout d'un axone de motoneurone se ramifie en plusieurs branches, qui font chacun synapse sur un muscle. La synapse entre muscle et motoneurone est appelée une '''jonction neuromusculaire'''. [[File:Motoneuron.svg|centre|vignette|upright=2|Motoneurone]] ===La classification d'Erlanger et Gasser=== La première classification distingue les fibres périphériques selon la vitesse de l'influx nerveux, qui dépend de la présence d'une gaine de myéline et du diamètre de l'axone. Sur la base de ces deux critères, on peut distinguer plusieurs types et sous-types d'axones, qui ont des vitesses différentes pour l'influx nerveux. La classification obtenue avec ce critère s'appelle la '''classification d'Erlanger et Gasser'''. Elle marche pour tous les neurones et ne fait pas la différence entre les axones moteurs, sensoriels ou autres. Elle classe les fibres en trois groupes : le groupe A, le groupe B et le groupe C. Pour la résumer, les axones du groupe A ont une gaine de myéline très épaisse, ceux du groupe B moyennement épaisse et ceux du groupe C n'ont pas de gaine de myéline. Les axones de motoneurones sont toutes des fibres du groupe A. Il faut dire que les fibres motrices ont besoin de transmettre rapidement les commandes motrices, ce qui fait qu'elles sont beaucoup myélinisées. Les neurones sensoriels sont eux dans les catégories A, B et C. {|class="wikitable" |- ! ! Axones A ! Axones B ! Axones C |- ! Myélinisée | Oui, beaucoup | Oui, moyennement | Non |- ! Diamètre | Variable : de 0.2-1.5 µm à 13-20 µm | 1 à 5 µm | 0.2 à 1.5 µm |- ! Vitesse de l'influx nerveux | Variable : de 3-30 à 80-120 mètres par secondes selon le sous-type d'axone | 3 à 15 mètres par secondes | 0.5 à 2.0 mètres par secondes |} Le groupe A est lui-même divisé en quatre sous-types nommés alpha (<math>\alpha</math>), beta (<math>\beta</math>), gamma (<math>\gamma</math>) et delta (<math>\delta</math>). Ces sous-groupes se distinguent non pas sur la taille de la gaine de myéline, mais par leur diamètre. {|class="wikitable" |+ Axones du groupe A |- ! ! Axones alpha (A <math>\alpha</math>) ! Axones beta (A <math>\beta</math>) ! Axones gamma (A <math>\gamma</math>) ! Axones delta (A <math>\delta</math>) |- ! Diamètre | 13 à 20 µm | 6 à 12 µm | 5 à 8 µm | 1 à 5 µm |- ! Vitesse de l'influx nerveux | 80-120 mètres par secondes | 33 à 75 mètres par secondes | 4 à 24 mètres par secondes | 3 à 30 mètres par secondes |} ===La classification en type I, II, III et IV=== Une autre classification distingue les axones des motoneurones, ceux des récepteurs sensoriels et ceux liés au système nerveux dit autonome. Elle ne recoupe pas la classification précédente : par exemple, le groupe A de la classification précédente regroupe des motoneurones et des axones sensoriels. Pour les fibres motrices, elle distingue trois sous-types, qui sont des fibres <math>\alpha</math>, <math>\beta</math> et <math>\gamma</math>. {|class="wikitable" |+ Fibres du système nerveux somatique moteur |- ! ! Fibres <math>\alpha</math> ! Fibres <math>\beta</math> ! Fibres <math>\gamma</math> |- ! Diamètre | 13 à 20 µm | | 5 à 8 µm |- ! Vitesse de conduction | 80 à 120 m/s | | 4 à 24 m/s |} Pour les axones sensoriels, elle distingue les fibres de type Ia, Ib, II, III et IV. Les types Ia, Ib, II et III correspondent respectivement aux axones des groupes A <math>\alpha</math>, A <math>\beta</math>, A <math>\gamma</math> et A <math>\delta</math>. le type IV appartient au groupe C de la classification précédente. {|class="wikitable" |+ Fibres du système nerveux somatique sensoriel |- ! ! Fibres Ia (groupe A <math>\alpha</math>) ! Fibres Ib (groupe A <math>\beta</math>) ! Fibres II (groupe A <math>\gamma</math>) ! Fibres III (groupe A <math>\delta</math>) ! Fibres IV (Groupe C) |- ! Gaine de myéline | colspan="3" | Épaisse | Mince | Absente |- ! Diamètre | colspan="2" | 13 à 20 µm | 6 à 12 µm | 1 à 5 µm | 0.2 à 1.5 µm |- ! Vitesse de conduction | colspan="2" | 80–120 m/s | 33–75 m/s | 3–30 m/s | 0.5-2.0 m/s |} Les axones du système nerveux autonome sont des fibres de type B ou de type C. Les fibres de type B sont dites pré-ganglionnaires, alors que celles de type C sont post-ganglionnaires. La différence est que les fibres pré-ganglionnaires rentrent dans un ganglion autonome, alors que les post-ganglionnaires en sortent. {| class="wikitable" |+ Fibres du système nerveux autonome |- ! Type ! Classification d'Erlanger-Gasser ! Diamètre ! Gaine de myéline ! Vitesse de conduction |- ! Fibres pré-ganglioniques | B | 1 à 5 µm | Présente | 3 à 15 m/s |- ! Fibres post-ganglioniques | C | 0.2 à 1.5 µm | Absente | 0.5 à 2.0 m/s |} ==Les ganglions sensoriels et sympathiques/parasympathiques== Après avoir vu les nerfs, voyons maintenant les ganglions. Les axones efférents et afférents proviennent de neurones, qui sont bien placés quelque part. Dans la plupart des cas, les axones proviennent de neurones regroupés dans des '''ganglions périphériques''', des amas de neurones, situés en-dehors du cerveau ou de la moelle épinière. Un point important est qu'il n'y a pas de ganglion moteur somatique, seulement des ganglions sensoriels et des ganglions autonomes. La motricité volontaire ne passe jamais pas un ganglion, seules les sensations et la motricité inconsciente le font. ===Les ganglions sensoriels et autonomes=== Les axones afférents sortent de neurones unipolaires. Mais où sont localisés ces neurones unipolaires ? La réponse identifie deux cas possibles. Dans le premier, les neurones sensoriels sont regroupés dans des '''ganglions sensoriels''', qui émettent des axones dans deux directions : un vers la peau ou l'organe innervé, l'autre vers le système nerveux central. Dans le second, ils proviennent des neurones sensoriels dispersés dans le corps, dont les dendrites captent les sensations et dont l'axone fait synapse avec le système nerveux central. Il y a aussi une sorte d'intermédiaire où on a deux neurones : un neurone récepteur qui est dans la peau ou l'organe, un neurone relai dans un ganglion sensoriel. [[File:Structure of sensory system (4 models) E.PNG|centre|vignette|upright=2.0|Structure du système nerveux sensoriel.]] Il est possible de se poser la même question quant à la localisation des neurones moteurs. Là encore, il y a deux réponses différentes. Mais cette fois-ci, la réponse dépend de si le motoneurone appartient au système somatique ou autonome. Les motoneurones du système somatique se situent dans le système nerveux central, dans le cerveau ou la moelle épinière. Avec le système nerveux somatique, les motoneurones innervent directement les muscles associés. Pour le système nerveux autonome, les motoneurones se situent dans des ganglions périphériques appelés '''ganglions autonomes''', sauf pour quelques exceptions. Mais pour le système nerveux autonome, les motoneurones ne se connectent pas directement sur l'organe à innerver, mais passent par l'intermédiaire d'un ganglion périphérique, qui fait lui-même synapse avec l'organe innervé. un motoneurone du SNC fait synapse avec un motoneurone dans le ganglion, qui lui-même innerve le muscle/organe. Les axones qui sortent de la moelle épinière forment une '''fibre pré-ganglionnaire''' (avant le ganglion), alors que le ganglion autonome émet une '''fibre post-ganglionnaire''' à destination du muscle/organe. [[File:1505 Comparison of Somatic and Visceral Reflexes.jpg|centre|vignette|upright=2|Comparaison entre système somatique et autonome.]] Les fibres post-ganglionnaires gonflent en plusieurs endroits et forment des '''varices post-ganglionnaires'''. C'est de ces varices que sont émis les neurotransmetteurs à destination du muscle. [[File:1504 Autonomic Varicosities.jpg|centre|vignette|upright=2|Varices post-ganglionnaires.]] ===Les neurotransmetteurs des ganglions périphériques=== Les neurotransmetteurs émis par les motoneurones varient suivant que l'on parle du système nerveux somatique, parasympathique ou sympathique. Les motoneurones du système nerveux central sont tous cholinergiques, ce qui fait que les axones sortant du SNC sont cholinergiques, que ce soit dans le système somatique, sympathique ou parasympathique. Pour le système somatique, ca s'arrête là car les motoneurones cholinergiques innervent directement les muscles. Mais pour le système autonome, il y a passage par un ganglion autonome. Et celui émet des fibres post-ganglionnaires, dont le neurotransmetteur n'est pas le même pour le système sympathique ou parasympathique. Les fibres sympathiques post-ganglionnaires sont noradrénergiques/adrénergiques, ce qui les démarque de la transmission cholinergique des fibres parasympathiques. [[File:Ach.SNP.png|centre|vignette|upright=2.0|Implication de l'acétylcholine dans les systèmes nerveux sympathiques et parasympathiques.]] Il existe cependant quelques exceptions à ces explications simplifiées. * Les axones sympathiques innervant le rein émettent de la dopamine, non de l'acétylcholine. * Les glandes sudoripares eccrines sont innervées par des fibres sympathiques cholinergiques. * Les glandes surrénales sont innervées par des fibres sympathiques cholinergiques. Le cas des glandes surrénales est particulier, car les glandes surrénales sont innervées directement par les motoneurones cérébraux, et non par l'intermédiaire d'un ganglion sympathique/parasympathique. Au passage, ces glandes surrénales sont dérivées embryologiquement du système nerveux et elles émettent de l'adrénaline et de la noradrénaline dans le sang, ce qui fait que cette exception semble plus apparente que factuelle. ==Les atteintes des nerfs== Il n'est pas rare qu'un nerf soit atteint par une maladie et/ou une inflammation. Notons qu'il faut faire la différence entre les maladies qui ne touchent qu'un seul nerf, aussi appelées '''mononeuropathies''', et les maladies qui touchent plusieurs nerfs, voire tous les nerfs de l'organisme, appelées des '''polyneuropathies'''. Les polynévrites sont causées par des maladies systémiques, qui touchent l'ensemble du système nerveux, quand ce n'est pas le corps tout entier. Elles sont causées par un diabète, des déficiences en vitamine, des infections, et j'en passe. Les mononévrites ont des causes bien plus localisées et sont souvent le résultat d'un nerf compressé ou écrasé. ===Les traumatismes des nerfs=== Un nerf peut se faire sectionner à la suite d'un traumatisme quelconque. Dans ce cas, les conséquences peuvent se traduire par une paralysie ou une perte des sensations localisée, selon le nerf coupé. Les conséquences sont similaires, bien que moins importantes, si le nerf est compressé. Dans le cas général, les symptômes sont à la fois moteurs et sensitifs, avec cependant quelques exceptions. Il est rare qu'une compression d'un nerf ne cause que des symptômes moteurs ou que des symptômes sensitifs. {|class="wikitable" |- !Nerf touché !Nom du syndrome !Description |- !Nerf médian, au niveau du poignet |Syndrome du canal carpien | * Douleurs, fourmillement et picotements sur la main, de préférence sur la palme des trois premiers doigts. * Parfois troubles moteurs de la main et du poignet. * Rarement hyper-sudation ou œdème de la main. |- !Nerf cubital, au niveau du coude |Syndrome du canal tubulaire du coude | * Paresthésies, surtout dans les 2 derniers doigts et le bord cubital de la main. * Perte de sensibilité de la main, sur toutes les sensations : toucher, douleur, température, ... * Signes moteurs : faiblesse musculaire dans la main et les doigts, une difficulté à écarter les doigts, etc. |- !... |... |... |} Si on observe un nerf compressé au microscope, on observe plusieurs lésions typiques. En premier lieu, la gaine de myéline contient plus de trous, plus de nœuds de Ranvier qu'un nerf normal. Elle est aussi moins épaisse, la majorité du diamètre du nerf étant occupé par l'axone proprement dit. On voit aussi un développement des cellules de Schwann, une croissance de leurs excroissances, et d'autres signes de régénération de la gaine de myéline. Pour résumer, la compression d'un nerf entraîne des dommages limités à la gaine de myéline, sauf exceptions. Les axones du nerf, ainsi que les tissus conjonctifs et le soma des cellules gliales, ne sont pas touchés. On peut s'étonner d'un tel résultat, mais sachez que les nerfs sont des tissus assez mous, qui peuvent encaisser une compression en se pliant ou se déformant sans se casser. Peu importe qu'il s'agisse d'une compression ou d'une section, le nerf finit par guérir. Les déficits sont temporaires, car les nerfs se régénèrent en quelques mois ou années, ce qui permet une récupération complète. Progressivement, le patient commence à ressentir des picotements, puis retrouve ses sensations ou sa motricité avec le temps. La durée de rémission dépend de l'état du nerf, les nerfs compressés tendant à récupérer plus vite que les nerfs sectionnés. La régénération impose que les ganglions à l'origine du nerf soient épargnés : la régénération d'un nerf consiste en une repousse de l'axone à partir du ganglion. La repousse des nerfs, fibres et faisceaux, se fait dans le système nerveux périphérique uniquement. Les nerfs associés au système nerveux central ne repoussent pas. Il s'agit là d'une distinction qu'on développera dans le chapitre sur la régénération du système nerveux : le système nerveux périphérique se régénère, pas le système nerveux central. Il s'agit d'un principe ayant peu d'exceptions, si ce n'est pour le nerf olfactif et une aire cérébrale appelée le bulbe olfactif. Une explication un peu moins simple est que le système nerveux central ne se régénère pas, sauf pour les aires de l'odorat. ===Les névrites (inflammations des nerfs)=== Il arrive qu'un nerf s’enflamme, suite à une infection ou une maladie auto-immune : c'est une '''névrite'''. L'origine des névrites est souvent d'origine infectieuse : la lèpre, la diphtérie ou le tétanos peuvent entraîner des névrites. Certains médicaments peuvent notamment entraîner des névrites, comme l'imipramine ou certains antipaludéens, tout comme des intoxications au mercure ou au plomb. Mais le plus souvent, les névrites sont causées par une absorption d'alcool trop importante, un diabète, ou une déficience en vitamine B12. Comme on s'en doute, ces causes entraînent plus souvent des polynévrites, vu qu'il s'agit d’affections globales, qui touchent le corps entier (diabète, infections, médicaments). {|class="wikitable" |- !Cause d'une névrite !Exemples |- !Infection |Diphtérie, Tétanos, Polyomyélite |- !Maladie auto-immunes |Sclérose en plaque |- !Intoxication |Alcool, antibiotiques (metronidazole), antipaludéens, imipramine, etc. |- !Déficience en vitamine |Carence en vitamine B6 ou B12 |- !Surcharge en sucre |Diabète, autre |- !Radiations |Traitement du cancer |- !Compression, traumatisme |Syndrome du canal carpien |} <noinclude> {{NavChapitre | book=Neurosciences | prev=La moelle épinière | prevText=La moelle épinière | next=Le système nerveux périphérique | nextText=Le système nerveux périphérique }}{{autoCat}} </noinclude> knov7ke0y2jjvt4jfej552j49a3mks1 762834 762833 2026-04-03T14:11:37Z Mewtow 31375 /* Les ganglions sensoriels et sympathiques/parasympathiques */ 762834 wikitext text/x-wiki Le système nerveux périphérique est la portion du système nerveux qui reste quand on retire le cerveau et la moelle épinière. Il est composé de nerfs, de petites fibres nerveuses et des ganglions (des amas de neurones, pour rappel). Il innerve la peau, les organes internes, les muscles et quelques autres structures anatomiques. Mais avant de voir le système nerveux périphérique, nous allons détailler ce qu'il y a à l'intérieur d'un nerf. ==Les nerfs périphériques== [[File:Transverse section of the optic nerve, from Leeuwenhoek, 1675 Wellcome L0001822.jpg|vignette|Section d'un nerf optique.]] Un '''nerf''' est un paquet d'axones, entourés par une pellicule externe appelée l''''épinèvre''', avec quelques vaisseaux sanguins entre les deux. Si vous regarder une coupe-section d'un nerf, vous verrez quelque chose de similaire à ce qu'il y a dans la photographie de droite. Vous vous dites sans doute que les zones blanches sont des axones, le noir étant du tissu de soutien. La partie noire regroupe du tissu de soutien, avec des gros vaisseaux sanguins pour alimenter le nerf en oxygène/nutriments. Les zones blanches sont des paquets d'axones, contenant plus d'un millier d'axones. Les paquets d'axones en question sont appelés des '''fascicules'''. Ils sont formés en entourant un paquet d'axone avec une pellicule appelée le '''périnèrve''', qui entoure le paquet d'axone. Le périnévre est composé de plusieurs couches de cellules "musculaires" (des myofibroblastes). Outre des axones, il y a aussi des vaisseaux sanguins de petite taille dans un fascicule. Les vaisseaux sanguins d'un fascicule sont des ramifications de vaisseaux sanguins plus gros, présents dans le tissu de soutien du nerf. En clair, les axones sont regroupés en fascicules, qui sont eux-même regroupés en nerfs. L'ensemble est illustré ci-contre, et les illustrations ci-dessous montrent une section d'un nerf optique. [[File:Nerf detaillé..png|centre|vignette|upright=2.0|Section d'un nerf.]] Les axones sont entourés par une pellicule protectrice, l''''endonèvre''', qui entoure la gaine de myéline des axones. L'endonèvre est composé de deux couches : une couche de glycocalyx, entourée de fibres de collagène. L'endonèvre n'est pas directement posé sur l'axone, il y a un peu de '''liquide endonévrial''' entre les deux. Il a un rôle similaire au liquide cérébrospinal, à savoir que c'est un liquide qui protège les neurones contre toute agression extérieure. Il y a aussi des vaisseaux sanguins dans l'endonèvre, et précisément des capillaires sanguins de très petite taille. Un point important est que l'endonèvre ne laisse pas passer grand chose : la plupart des molécules extérieures ne peuvent pas rentrer dans le neurone, à cause de lui. Il protège les neurones contre la présence de toxiques, mais surtout : il régule l'équilibre ionique à la surface de l'axone. Il garantit qu'il n'y a pas d'excès ou de déficiences en ions potassium, sodium, calcium, chlore et autres ; qui sont impliqués dans le potentiel de membrane. Et il ne laisse pas non plus passer les virus et bactéries, du moins pas facilement. De par sa fonction de tampon chimique et de protection immunitaire, il est l'équivalent pour les nerfs de la barrière hématoencéphalique, qui protège le cerveau contre les agressions extérieures. ==Les axones du système nerveux périphérique== Le système nerveux périphérique comprend des axones, appelés les '''fibres périphériques''', ou encore fibres. Les fibres périphériques sont à distinguer des nerfs, les différences étant qu'un nerf est un regroupement d'axone, alors que les fibres sont des axones seuls. Les axones sensoriels et moteurs ne se mélangent pas, ou rarement, dans le système nerveux périphérique. Il existe plusieurs types de fibres périphériques, qui se distinguent essentiellement par leur caractère moteur/sensoriel/autonomique, par leur vitesse de conduction, etc. Dans ce qui suit, nous allons voir les classifications principales des fibres périphériques. ===Les axones afférents et efférents=== Avant de poursuivre, parlons rapidement de la notion d'axones afférents et efférents. Les axones dits afférent partent de la périphérie et rentrent dans le système nerveux central, alors que les axones efférents en sortent pour innerver la périphérie. Les axones afférents sont des axones de neurone sensoriel. De même, les axones efférents sont presque toujours des axones de motoneurones. Il existe cependant des axones efférents qui ne sont pas du tout moteurs. Par exemple, de nombreux axones efférents servent à réguler la sensibilité des neurones sensoriels, notamment pour réguler la sensibilité à la douleur. Mais nous en reparlerons plus tard. [[File:Afferent and efferent neurons en.svg|centre|vignette|upright=2|Axones afférents et éfférents.]] [[File:202101 Sensory neuron.svg|vignette|Neurone sensoriel pseudo-unipolaire.]] Il y a plusieurs différences entre axones afférent/sensitifs et efférents/moteurs. Premièrement, les axones sensitifs naissent d'un neurone unipolaire, à savoir que son axone se divise en deux branches allant dans des directions opposées. L'une innerve la peau ou l'organe, l'autre fait synapse avec un neurone dans le système nerveux central. Les motoneurones sont eux des neurones multipolaires, avec plusieurs dendrites et un seul axone. Il y a quelques exceptions, mais c'est une règle assez fiable : neurones unipolaires dans le SNP, multipolaires dans le SNC. Les motoneurones émettent des axones en direction des muscles (ou des organes dans certains cas précis). Ils se regroupent généralement pour former des nerf moteurs. Le bout d'un axone de motoneurone se ramifie en plusieurs branches, qui font chacun synapse sur un muscle. La synapse entre muscle et motoneurone est appelée une '''jonction neuromusculaire'''. [[File:Motoneuron.svg|centre|vignette|upright=2|Motoneurone]] ===La classification d'Erlanger et Gasser=== La première classification distingue les fibres périphériques selon la vitesse de l'influx nerveux, qui dépend de la présence d'une gaine de myéline et du diamètre de l'axone. Sur la base de ces deux critères, on peut distinguer plusieurs types et sous-types d'axones, qui ont des vitesses différentes pour l'influx nerveux. La classification obtenue avec ce critère s'appelle la '''classification d'Erlanger et Gasser'''. Elle marche pour tous les neurones et ne fait pas la différence entre les axones moteurs, sensoriels ou autres. Elle classe les fibres en trois groupes : le groupe A, le groupe B et le groupe C. Pour la résumer, les axones du groupe A ont une gaine de myéline très épaisse, ceux du groupe B moyennement épaisse et ceux du groupe C n'ont pas de gaine de myéline. Les axones de motoneurones sont toutes des fibres du groupe A. Il faut dire que les fibres motrices ont besoin de transmettre rapidement les commandes motrices, ce qui fait qu'elles sont beaucoup myélinisées. Les neurones sensoriels sont eux dans les catégories A, B et C. {|class="wikitable" |- ! ! Axones A ! Axones B ! Axones C |- ! Myélinisée | Oui, beaucoup | Oui, moyennement | Non |- ! Diamètre | Variable : de 0.2-1.5 µm à 13-20 µm | 1 à 5 µm | 0.2 à 1.5 µm |- ! Vitesse de l'influx nerveux | Variable : de 3-30 à 80-120 mètres par secondes selon le sous-type d'axone | 3 à 15 mètres par secondes | 0.5 à 2.0 mètres par secondes |} Le groupe A est lui-même divisé en quatre sous-types nommés alpha (<math>\alpha</math>), beta (<math>\beta</math>), gamma (<math>\gamma</math>) et delta (<math>\delta</math>). Ces sous-groupes se distinguent non pas sur la taille de la gaine de myéline, mais par leur diamètre. {|class="wikitable" |+ Axones du groupe A |- ! ! Axones alpha (A <math>\alpha</math>) ! Axones beta (A <math>\beta</math>) ! Axones gamma (A <math>\gamma</math>) ! Axones delta (A <math>\delta</math>) |- ! Diamètre | 13 à 20 µm | 6 à 12 µm | 5 à 8 µm | 1 à 5 µm |- ! Vitesse de l'influx nerveux | 80-120 mètres par secondes | 33 à 75 mètres par secondes | 4 à 24 mètres par secondes | 3 à 30 mètres par secondes |} ===La classification en type I, II, III et IV=== Une autre classification distingue les axones des motoneurones, ceux des récepteurs sensoriels et ceux liés au système nerveux dit autonome. Elle ne recoupe pas la classification précédente : par exemple, le groupe A de la classification précédente regroupe des motoneurones et des axones sensoriels. Pour les fibres motrices, elle distingue trois sous-types, qui sont des fibres <math>\alpha</math>, <math>\beta</math> et <math>\gamma</math>. {|class="wikitable" |+ Fibres du système nerveux somatique moteur |- ! ! Fibres <math>\alpha</math> ! Fibres <math>\beta</math> ! Fibres <math>\gamma</math> |- ! Diamètre | 13 à 20 µm | | 5 à 8 µm |- ! Vitesse de conduction | 80 à 120 m/s | | 4 à 24 m/s |} Pour les axones sensoriels, elle distingue les fibres de type Ia, Ib, II, III et IV. Les types Ia, Ib, II et III correspondent respectivement aux axones des groupes A <math>\alpha</math>, A <math>\beta</math>, A <math>\gamma</math> et A <math>\delta</math>. le type IV appartient au groupe C de la classification précédente. {|class="wikitable" |+ Fibres du système nerveux somatique sensoriel |- ! ! Fibres Ia (groupe A <math>\alpha</math>) ! Fibres Ib (groupe A <math>\beta</math>) ! Fibres II (groupe A <math>\gamma</math>) ! Fibres III (groupe A <math>\delta</math>) ! Fibres IV (Groupe C) |- ! Gaine de myéline | colspan="3" | Épaisse | Mince | Absente |- ! Diamètre | colspan="2" | 13 à 20 µm | 6 à 12 µm | 1 à 5 µm | 0.2 à 1.5 µm |- ! Vitesse de conduction | colspan="2" | 80–120 m/s | 33–75 m/s | 3–30 m/s | 0.5-2.0 m/s |} Les axones du système nerveux autonome sont des fibres de type B ou de type C. Les fibres de type B sont dites pré-ganglionnaires, alors que celles de type C sont post-ganglionnaires. La différence est que les fibres pré-ganglionnaires rentrent dans un ganglion autonome, alors que les post-ganglionnaires en sortent. {| class="wikitable" |+ Fibres du système nerveux autonome |- ! Type ! Classification d'Erlanger-Gasser ! Diamètre ! Gaine de myéline ! Vitesse de conduction |- ! Fibres pré-ganglioniques | B | 1 à 5 µm | Présente | 3 à 15 m/s |- ! Fibres post-ganglioniques | C | 0.2 à 1.5 µm | Absente | 0.5 à 2.0 m/s |} ==Les ganglions sensoriels et sympathiques/parasympathiques== Les axones efférents et afférents proviennent de neurones, qui sont bien placés quelque part. Mais où ? La réponse est différente selon qu'on parle du système nerveux moteur, autonome ou sensoriel. La motricité volontaire passe directement du système nerveux central au muscle. Les nerfs moteurs sortent de la moelle épinière ou du cerveau et innervent directement les muscles, sans passage par un ganglions. Les neurones efférents pour la motricité volontaire sont localisés dans le système nerveux, pas ailleurs. Par contre, les sensations et la motricité inconsciente font autrement. Dans la plupart des cas, elles font relai dans des '''ganglions périphériques''', des amas de neurones, situés en-dehors du cerveau ou de la moelle épinière. Les neurones afférents sont localisés dans des ''ganglions sensoriels'', les neurones efférents autonomes sont eux aussi regroupés dans des ''ganglions autonomes''. Pour résumer, il n'y a pas de ganglion moteur somatique, seulement des ganglions sensoriels et des ganglions autonomes. ===Les ganglions sensoriels=== Les axones afférents sortent de neurones unipolaires. Mais où sont localisés ces neurones unipolaires ? La réponse identifie plusieurs cas possibles. * Dans le premier, les neurones sensoriels sont regroupés dans des '''ganglions sensoriels''', qui émettent des axones dans deux directions : un vers la peau ou l'organe innervé, l'autre vers le système nerveux central. * Dans le second, ils proviennent des neurones sensoriels dispersés dans le corps, dont les dendrites captent les sensations et dont l'axone fait synapse avec le système nerveux central. * Il y a aussi une sorte d'intermédiaire où on a deux neurones : un neurone récepteur qui est dans la peau ou l'organe, un neurone relai dans un ganglion sensoriel. [[File:Structure of sensory system (4 models) E.PNG|centre|vignette|upright=2.0|Structure du système nerveux sensoriel.]] ===Les ganglions autonomes=== : Nous utiliserons maintenant l'abréviation SNA pour parler du Système Nerveux Autonome. Pour le SNA, les motoneurones se situent dans des ganglions périphériques, appelés '''ganglions autonomes''', sauf pour quelques exceptions. Il faut noter que les neurones de ces ganglions sont multipolaires, contrairement aux ganglions sensoriels. En conséquence, les motoneurones du SNC font synapse avec les neurones du ganglions autonome, qui eux émettent des axones en direction du muscle ou de la glande à innerver. Les axones qui sortent de la moelle épinière forment une '''fibre pré-ganglionnaire''' (avant le ganglion), alors que le ganglion autonome émet une '''fibre post-ganglionnaire''' à destination du muscle/organe. [[File:1505 Comparison of Somatic and Visceral Reflexes.jpg|centre|vignette|upright=2|Comparaison entre système somatique et autonome.]] Les fibres post-ganglionnaires gonflent en plusieurs endroits et forment des '''varices post-ganglionnaires'''. C'est de ces varices que sont émis les neurotransmetteurs à destination du muscle. [[File:1504 Autonomic Varicosities.jpg|centre|vignette|upright=2|Varices post-ganglionnaires.]] Maintenant, regardons le neurotransmetteurs utilisés par le SNA. Les fibres pré-ganglionnaires sont systématiquement cholinergiques. La raison à cela est que les motoneurones du SNC sont tous cholinergiques, et ce sont eux qui donnent naissance aux fibre pré-ganglionnaires. Pour le système somatique, ça s'arrête là car les motoneurones cholinergiques innervent directement les muscles, via la jonction neuromusculaire. Mais pour le système autonome, il y a passage par un ganglion autonome, qui fait office de relai et peut parfois changer de neurotransmetteur. Le ganglion autonome émet des fibres post-ganglionnaires, dont le neurotransmetteur n'est pas le même pour le système sympathique ou parasympathique. * Les fibres sympathiques post-ganglionnaires sont noradrénergiques/adrénergiques. * Les fibres para sympathiques post-ganglionnaires sont cholinergiques. [[File:Ach.SNP.png|centre|vignette|upright=2.0|Implication de l'acétylcholine dans les systèmes nerveux sympathiques et parasympathiques.]] Il existe cependant quelques exceptions à ces explications simplifiées. * Les axones sympathiques innervant le rein émettent de la dopamine, non de l'acétylcholine. * Les glandes sudoripares eccrines sont innervées par des fibres sympathiques cholinergiques. * Les glandes surrénales sont innervées par des fibres sympathiques cholinergiques. Le cas des glandes surrénales est particulier, car les glandes surrénales sont innervées directement par les motoneurones cérébraux, et non par l'intermédiaire d'un ganglion sympathique/parasympathique. Au passage, ces glandes surrénales sont dérivées embryologiquement du système nerveux et elles émettent de l'adrénaline et de la noradrénaline dans le sang, ce qui fait que cette exception semble plus apparente que factuelle. ==Les atteintes des nerfs== Il n'est pas rare qu'un nerf soit atteint par une maladie et/ou une inflammation. Notons qu'il faut faire la différence entre les maladies qui ne touchent qu'un seul nerf, aussi appelées '''mononeuropathies''', et les maladies qui touchent plusieurs nerfs, voire tous les nerfs de l'organisme, appelées des '''polyneuropathies'''. Les polynévrites sont causées par des maladies systémiques, qui touchent l'ensemble du système nerveux, quand ce n'est pas le corps tout entier. Elles sont causées par un diabète, des déficiences en vitamine, des infections, et j'en passe. Les mononévrites ont des causes bien plus localisées et sont souvent le résultat d'un nerf compressé ou écrasé. ===Les traumatismes des nerfs=== Un nerf peut se faire sectionner à la suite d'un traumatisme quelconque. Dans ce cas, les conséquences peuvent se traduire par une paralysie ou une perte des sensations localisée, selon le nerf coupé. Les conséquences sont similaires, bien que moins importantes, si le nerf est compressé. Dans le cas général, les symptômes sont à la fois moteurs et sensitifs, avec cependant quelques exceptions. Il est rare qu'une compression d'un nerf ne cause que des symptômes moteurs ou que des symptômes sensitifs. {|class="wikitable" |- !Nerf touché !Nom du syndrome !Description |- !Nerf médian, au niveau du poignet |Syndrome du canal carpien | * Douleurs, fourmillement et picotements sur la main, de préférence sur la palme des trois premiers doigts. * Parfois troubles moteurs de la main et du poignet. * Rarement hyper-sudation ou œdème de la main. |- !Nerf cubital, au niveau du coude |Syndrome du canal tubulaire du coude | * Paresthésies, surtout dans les 2 derniers doigts et le bord cubital de la main. * Perte de sensibilité de la main, sur toutes les sensations : toucher, douleur, température, ... * Signes moteurs : faiblesse musculaire dans la main et les doigts, une difficulté à écarter les doigts, etc. |- !... |... |... |} Si on observe un nerf compressé au microscope, on observe plusieurs lésions typiques. En premier lieu, la gaine de myéline contient plus de trous, plus de nœuds de Ranvier qu'un nerf normal. Elle est aussi moins épaisse, la majorité du diamètre du nerf étant occupé par l'axone proprement dit. On voit aussi un développement des cellules de Schwann, une croissance de leurs excroissances, et d'autres signes de régénération de la gaine de myéline. Pour résumer, la compression d'un nerf entraîne des dommages limités à la gaine de myéline, sauf exceptions. Les axones du nerf, ainsi que les tissus conjonctifs et le soma des cellules gliales, ne sont pas touchés. On peut s'étonner d'un tel résultat, mais sachez que les nerfs sont des tissus assez mous, qui peuvent encaisser une compression en se pliant ou se déformant sans se casser. Peu importe qu'il s'agisse d'une compression ou d'une section, le nerf finit par guérir. Les déficits sont temporaires, car les nerfs se régénèrent en quelques mois ou années, ce qui permet une récupération complète. Progressivement, le patient commence à ressentir des picotements, puis retrouve ses sensations ou sa motricité avec le temps. La durée de rémission dépend de l'état du nerf, les nerfs compressés tendant à récupérer plus vite que les nerfs sectionnés. La régénération impose que les ganglions à l'origine du nerf soient épargnés : la régénération d'un nerf consiste en une repousse de l'axone à partir du ganglion. La repousse des nerfs, fibres et faisceaux, se fait dans le système nerveux périphérique uniquement. Les nerfs associés au système nerveux central ne repoussent pas. Il s'agit là d'une distinction qu'on développera dans le chapitre sur la régénération du système nerveux : le système nerveux périphérique se régénère, pas le système nerveux central. Il s'agit d'un principe ayant peu d'exceptions, si ce n'est pour le nerf olfactif et une aire cérébrale appelée le bulbe olfactif. Une explication un peu moins simple est que le système nerveux central ne se régénère pas, sauf pour les aires de l'odorat. ===Les névrites (inflammations des nerfs)=== Il arrive qu'un nerf s’enflamme, suite à une infection ou une maladie auto-immune : c'est une '''névrite'''. L'origine des névrites est souvent d'origine infectieuse : la lèpre, la diphtérie ou le tétanos peuvent entraîner des névrites. Certains médicaments peuvent notamment entraîner des névrites, comme l'imipramine ou certains antipaludéens, tout comme des intoxications au mercure ou au plomb. Mais le plus souvent, les névrites sont causées par une absorption d'alcool trop importante, un diabète, ou une déficience en vitamine B12. Comme on s'en doute, ces causes entraînent plus souvent des polynévrites, vu qu'il s'agit d’affections globales, qui touchent le corps entier (diabète, infections, médicaments). {|class="wikitable" |- !Cause d'une névrite !Exemples |- !Infection |Diphtérie, Tétanos, Polyomyélite |- !Maladie auto-immunes |Sclérose en plaque |- !Intoxication |Alcool, antibiotiques (metronidazole), antipaludéens, imipramine, etc. |- !Déficience en vitamine |Carence en vitamine B6 ou B12 |- !Surcharge en sucre |Diabète, autre |- !Radiations |Traitement du cancer |- !Compression, traumatisme |Syndrome du canal carpien |} <noinclude> {{NavChapitre | book=Neurosciences | prev=La moelle épinière | prevText=La moelle épinière | next=Le système nerveux périphérique | nextText=Le système nerveux périphérique }}{{autoCat}} </noinclude> fu1e7ymygns50y28ofdi78b1fzso07x Discussion Wikilivres:Le Bistro/2026 5 83406 762841 762520 2026-04-03T17:11:02Z MediaWiki message delivery 36013 /* Action Required: Update templates/modules for electoral maps (Migrating from P1846 to P14226) */ nouvelle section 762841 wikitext text/x-wiki == Actualités techniques n° 2026-03 == <section begin="technews-2026-W03"/><div class="plainlinks"> Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2026/03|D’autres traductions]] sont disponibles. '''En lumière cette semaine''' * La Fondation Wikimedia a publié des questions directrices pour son plan annuel de juillet 2026 à juin 2027 sur les plateformes [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2026-2027/Product & Technology OKRs|Meta]] et ''[[diffblog:2025/12/10/shaping-wikimedia-foundations-2026-2027-annual-goals-key-questions-for-the-wikimedia-movement/|Diff]]''. Celles-ci portent sur les tendances mondiales, une expérimentation plus rapide et plus constructive, un meilleur accompagnement des nouveaux contributeurs, le renforcement du rôle des éditeurs et des utilisateurs avancés, l'amélioration de la collaboration entre les projets, ainsi que le développement et la fidélisation du lectorat. Des commentaires et suggestions sont les bienvenus sur la [[m:Talk:Wikimedia Foundation Annual Plan/2026-2027|page de discussion]]. '''Actualités pour la contribution''' * Dans le cadre des travaux en cours de l'équipe technique communautaire sur le projet [[m:Special:MyLanguage/Community Wishlist/W372|Listes de surveillance multiples]], l'affichage de [[Special:EditWatchlist|Modifier la liste de surveillance]] sera mis à jour entant que qu'une première étape vers la prise en charge de plusieurs listes de surveillance. De plus, la pagination de [[Special:Search|Recherche]] sera également mise à jour, dans le cadre du travail sur le souhait [[m:Special:MyLanguage/Community Wishlist/W186|Refonte de la pagination / navigation des pages]]. [https://phabricator.wikimedia.org/T411596] * [[m:Special:GlobalWatchlist|La Liste de Surveillance Globale]] est une [[mw:Special:MyLanguage/Extension:GlobalWatchlist|extension]] de MediaWiki qui vous permet de voir vos listes de surveillance provenant de différents wikis sur la même page. Il a récemment été mis à jour pour ressembler davantage à la [[Special:Watchlist|Liste de surveillance]] régulière, par exemple en le préparant pour les comptes temporaires dans le masquage IP (y compris le réacheminement des liens des utilisateurs vers les pages de contributions), en mettant les titres de page en gras et en ouvrant les liens dans les résumés d'édition et les balises dans de nouveaux onglets du navigateur. [https://phabricator.wikimedia.org/T398361][https://phabricator.wikimedia.org/T298919][https://phabricator.wikimedia.org/T273526][https://phabricator.wikimedia.org/T286309] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:28|la tâche soumise|les {{formatnum:28}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:28||s}} la semaine dernière]]. Par exemple, le problème selon lequel les blocs globaux ne disposaient pas de l'option permettant de désactiver l'envoi d'e-mails a maintenant été résolu et sera disponible à l'utilisation à partir de la semaine du 13 janvier. [https://phabricator.wikimedia.org/T401293] '''Actualités pour la contribution technique''' * L'[[mw:Special:MyLanguage/VisualEditor/Citation tool|outil de citation VisualEditor]] et les [[mw:Special:MyLanguage/Help:Reference Previews|Aperçus de référence]] prennent désormais en charge "carte" comme type de référence. [https://phabricator.wikimedia.org/T411083] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.46/wmf.10|MediaWiki]]/[[mw:MediaWiki 1.46/wmf.11|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/03|Traduire]]&nbsp;• [[m:Tech|Obtenir de l’aide]]&nbsp;• [[m:Talk:Tech/News|Donner son avis]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' </div><section end="technews-2026-W03"/> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 12 janvier 2026 à 20:33 (CET) <!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29907192 --> == Thank You for Last Year – Join Wiki Loves Ramadan 2026 == Dear Wikimedia communities, We hope you are doing well, and we wish you a happy New Year. ''Last year, we captured light. This year, we’ll capture legacy.'' In 2025, communities around the world shared the glow of Ramadan nights and the warmth of collective iftars. In 2026, ''Wiki Loves Ramadan'' is expanding, bringing more stories, more cultures, and deeper global connections across Wikimedia projects. We invite you to explore the ''Wiki Loves Ramadan 2026'' [[m:Special:MyLanguage/Wiki Loves Ramadan 2026|Meta page]] to learn how you can participate and [[m:Special:MyLanguage/Wiki Loves Ramadan 2026/Participating communities|sign up]] your community. 📷 ''Photo campaign on '' [[c:Special:MyLanguage/Commons:Wiki Loves Ramadan 2026|Wikimedia Commons]] If you have questions about the project, please refer to the FAQs: * [[m:Special:MyLanguage/Wiki Loves Ramadan/FAQ/|Meta-Wiki]] * [[c:Special:MyLanguage/Commons:Wiki Loves Ramadan/FAQ|Wikimedia Commons]] ''Early registration for updates is now open via the '''[[m:Special:RegisterForEvent/2710|Event page]]''''' ''Stay connected and receive updates:'' * [https://t.me/WikiLovesRamadan Telegram channel] * [https://lists.wikimedia.org/postorius/lists/wikilovesramadan.lists.wikimedia.org/ Mailing list] We look forward to collaborating with you and your community. '''The Wiki Loves Ramadan 2026 Organizing Team''' 16 janvier 2026 à 20:44 (CET) <!-- Message envoyé par User:ZI Jony@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Non-Technical_Village_Pumps_distribution_list&oldid=29879549 --> == <span lang="en" dir="ltr">Tech News: 2026-04</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W04"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/04|Translations]] are available. '''Updates for editors''' * The tray shown on [[Special:Diff|Special:Diff]] in mobile view has been redesigned. It is now collapsed by default, and incorporates a link to undo the edit being viewed, making it easier for mobile editors and reviewers to take action while keeping the interface uncluttered. [https://phabricator.wikimedia.org/T402297] * [[m:Special:GlobalWatchlist|The Global Watchlist]] lets you view your watchlists from multiple wikis on one page. The [[mw:Special:MyLanguage/Extension:GlobalWatchlist|extension]] continues to improve — it now automatically determines the text direction (ensuring correct display of sites with unusual domain names) and shows detailed descriptions for log actions. Later this week, a new permanent link for page creations and CSS classes for each entry element will be added. [https://phabricator.wikimedia.org/T412505][https://phabricator.wikimedia.org/T287929][https://phabricator.wikimedia.org/T262768][https://phabricator.wikimedia.org/T414135] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:32}} community-submitted {{PLURAL:32|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the previously observed issue in Vector 2022, where anchor link targets were obscured by the sticky header, has now been addressed. [https://phabricator.wikimedia.org/T406114] '''Updates for technical contributors''' * As mentioned in the [[m:Special:MyLanguage/Tech/News/2025/44|October 2025 deprecation announcement]], MediaWiki Interfaces team will begin sunsetting all transform endpoints containing a trailing slash from the MediaWiki REST API the week of January 26. Changes are expected to roll out to all wikis on or before January 30th. All API users currently calling them are encouraged to transition to the non-trailing slash versions. Both endpoint variations can be found, compared, and tested using the [https://test.wikipedia.org/wiki/Special:RestSandbox REST Sandbox]. If you have questions or encounter any problems, please file a ticket in Phabricator to the [https://phabricator.wikimedia.org/project/view/6931/ #MW-Interfaces-Team board]. * Interactive reference documentation for the [[mw:Special:MyLanguage/Wikimedia REST API|Wikimedia REST API]] has moved. Requests to API docs previously hosted through [[mw:Special:MyLanguage/RESTBase|RESTBase]] (e.g.: <code dir=ltr>https://en.wikipedia.org/api/rest_v1/</code>) are now redirected to the [[w:en:Special:RestSandbox|REST Sandbox]]. * The [[mw:Special:MyLanguage/Wikidata Platform|WMF Wikidata Platform team]] (WDP) has published its [[d:Special:MyLanguage/Wikidata:Wikidata Platform team/Newsletter|January 2026 newsletter]]. It includes updates on the legacy full-graph endpoint decommissioning, the User-Agent policy change, the monthly Blazegraph migration office hours, and efforts to reduce regressions caused by the legacy endpoint shutdown. As a reminder, you can [[m:Special:MyLanguage/Global message delivery/Targets/WDP team updates|subscribe to the WDP newsletter]]! * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.12|MediaWiki]] '''Meetings and events''' * The [[mw:Wikimedia Hackathon Northwestern Europe 2026|Wikimedia Hackathon Northwestern Europe 2026]] will take place on 13-14 March 2026 in Arnhem, the Netherlands. Applications opened mid-December and will close soon or when capacity is reached. It's a two-day, technically oriented hackathon bringing together Wikimedians from the region. Hope to see you there! '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/04|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W04"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 19 janvier 2026 à 21:29 (CET) <!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29943403 --> == Révision annuelle du code universel de conduite et des lignes directrices de l'application == <section begin="announcement-content" /> Nous vous informons que la période de relecture annuelle du Code de conduite universel et des règles d'applications est actuellement ouverte. Vous pouvez faire vos commentaires sur les modifications que vous souhaitez apporter jusqu'au 9 février 2026. C'est la première d'une série d'étapes nécessaires pour la révision annuelle. Vous trouverez [[m:Special:MyLanguage/Universal Code of Conduct/Annual review/2026|d'autres informations et les discussions auxquelles participer sur la page UCoC de Meta]]. Le [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee|Comité de coordination du code universel de conduite]] (U4C &mdash; Universal Code of Conduct Coordinating Committee) est un groupe global dont le rôle est de fournir une implémentation équitable et cohérente de l'UCoC. Cette relecture annuelle a été envisagée et mise en place par l'U4C. Pour plus d'informations et les responsabilités de l'U4C, veuillez lire la [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter|Charte de l'U4C]]. Veuillez partager ces informations avec les autres membres concernés de votre communauté. -- En coopération avec l'U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|discussion]])<section end="announcement-content" /> 19 janvier 2026 à 22:01 (CET) <!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=29905753 --> == Actualités techniques n° 2026-05 == <section begin="technews-2026-W05"/><div class="plainlinks"> Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2026/05|D’autres traductions]] sont disponibles. '''Actualités pour la contribution''' * La Fondation Wikimedia invite à donner des commentaires sur [[m:Special:MyLanguage/Product and Technology Advisory Council/Year1 Reflections and Proposed Way Forward 2026 Update|l’avenir proposé]] du [[:m:Special:MyLanguage/Product and Technology Advisory Council|Conseil consultatif des produits et technologies]] jusqu’au 28 février. * Tous les utilisateurs disposant d'un compte enregistré peuvent désormais utiliser des clés d'accès pour la [[m:Special:MyLanguage/Help:Two-factor authentication|double authentification]] (2FA). Les clés d'accès sont un moyen simple de se connecter sans utiliser un second appareil. Elles vérifient l'identité de l'utilisateur à l'aide d'une empreinte digitale, d'une reconnaissance faciale ou d'un code PIN. Pour configurer une clé d'accès, configurez d'abord une méthode 2FA classique. Actuellement, pour se connecter avec une clé d'accès, les utilisateurs doivent également utiliser un mot de passe. Plus tard ce trimestre, la connexion sans mot de passe permettra aux utilisateurs de se connecter d'un simple clic avec une clé d'accès. Les utilisateurs disposant de droits avancés devront également avoir la 2FA activée. Cela fait partie du projet [[mw:Special:MyLanguage/Product Safety and Integrity/Account Security|Sécurité du compte]]. * Les contributeurs non enregistrés sur des IP bloquées ou des plages d'IP bloquées peuvent désormais interagir sur le wiki pour faire appel d'un blocage en créant un compte temporaire afin de contester un blocage sur la page de discussion de l'utilisateur, sauf si l'option « empêcher cet utilisateur de modifier sa propre page de discussion » est activée. Cela résout le problème des utilisateurs déconnectés incapables d'utiliser le processus de déblocage par défaut via la page de discussion de l'utilisateur. [https://phabricator.wikimedia.org/T398673] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:20|la tâche soumise|les {{formatnum:20}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:20||s}} la semaine dernière]]. Par exemple, la description des méthodes d'authentification à deux facteurs (2FA) sur la page de gestion a été mise à jour. Il est désormais plus clair et plus facile pour les utilisateurs à comprendre et à utiliser. [https://phabricator.wikimedia.org/T332385] '''Actualités pour la contribution technique''' * Une nouvelle variable AbuseFilter, <code>account_type</code>, a été ajoutée pour fournir un moyen fiable de déterminer le type de compte créé dans les actions <code>createaccount</code> et <code>autocreateaccount</code>. Dans le cadre de ce changement, la variable <code>accountname</code> a été renommée en <code>account_name</code>, et <code>accountname</code> est désormais obsolète. Les gestionnaires de filtres doivent mettre à jour tous les filtres qui utilisent des vérifications de type de compte codées en dur ou la variable obsolète. [https://phabricator.wikimedia.org/T414049] * Les vignettes d'images demandées dans des tailles non standard, et en utilisant des méthodes non standard telles que les requêtes directes à <code dir=ltr><nowiki>upload.wikimedia.org/…</nowiki></code>, cesseront de fonctionner dans un proche avenir. Ce changement vise à prévenir les abus externes continus par des robots et des aspirateurs web. Certains utilisateurs ayant des CSS/JS personnalisés, les administrateurs d'interface qui peuvent corriger les gadgets et les thèmes locaux, ainsi que les auteurs d'outils, devront mettre à jour leur code pour utiliser des tailles de vignettes standard. [[phab:T414805|Des détails, des liens de recherche et des exemples de correction sont disponibles dans la tâche]]. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.46/wmf.13|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/05|Traduire]]&nbsp;• [[m:Tech|Obtenir de l’aide]]&nbsp;• [[m:Talk:Tech/News|Donner son avis]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' </div><section end="technews-2026-W05"/> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 26 janvier 2026 à 22:17 (CET) <!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29969530 --> == <span lang="en" dir="ltr">Tech News: 2026-06</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W06"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/06|Translations]] are available. '''Updates for editors''' * The "{{int:pageinfo-toolboxlink}}" feature, which gives validating information about a page ([{{fullurl:{{FULLPAGENAME}}|action=info}} example]), now automatically includes a table of contents. If there is a local [[{{ns:8}}:Pageinfo-header]] page created by individual users, it can now be removed. [https://phabricator.wikimedia.org/T363726] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:21}} community-submitted {{PLURAL:21|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, VisualEditor previously added bold or italic formatting inside link descriptions, making the wikicode complex. This has now been fixed. [https://phabricator.wikimedia.org/T409669] '''Updates for technical contributors''' * There was no XML dump on 20 January. Additionally, from now on, dumps will be generated once per month only. [https://phabricator.wikimedia.org/T414389] * The MediaWiki Interfaces team removed support for all transform endpoints containing a trailing slash from the [https://www.mediawiki.org/wiki/Special:MyLanguage/API:REST%20API MediaWiki REST API]. All API users currently calling those endpoints are encouraged to transition to the non-trailing slash versions. If you have questions or encounter any problems, please file a ticket in phabricator to the [https://phabricator.wikimedia.org/project/view/6931/ #MW-Interfaces-Team board]. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.14|MediaWiki]] '''Weekly highlight''' * Users are reminded that the Wikimedia Foundation has shared some guiding questions for the July 2026–June 2027 Annual Plan on [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2026-2027/Product & Technology OKRs|Meta]] and ''[[diffblog:2025/12/10/shaping-wikimedia-foundations-2026-2027-annual-goals-key-questions-for-the-wikimedia-movement/|Diff]]''. These focus on global trends, faster and healthier experimentation, better support for newcomers, strengthening editors and advanced users, improving collaboration across projects, and growing and retaining readership. Feedback and ideas are welcome on the [[m:Talk:Wikimedia Foundation Annual Plan/2026-2027|talk page]]. '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/06|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W06"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 2 février 2026 à 18:43 (CET) <!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30000986 --> == Actualités techniques n° 2026-07 == <section begin="technews-2026-W07"/><div class="plainlinks"> Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2026/07|D’autres traductions]] sont disponibles. '''Actualités pour la contribution''' * [[File:Maki-gift-15.svg|12px|link=|class=skin-invert|Concerne un souhait]] Les contributeurs connectés qui gèrent de grandes ou complexes listes de suivi peuvent désormais organiser et filtrer les pages surveillées de manière à améliorer leurs flux de travail grâce à la nouvelle fonctionnalité [[mw:Special:MyLanguage/Help:Watchlist labels|Étiquettes de liste de suivi]]. En ajoutant des étiquettes personnalisées (par exemple : pages que vous avez créées, pages surveillées pour vandalisme, ou pages de discussion), les utilisateurs peuvent identifier plus rapidement ce qui nécessite une attention, réduire la charge cognitive et répondre plus efficacement. Cela améliore l'utilisabilité de la liste de suivi, en particulier pour les éditeurs très actifs. * Une nouvelle fonctionnalité disponible sur [[Special:Contributions|Special:Contributions]] montre [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts|des comptes temporaires]] qui sont probablement utilisés par la même personne, et rend ainsi le patrouillage moins chronophage. En vérifiant les contributions d'un compte temporaire, les utilisateurs ayant accès aux adresses IP des comptes temporaires peuvent désormais avoir une vue des contributions des comptes temporaires associés. La fonctionnalité recherche toutes les adresses IP associées à un compte temporaire donné pendant la période de conservation des données et affiche toutes les contributions de tous les comptes temporaires ayant utilisé ces adresses IP. [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts#February 2026: Improvements to the patroller tooling|Plus...]] [https://phabricator.wikimedia.org/T415674] * Lorsque les éditeurs prévisualisent une modification de wikitexte, la boîte de rappel indiquant qu'ils ne voient qu'une prévisualisation (qui est affichée en haut) a désormais un fond gris/neutre au lieu d'un fond jaune/d'avertissement. Cela facilite la distinction entre les notes de prévisualisation et les avertissements réels (par exemple, les conflits de modification ou les cibles de redirection problématiques), qui seront désormais affichés dans des boîtes d'avertissement ou d'erreur séparées. [https://phabricator.wikimedia.org/T414742] * La [[m:Special:GlobalWatchlist|Liste de suivi globale]] vous permet de consulter vos listes de suivi provenant de plusieurs wikis sur une seule page. L' [[mw:Special:MyLanguage/Extension:GlobalWatchlist|extension]] continue de s'améliorer — elle prend désormais en charge correctement plus d'un site Wikibase, par exemple à la fois [[d:|Wikidata]] et [[testwikidata:|testwikidata]]. De plus, des problèmes concernant la direction du texte ont été résolus pour les utilisateurs qui préfèrent Wikidata ou d'autres sites Wikibase dans des langues de droite à gauche (RTL). [https://phabricator.wikimedia.org/T415440][https://phabricator.wikimedia.org/T415458] * <span lang="en" dir="ltr" class="mw-content-ltr">The automatic "magic links" for ISBN, RFC, and PMID numbers have been [[mw:Special:MyLanguage/Help:Magic links|deprecated in wikitext since 2021]] due to inflexibility and difficulties with localization. Several wikis have successfully replaced RFC and PMID magic links with equivalent external links, but a template was often required to replace the functionality of the ISBN magic link. There is now a new [[mw:Special:MyLanguage/Help:Magic words#isbn|built-in parser function]] <code dir=ltr><nowiki>{{#isbn}}</nowiki></code> available to replace the basic functionality of the ISBN magic link. This makes it easier for wikis who wish to migrate off of the deprecated magic link functionality to do so.</span> [https://phabricator.wikimedia.org/T145604] * Deux nouveaux wikis ont été créés : ** un {{int:project-localized-name-group-wikipedia}} dans [[d:Q35401|Jju]] ([[w:kaj:|<code>w:kaj:</code>]]) [https://phabricator.wikimedia.org/T413283] ** un {{int:project-localized-name-group-wikipedia}} dans [[d:Q1186896|Nawat]] ([[w:ppl:|<code>w:ppl:</code>]]) [https://phabricator.wikimedia.org/T413273] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:23|la tâche soumise|les {{formatnum:23}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:23||s}} la semaine dernière]]. '''Actualités pour la contribution technique''' * Un nouveau groupe d'utilisateurs global a été créé : [[{{int:grouppage-local-bot}}|{{int:group-local-bot}}]]. Il sera utilisé en interne par le logiciel pour permettre aux robots communautaires de contourner les limites de débit appliquées aux [[w:en:Web_scraping|web scrapers]] abusifs. Les comptes approuvés en tant que robots sur au moins un wiki Wikimedia seront automatiquement ajoutés à ce groupe. Cela ne changera pas les autorisations dont dispose le robot. [https://phabricator.wikimedia.org/T415588] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.46/wmf.15|MediaWiki]] '''Rencontres et évènements''' * La [[mw:Special:MyLanguage/MediaWiki Users and Developers Conference Spring 2026|Conférence des utilisateurs et des développeurs de MediaWiki, Printemps 2026]] se tiendra du 25 au 27 mars à Salt Lake City, États-Unis. Cet événement est organisé par et pour la communauté MediaWiki de tiers. Vous pouvez proposer des sessions et vous inscrire pour y assister. [https://lists.wikimedia.org/hyperkitty/list/wikitech-l@lists.wikimedia.org/thread/AZBWVI46SDEB65PGR5J6E4TYOQQEZXM7/] '''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/07|Traduire]]&nbsp;• [[m:Tech|Obtenir de l’aide]]&nbsp;• [[m:Talk:Tech/News|Donner son avis]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' </div><section end="technews-2026-W07"/> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 10 février 2026 à 00:30 (CET) <!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30026671 --> == Actualités techniques n° 2026-08 == <section begin="technews-2026-W08"/><div class="plainlinks"> Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2026/08|D’autres traductions]] sont disponibles. '''En lumière cette semaine''' * <span class="mw-translate-fuzzy">L'[[mw:Special:MyLanguage/Wikimedia Site Reliability Engineering|équipe SRE]] va procéder au nettoyage d'[[m:Special:MyLanguage/Etherpad|Etherpad]], l'éditeur web open source de documents collaboratifs en temps réel. Tous les blocs-notes seront définitivement supprimés après le 30 avril 2026 – si des projets de migration sont encore en cours à cette date, l'équipe pourra réexaminer la date au cas par cas. Veuillez effectuer des sauvegardes locales de tout contenu que vous souhaitez conserver, car les données supprimées ne pourront pas être récupérées. Ce nettoyage permet de réduire la taille de la base de données et l'empreinte de l'infrastructure. Etherpad continuera de prendre en charge la collaboration en temps réel, mais le stockage à long terme n'est plus assuré. D'autres nettoyages pourront avoir lieu ultérieurement sans préavis.</span> [https://phabricator.wikimedia.org/T415237] '''Actualités pour la contribution''' * L'équipe de Recherche d'Informations lancera une [[mw:Special:MyLanguage/Readers/Information Retrieval/Phase 1|expérimentation sur l'application mobile Android]], afin de tester des fonctionnalités de recherche hybrides capables de gérer à la fois les requêtes sémantiques et par mots-clés. L'amélioration de la recherche sur la plateforme permettra aux lecteurs de trouver plus facilement ce qu'ils cherchent, directement sur Wikipédia. L'expérimentation sera d'abord lancée sur Wikipédia en grec fin février, puis sur les versions anglaise, française et portugaise en mars. [https://diff.wikimedia.org/2026/01/08/semantic-search-making-it-easier-to-find-the-information-readers-want/ En savoir plus] sur le blog ''Diff''. [https://www.mediawiki.org/wiki/Readers/Information_Retrieval] * L'équipe « Croissance des lecteurs » mènera [[mw:Special:MyLanguage/Readers/Reader Growth/WE3.10.2 Mobile Table of Contents|une expérience]] auprès des utilisateurs de la version mobile du site web qui ajoute une table des matières et développe automatiquement toutes les sections des articles, afin de mieux comprendre les problèmes de navigation qu'ils rencontrent. Le test sera disponible sur les versions arabe, chinoise, anglaise, française, indonésienne et vietnamienne de Wikipedia. * Auparavant, les notifications ([[{{ns:8}}:Sitenotice]] et [[{{ns:8}}:Anonnotice]]) du site ne s'affichaient que sur la version ordinateur. Maintenant, elles s'afficheront désormais sur toutes les plateformes. Les utilisateurs mobiles verront ces notifications. Les administrateurs du site doivent être prêts à tester et à corriger les notifications sur les appareils mobiles afin d'éviter toute interférence avec les articles. Pour désactiver ces notifications, les administrateurs d'interface peuvent ajouter <code dir="ltr">#siteNotice { display: none; }</code> à [[{{ns:8}}:Minerva.css]]. [https://phabricator.wikimedia.org/T138572][https://phabricator.wikimedia.org/T416644] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:19|la tâche soumise|les {{formatnum:19}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:19||s}} la semaine dernière]]. Par exemple, un problème concernant la section ''[[Special:RecentChanges|Spécial:Modifications récentes]]'' a été résolu. Auparavant, cliquer sur « Masquer » dans les filtres actifs entraînait la disparition du bouton « Afficher les nouvelles modifications depuis… », alors qu'il aurait dû rester visible. Ce bouton fonctionne désormais correctement. [https://phabricator.wikimedia.org/T406339] '''Actualités pour la contribution technique''' * Une nouvelle documentation est désormais disponible pour aider les rédacteurs à déboguer les fonctionnalités de recherche interne. Elle facilite le dépannage lorsque des pages n'apparaissent pas dans les résultats, lorsque le classement semble inattendu et lorsqu'il est nécessaire d'inspecter le contenu indexé, ce qui permet de mieux comprendre et d'analyser le comportement de la recherche. [[mw:Help:CirrusSearch/Debug|En savoir plus]]. [https://phabricator.wikimedia.org/T411169] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.46/wmf.16|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/08|Traduire]]&nbsp;• [[m:Tech|Obtenir de l’aide]]&nbsp;• [[m:Talk:Tech/News|Donner son avis]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' </div><section end="technews-2026-W08"/> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 16 février 2026 à 20:17 (CET) <!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30086330 --> == <span lang="en" dir="ltr">Tech News: 2026-09</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W09"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/09|Translations]] are available. '''Weekly highlight''' * [[mw:Special:MyLanguage/Edit check/Reference Check|Reference Check]] has been deployed to English Wikipedia, completing its rollout across all Wikipedias. The feature prompts newcomers to add a citation before publishing new content, helping reduce common citation-related reverts and improve verifiability. In A/B testing, the impact was substantial: newcomers shown Reference Check were approximately 2.2 times more likely to include a reference on desktop and about 17.5 times more likely on mobile web. [https://analytics.wikimedia.org/published/reports/editing/reference_check_ab_test_report_final_2025.html] '''Updates for editors''' * The [[mw:Special:MyLanguage/Extension:InterwikiSorting|InterwikiSorting extension]], which allowed for the [[m:Special:MyLanguage/Interwiki sorting order|sorting of interwiki links]], has been undeployed from Wikipedia. As a result, editors who had enabled interwiki link sorting in non-compact mode (full list format) will now see links reordered. The links moving forward will be listed in the alphabetical order of language code. [https://phabricator.wikimedia.org/T253764] * Later this week, people who are editing a page-section using the mobile visual editor, will notice a new "Edit full page" button. When tapped, you will be able to edit the entire article. This helps when the change you want to make is outside the section you initially opened. [https://phabricator.wikimedia.org/T387175][https://phabricator.wikimedia.org/T409112] * [[mw:Special:MyLanguage/Readers/Reader Experience|The Reader Experience team]] is inviting editors to assess whether dark mode should still be considered "beta" on their wiki, based on their experience of how well it functions on desktop and mobile. If the feature is deemed mature, editors can update the interface messages in <code dir=ltr>MediaWiki:skin-theme-description</code> and <code dir=ltr>MediaWiki:Vector-night-mode-beta-tag</code> to indicate that dark mode is ready and no longer considered beta. * The improved [[mw:Wikimedia_Apps/Team/iOS/Activity_Tab|Activity tab]] which displays user-insights is now available to all users of the Wikipedia iOS app (version 7.9.0 and later). Following earlier A/B testing that showed higher account creation among users with access to the feature, it has been rolled out to 100% of users along with some updates. The Activity tab now shows your edited articles in the timeline, offers editing impact insights like contribution counts and article view trends, and customization options to improve in-app experience for users. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:21}} community-submitted {{PLURAL:21|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, a bug that prevented [[mw:Special:MyLanguage/Extension:DiscussionTools|DiscussionTools]] from working on mobile has now been fixed, restoring full functionality. [https://phabricator.wikimedia.org/T415303] '''Updates for technical contributors''' * The [[m:Special:GlobalWatchlist|Global Watchlist]] lets you view your watchlists from multiple wikis on one page. The [[mw:Special:MyLanguage/Extension:GlobalWatchlist|extension]] that makes this possible continues to improve. The latest upgrade is the inclusion of a [[mw:Extension:GlobalWatchlist#hook|new hook]], <code dir=ltr>ext.globalwatchlist.rebuild</code>, which fires after each watchlist rebuild. This allows you to run gadgets and user scripts for the Special page. [https://phabricator.wikimedia.org/T275159] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.17|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/09|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W09"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 23 février 2026 à 20:03 (CET) <!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30119102 --> == Actualités techniques n° 2026-10 == <section begin="technews-2026-W10"/><div class="plainlinks"> Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2026/10|D’autres traductions]] sont disponibles. '''En lumière cette semaine''' * Le [[m:Special:MyLanguage/Wikipedia 25/Easter egg experiments|mode Anniversaire]] Wikipedia 25 est maintenant disponible sur Wikipédia en français, anglais, betawi, breton, chinois, espagnol, gorontalo, indonésien, italien, luxembourgeois, madurais, néerlandais, sicilien, tchèque, thaï et vietnamien ! Cette campagne à temps limitée célèbre 25 ans de Wikipédia avec une mascotte : « Baby Globe », disponible sous la forme d'un réglage. Lorsque ce réglage est activé, Baby Globe est montrée sur [[m:Special:MyLanguage/Wikipedia 25/Easter egg experiments/article configuration|environ 2 500 articles]], attendant d'être découverte par des lecteurs. Chaque communauté peut choisir d'activer le mode Anniversaire par consensus et en demandant à un administrateur de le rendre disponible et de le personaliser via une [[m:Special:MyLanguage/Wikipedia 25/Easter egg experiments#Community Configuration Demo|configuration]] sur le wiki local. '''Actualités pour la contribution''' * Le [[:m:Special:MyLanguage/WMDE Technical Wishes/Sub-referencing|sous-référencement]], une nouvelle fonctionalité pour réutiliser des références avec des détails différents est maintenant disponible sur Wikipédia en suédois, polonais et [[:phab:T418209|quelques autres]]. Vous pouvez [[:m:Special:MyLanguage/WMDE Technical Wishes/Sub-referencing#test|essayer la fonctionalité]] sur ces projets ou sur testwiki et [https://en.wikipedia.beta.wmcloud.org/wiki/Sub-referencing betawiki]. Les retours des premiers essais sur Wikipédia en allemand ont été [[:m:Special:MyLanguage/WMDE Technical Wishes/Sub-referencing/Learnings|publiés dans un rapport]]. Contactez l'équipe de Wikimédia Allemagne si vous êtes [[:m:Talk:WMDE Technical Wishes/Sub-referencing#Pilot wikis|intéressés pour devenir un wiki pilote]]. * La [[mw:Special:MyLanguage/Help:Edit check#Paste check|vérification du collage clavier]] sera disponible sur tous les Wikipédias cette semaine. Cette fonctionalité avertit les nouveaux contributeurs qui collent du texte qu'ils n'ont probablement pas écrit de vérifier si laisser celui-ci risque de causer une violation du droit d'auteur. La vérification du collage clavier [[mw:Special:MyLanguage/Edit check/Tags|marque]] toutes les modifications où l'avertissement a été montré pour permettre leur vérification. Les administrateurs locaux peuvent configurer les différents aspects de cette fonctionalité à travers [[{{#special:EditChecks}}]]. Des [[mw:Special:MyLanguage/Edit check/Paste Check#A/B Experiment|études]] sur 22 wikis ont montré que cette vérification permet une réduction de 18% des annulations comparé au groupe de contrôle. Les traducteurs peuvent [https://translatewiki.net/w/i.php?title=Special%3ATranslate&group=ext-visualeditor-ve-mw-editcheck&filter=&optional=1&action=translate aider à traduire] cette fonctionalité. * <span lang="en" dir="ltr" class="mw-content-ltr">The [[mw:Special:MyLanguage/Readers/Reader Experience|Reader Experience team]] will be standardizing the user menu in the top right for all mobile users so that it is closer to the desktop experience. Currently this user menu is only visible to users with Advanced Mobile Controls (AMC) turned on. The only change is that a couple buttons previously in the left-side menu will move to the top right for users who do not have AMC turned on. This change is expected to go out March 9 and seeks to improve the user interface.</span> [https://phabricator.wikimedia.org/T413912] * À partir de la semaine du 2 mars, les emails envoyés lorsqu'une adresse email a été ajoutée, supprimée ou changée pour un compte changera pour adopter un formattage HTML beaucoup plus agréable et plus clair que le texte brut précédent. [https://phabricator.wikimedia.org/T410807] * Les notifications sont actuellement limitées à 2 000 entrées historiques par utilisateur et remontent à 2013 lorsque la fonctionnalité a été publiée. Le système va être modifié pour ne stocker que les notifications des 5 dernières années, mais jusqu'à 10 000 d'entre elles. Cela contribuera à la santé à long terme des infrastructures et à empêcher que les notifications plus récentes disparaissent trop tôt. [https://phabricator.wikimedia.org/T383948] * <span lang="en" dir="ltr" class="mw-content-ltr">The [[m:Special:GlobalWatchlist|Global Watchlist]] which lets you view your watchlists from multiple wikis on a single page continues to see improvements. The latest update improves label usage experience. The [[mw:Special:MyLanguage/Extension:GlobalWatchlist|extension]] now allows activating the [[mw:Special:MyLanguage/Manual:Language#Fallback languages|language fallback system]] for Wikidata items without labels in the viewed language, and showing those labels in the user’s preferred Wikidata language if no <code dir=ltr>uselang=</code> URL parameter is provided.</span> [https://phabricator.wikimedia.org/T373686][https://phabricator.wikimedia.org/T416111] * L'équipe Wikipédia Android a commencé un test beta de la [[mw:Special:MyLanguage/Readers/Information Retrieval/Phase 1|recherche hybride]] sur Wikipédia en grec. Cette recherche hybride supporte les requêtes sémantique et par mot clés, permettant aux utilisateurs de trouver ce qu'ils cherchent plus facilement. * Pour des raisons de sécurité, les membres de certains groupes sont [[m:Special:MyLanguage/Mandatory two-factor authentication for users with some extended rights|forcés d'avoir la double authentification]] (A2F) d'activée. Actuellement, l'A2F n'est nécessaire que pour utiliser les droits du groupe, et non pour en faire partie. Vu que ce système admet certaines failles, il sera [[phab:T418580|changé graduellement en mars]]. Les membres de ces groupes ne pourront plus désactiver la dernière méthose d'A2F sur leur compte, et il sera impossible d'ajouter des utilisateurs sans A2F à ces groupes. Il sera toujours possible de rajouter d'autres méthodes d'authentification et d'en enlever, tant qu'une est toujours activée. Dans la seconde moitié de mars, les utilisateurs sans A2F seront retirés de ces groupes. Cela s'applique aux administrateurs CentralNotice, aux vérificateurs d'utilisateurs, aux administrateurs d'interface, aux masqueurs, aux staff de Wikidata et Wikifonctions ainsi qu'aux bureaux IT et Confiance et sécurité de la WMF. Rien ne changera pour les autres utilisateurs. Voir la tâche liée pour le calendrier de déploiement. [https://phabricator.wikimedia.org/T418580] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:27|la tâche soumise|les {{formatnum:27}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:27||s}} la semaine dernière]]. Par exemple, le problème empêchant les utilisateurs de créer une instance dans [https://www.wikibase.cloud/ Wikibase.cloud] a maintenant été résolu. [https://phabricator.wikimedia.org/T416807] '''Actualités pour la contribution technique''' * <span lang="en" dir="ltr" class="mw-content-ltr">To help ensure [[mw:Special:MyLanguage/MediaWiki Product Insights/Responsible Reuse|fair use of infrastructure]], over the next month the Wikimedia Foundation will implement global API rate limits across our APIs. In early March, stricter limits will be applied to unidentified requests from outside Toolforge/WMCS and API requests that are made from web browsers. In April, higher limits will be applied to identified traffic. These limits are intentionally set as high as possible to minimise impact on the community. Bots running in Toolforge/WMCS or with the bot user right on any wiki should not be affected for now. However, all developers are advised to follow updated best practices. For more information, see [[mw:Special:MyLanguage/Wikimedia APIs/Rate limits|Wikimedia APIs/Rate limits]].</span> * <span lang="en" dir="ltr" class="mw-content-ltr">The Wikidata Query Service Linked Data Fragment (LDF) endpoint will be decommissioned in February. This endpoint served limited traffic, which was successfully migrated to other data access methods that were better suited to support existing use cases. The hardware used to support the LDF endpoint will be reallocated to support the ongoing backend migration efforts.</span> [https://phabricator.wikimedia.org/T415696] * Le nouvel analyseur syntaxique Parsoid [[mw:Special:MyLanguage/Parsoid/Parser Unification/Updates|continue d'être déployés sur plus de wikis]], améliorant la pérennité de la platforme et rendant plus facile l'ajout de nouvelles fonctionalités de lecture et de modification. Parsoid est maintenant l'analyseur par défaut sur 488 wikis de la WMF (268 Wikipédias), couvrant plus de 10% de toutes les lectures de pages Wikipédia. * Le processus et les critères pour [[Special:MyLanguage/Wikimedia Enterprise#Access|demander un accès exceptionnel]] au flux à fort volume de l'API ''Wikimédia Entreprise'' (sans coût pour des utilisations en rapport à notre mission) [[m:Talk:Wikimedia Enterprise#Exceptional access criteria|ont maintenant été publiés]]. Notre but est de donner une documentation plus claire et plus complète aux utilisateurs. * [https://techblog.wikimedia.org/ Le blog Tech], dédié à la communité technique de Wikimédia [https://techblog.wikimedia.org/2026/02/24/a-tech-blog-diff/ va migrer] vers [[diffblog:|Diff]], le blog pour les nouvelles et événements de la communauté. La migration devrait être terminée en Avril 2026, après quoi les nouveaux posts seront acceptés pour être publiés. Les lecteurs pourront lire les posts - anciens ou nouveaux - sur https://diff.wikimedia.org/. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.46/wmf.18|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/10|Traduire]]&nbsp;• [[m:Tech|Obtenir de l’aide]]&nbsp;• [[m:Talk:Tech/News|Donner son avis]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' </div><section end="technews-2026-W10"/> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 2 mars 2026 à 18:51 (CET) <!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30137798 --> == <span lang="en" dir="ltr">Tech News: 2026-11</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W11"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/11|Translations]] are available. '''Weekly highlight''' * [[m:Special:MyLanguage/Tech/Server switch|All wikis will be read-only]] for a few minutes on Wednesday, 25 March 2026 at [https://zonestamp.toolforge.org/1774450800 15:00 UTC]. This is for the datacenter server switchover backup tests, [[wikitech:Deployments/Yearly calendar|which happen twice a year]]. During the switchover, all Wikimedia website traffic is shifted from one primary data center to the backup data center to test availability and prevent service disruption even in emergencies. * Last week, all wikis had 2 hours of read-only time, and extended unavailability for user-scripts and gadgets. This was due to a security incident which has since been resolved. Work is ongoing to prevent re-occurrences. For current information please see the [[m:Steward's noticeboard#Statement on Meta about today's user script security incident|post on the Stewards' noticeboard]] ([[m:Special:MyLanguage/Wikimedia Foundation/Product and Technology/Product Safety and Integrity/March 2026 User Script Incident|translations]]). '''Updates for editors''' * Users facing multiple blocks on mobile will now see the reasons for each block separately, instead of a generic message. This helps them understand why they are blocked and what steps they can take to resolve the issue. For example, users affected for using common VPNs (such as [[Special:MyLanguage/Apple iCloud Private Relay|iCloud Private Relay]]) will receive clearer guidance on what they need to do to start editing again. [https://phabricator.wikimedia.org/T357118] * Later this week, [[mw:Special:MyLanguage/VisualEditor/Suggestion Mode|Suggestion Mode]] will become available as a beta feature within the visual editor at all Wikipedias. This feature proactively suggests various types of actions that people can consider taking to improve Wikipedia articles, and learn about related guidelines. The feature is locally configurable, and can also be locally expanded with custom Suggestions. Current settings can be seen at [[Special:EditChecks]] and there are [[mw:Special:MyLanguage/Help:Suggestion mode#For administrators %E2%80%93 local customization|instructions for how administrators can customize]] the links to point to local guidelines. The feature is connected to [[mw:Special:MyLanguage/Help:Edit check|Edit check]] which suggests improvements while someone is writing new content. In the future, the Editing team plans to evaluate the feature's impact with newcomers through a controlled experiment. [https://phabricator.wikimedia.org/T404600] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:23}} community-submitted {{PLURAL:23|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the issue where the cursor became misaligned during the use of CodeMirror’s syntax highlighting, which makes wikitext and code easier to read, has now been fixed. This problem specifically affected users who defined a font rule in a custom stylesheet while creating a new topic with DiscussionTools. [https://phabricator.wikimedia.org/T418793] '''Updates for technical contributors''' * API rate limiting update: To help ensure [[mw:Special:MyLanguage/MediaWiki Product Insights/Responsible Reuse|fair use of infrastructure]], global API rate limits will be applied this week to requests without a compliant User-Agent that originate from outside Toolforge/WMCS and to unauthenticated requests made from web browsers. Higher limits will be applied to identified traffic in April. Bots running in Toolforge/WMCS or with the bot user right on any wiki should not be affected for now. However, all developers are advised to follow updated best practices. For more information, see [[mw:Special:MyLanguage/Wikimedia APIs/Rate limits|Wikimedia APIs/Rate limits]]. * The new GraphQL API has been released. The API was developed as a flexible alternative to select features of the Wikidata Query Service (WDQS), to improve developer experience and foster adaptability, and efficient data access. Try it out and [[d:Wikidata:Wikibase GraphQL#Feedback and development|give feedback]]. You can also [https://greatquestion.co/wikimediadeutschland/GraphQLAPI/apply sign up for usability tests]. * The [[m:Special:MyLanguage/Product and Technology Advisory Council/Unsupported Tools Working Group|PTAC Unsupported Tools Working Group]] continued improvements to [[commons:Special:MyLanguage/Commons:Video2commons#|Video2Commons]] in February, with fixes addressing authentication errors, large-file handling, task queue visibility, and clearer upload behavior. Work is still ongoing in some areas, including changes related to deprecated server-side uploads. Read [[m:Special:MyLanguage/Product and Technology Advisory Council/Unsupported Tools Working Group#February 2026|this update]] to learn more. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.19|MediaWiki]] '''In depth''' * The Article Guidance team invites experienced Wikipedia editors from selected [[mw:Special:MyLanguage/Article guidance/Pilot wikis and collaborators#Collaborators|pilot wikis]] and interested contributors from other Wikipedias to fill out this questionnaire which is available in [https://docs.google.com/forms/d/e/1FAIpQLSfmLeVWnxmsCbPoI_UF2jyRcn73WRGWCVPHzerXb4Cz97X_Ag/viewform English], [https://docs.google.com/forms/d/e/1FAIpQLSd6rzr4XXQw8r4024fE3geTPFe13M_6w7Mitj-YJi0sOlWTAw/viewform?usp=header Arabic], [https://docs.google.com/forms/d/e/1FAIpQLSdok3-RfB18lcugYTUMGkpwmqG_8p760Wv4dCXitOXOszjUDw/viewform?usp=header Bengali], [https://docs.google.com/forms/d/e/1FAIpQLSfjTfYp4jEo0akA4B1e-Nfg3QZPCudUjhJzHzzDi6AHyAaMGA/viewform?usp=header Japanese], [https://docs.google.com/forms/d/e/1FAIpQLScteVoI29Aue4xc72dekk-6RYtvmMgQxzMI900UOawrFrSTWg/viewform?usp=header Portuguese], [https://docs.google.com/forms/d/e/1FAIpQLSetdxnYwL3ub2vqA7awCg5hJZPMIYcDPaiTe12rY9h0GYnVlw/viewform?usp=header Persian], and [https://docs.google.com/forms/d/e/1FAIpQLScNvfJF-Ot-4pzA4qAN771_0QDJ4Li19YcUsaTgSKW8Nc7U_Q/viewform?usp=header Turkish]. Your answers will help the team customize guidance for less experienced editors and help them learn community policies and practices while creating an article. Learn more [[mw:Special:MyLanguage/Article guidance|on the project page]]. '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/11|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W11"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 9 mars 2026 à 19:52 (CET) <!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30213008 --> == <span lang="en" dir="ltr">Tech News: 2026-12</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W12"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/12|Translations]] are available. '''Updates for editors''' * The [[mw:Special:MyLanguage/Help:Extension:CodeMirror|{{int:codemirror-beta-feature-title}}]] beta feature, also known as [[mw:Special:MyLanguage/Extension:CodeMirror|CodeMirror 6]], has been used for wikitext syntax highlighting since November 2024. It will be promoted out of beta by May 2026 in order to bring improvements and new [[mw:Special:MyLanguage/Help:Extension:CodeMirror#Features|features]] to all editors who use the standard syntax highlighter. If you have any questions or concerns about promoting the feature out of beta, [[mw:Special:MyLanguage/Help talk:Extension:CodeMirror|please share]]. [https://phabricator.wikimedia.org/T259059] * Some changes to local user groups are performed by stewards on Meta-Wiki and logged there only. Now, interwiki rights changes will be logged both on Meta-Wiki and the wiki of the target user to make it easier to access a full record of user's rights changes on a local wiki. Past log entries for such changes will be backfilled in the coming weeks. [https://phabricator.wikimedia.org/T6055] * On wikis using [[m:Special:MyLanguage/Flagged Revisions|Flagged Revisions]], the number of pending changes shown on [[{{#Special:PendingChanges}}]] previously counted pages which were no longer pending review, because they have been removed from the system without being reviewed, e.g. due to being deleted, moved to a different namespace, or due to wiki configuration changes. The count will be correct now. On some wikis the number shown will be much smaller than before. There should be no change to the list of pages itself. [https://phabricator.wikimedia.org/T413016] * Wikifunctions composition language has been rewritten, resulting in a new version of the language. This change aims to increase service stability by reducing the orchestrator's memory consumption. This rewrite also enables substantial latency reduction, code simplification, and better abstractions, which will open the door to later feature additions. Read more about [[f:Special:MyLanguage/Wikifunctions:Status updates/2026-03-11|the changes]]. * Users can now sort search results alphabetically by page title. The update gives an additional option to finding pages more easily and quickly. Previously, results could be sorted by Edit date, Creation date, or Relevance. To use the new option, open 'Advanced Search' on the search results page and select 'Alphabetically' under 'Sorting Order'. [https://phabricator.wikimedia.org/T403775] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:28}} community-submitted {{PLURAL:28|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the bug that prevented UploadWizard on Wikimedia Commons from importing files from Flickr has now been fixed. [https://phabricator.wikimedia.org/T419263] '''Updates for technical contributors''' * A new special page, [[{{#special:LintTemplateErrors}}]], has been created to list transcluded pages that are flagged as containing lint errors to help users discover them easily. The list is sorted by the number of transclusions with errors. For example: [[{{#special:LintTemplateErrors}}/night-mode-unaware-background-color]]. [https://phabricator.wikimedia.org/T170874] * Users of the [[mw:Special:MyLanguage/Help:Extension:CodeMirror|{{int:codemirror-beta-feature-title}}]] beta feature have been using [[mw:Special:MyLanguage/Extension:CodeMirror|CodeMirror]] instead of [[mw:Special:MyLanguage/Extension:CodeEditor|CodeEditor]] for syntax highlighting when editing JavaScript, CSS, JSON, Vue and Lua content pages, for some time now. Along with promoting CodeMirror 6 out of beta, the plan is to replace CodeEditor as the standard editor for these content models by May 2026. [[mw:Special:MyLanguage/Help talk:Extension:CodeMirror|Feedback or concerns are welcome]]. [https://phabricator.wikimedia.org/T419332] * The [[mw:Special:MyLanguage/Extension:CodeMirror|CodeMirror]] JavaScript modules will soon be upgraded to CodeMirror 6. Leading up to the upgrade, loading the <code dir=ltr>ext.CodeMirror</code> or <code dir=ltr>ext.CodeMirror.lib</code> modules from gadgets and user scripts was deprecated in July 2025. The use of the <code dir=ltr>ext.CodeMirror.switch</code> hook was also deprecated in March 2025. Contributors can now make their scripts or gadgets compatible with CodeMirror 6. See the [[mw:Special:MyLanguage/Extension:CodeMirror#Gadgets and user scripts|migration guide]] for more information. [https://phabricator.wikimedia.org/T373720] * The MediaWiki Interfaces team is expanding coverage of REST API module definitions to include [[mw:Special:MyLanguage/API:REST API/Extensions|extension APIs]]. REST API modules are groups of related endpoints that can be independently managed and versioned. Modules now exist for [https://phabricator.wikimedia.org/T414470 GrowthExperiments] and [https://phabricator.wikimedia.org/T419053 Wikifunctions] APIs. As we migrate extension APIs to this structure, documentation will move out of the main MediaWiki OpenAPI spec and REST Sandbox view, and will instead be accessible via module-specific options in the dropdown on the [https://test.wikipedia.org/wiki/Special:RestSandbox REST Sandbox] (i.e., [[{{#Special:RestSandbox}}]], available on all wiki projects). * The [[mw:Special:MyLanguage/Extension:Scribunto|Scribunto]] extension provides different pieces of information about the wiki where the module is being used via the [[mw:Special:MyLanguage/Extension:Scribunto/Lua reference manual|mw.site]] library. Starting last week, the library also provides a [[mw:Special:MyLanguage/Extension:Scribunto/Lua reference manual#mw.site.wikiId|way]] of accessing the [[mw:Special:MyLanguage/Manual:Wiki ID|wiki ID]] that can be used to facilitate cross-wiki module maintenance. [https://phabricator.wikimedia.org/T146616] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.20|MediaWiki]] '''In depth''' * The [[m:Special:MyLanguage/Coolest Tool Award|2026 Coolest Tool Award]] celebrating outstanding community tools, is now open for nominations! Nominate your favorite tool using the [https://wikimediafoundation.limesurvey.net/435684?lang=en nomination survey] form by 23 March 2026. For more information on privacy and data handling, please see the [[foundation:Special:MyLanguage/Legal:Coolest_Tool_Award_2026_Survey_Privacy_Statement|survey privacy statement]]. '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/12|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W12"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 16 mars 2026 à 20:35 (CET) <!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30260505 --> == <span lang="en" dir="ltr">Upcoming deployment of CampaignEvents extension to Wikibooks</span> == <div lang="en" dir="ltr"> <section begin="message"/> Hello everyone, We are writing to inform you that the [[mw:Help:Extension:CampaignEvents|CampaignEvents extension]] will be deployed to all Wikibooks projects during the week of '''23 March 2026'''. This follows last year’s broader rollout across Wikimedia projects. We realized that Wikibooks was not included at the time, and we’re now addressing that to ensure consistency across all communities. The CampaignEvents extension provides tools to support event and campaign organization on-wiki, including features like on-wiki event registration and collaboration lists(global event list). We welcome any questions, feedback, or concerns you may have. We are also happy to support anyone interested in trying out the tools. ''Apologies if this message is not in your preferred language. If you’re able to help translate it for your community, please feel free to do so.'' <section end="message"/> </div> <bdi lang="en" dir="ltr">[[User:Udehb-WMF|Udehb-WMF]] ([[User talk:Udehb-WMF|discussion]]) 19 mars 2026 à 19:22 (CET)</bdi> <!-- Message envoyé par User:Udehb-WMF@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=User:Udehb-WMF/sandbox/MM_target&oldid=30284073 --> == <span lang="en" dir="ltr">Tech News: 2026-13</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W13"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/13|Translations]] are available. '''Weekly highlight''' * Wikimedia site users can now log in without a password using passkeys. This is a secure method supported by fingerprint, facial recognition, or PIN. With this change, all users who opt for passwordless login will find it easier, faster, and more secure to log in to their accounts using any device. The new passkey login option currently appears as an autofill suggestion in the username field. An additional [[phab:T417120|"Log in with passkey" button]] will soon be available for users who have already registered a passkey. This update will improve security and user experience. The [[c:File:Passwordless_login_screencast.webm|screen recording]] demonstrates the passwordless login process step by step. * [[m:Special:MyLanguage/Tech/Server switch|All wikis will be read-only]] for a few minutes on Wednesday, 25 March 2026 at [https://zonestamp.toolforge.org/1774450800 15:00 UTC]. This is for the datacenter server switchover backup tests, [[wikitech:Deployments/Yearly calendar|which happen twice a year]]. During the switchover, all Wikimedia website traffic is shifted from one primary data center to the backup data center to test availability and prevent service disruption even in emergencies. '''Updates for editors''' * Wikimedia site users can now export their notifications older than 5 years using a [[toolforge:echo-chamber|new Toolforge tool]]. This will ensure that users retain their important notifications and avoid them being lost based on the planned change to delete notifications older than 5 years, as previously announced. [https://phabricator.wikimedia.org/T383948] * Wikipedia editors in Indonesian, Thai, Turkish, and Simple English now have access to Special:PersonalDashboard. This is an [[mw:Special:MyLanguage/Moderator Tools/Dashboard|early version of an experience]] that introduces newer editors to patrolling workflows, making it easier for them to move from making edits to participating in more advanced moderation work on their project. [https://phabricator.wikimedia.org/T402647] * The [[Special:Block]] now has two minor interface changes. Administrators can now easily perform indefinite blocks through a dedicated radio button in the expiry section. Also, choosing an indefinite expiry provides a different set of common reasons to select from, which can be changed at: [[MediaWiki:Ipbreason-indef-dropdown]]. [https://phabricator.wikimedia.org/T401823] * Mobile editors [[mw:Special:MyLanguage/Contributors/Account Creation Experiments#Logged-out|at several wikis]] can now see an improved logged-out edit warning, thanks to the recent updates from the Growth team. These changes released last week are part of ongoing efforts and tests to enhance [[mw:Special:MyLanguage/Contributors/Account Creation Experiments|account creation experience on mobile]] and then increase participation. [https://phabricator.wikimedia.org/T408484] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:36}} community-submitted {{PLURAL:36|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the bug that prevented mobile web users from seeing the block information when affected by multiple blocks has been fixed. They can now see messages of all the blocks currently affecting them when they access Wikipedia. '''Updates for technical contributors''' * Images built using Toolforge will soon get the upgraded buildpacks version, bringing support for newer language versions and other upstream improvements and fixes. If you use Toolforge Build Service, review the recent [https://lists.wikimedia.org/hyperkitty/list/cloud-announce@lists.wikimedia.org/thread/EMYTA32EV2V5SQ2JIEOD2CL66YFIZEKV/ cloud-announce email] and update your build configuration as necessary to ensure your tools are compatible. [https://wikitech.wikimedia.org/w/index.php?title=Help:Toolforge/Building_container_images&oldid=2392097#Buildpack_environment_upgrade_process][https://phabricator.wikimedia.org/T380127] * The [https://api.wikimedia.org/wiki/Main_Page API Portal] documentation wiki will shut down in June 2026. API keys created on the API Portal will continue to work normally. api.wikimedia.org endpoints will be deprecated gradually starting in July 2026. Documentation on the API Portal is moving to [[mw:Wikimedia APIs|mediawiki.org]]. Learn more on the [[wikitech:API Portal/Deprecation|project page]]. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.21|MediaWiki]] '''In depth''' * [[m:Special:MyLanguage/WMDE Technical Wishes|WMDE Technical Wishes]] is considering improvements to [[m:WMDE Technical Wishes/References/VisualEditor automatic reference names|automatically generated reference names in VisualEditor]]. Please check out the [[m:WMDE Technical Wishes/References/VisualEditor automatic reference names#Proposed solutions|proposed solutions]] and participate in the [[m:Talk:WMDE Technical Wishes/References/VisualEditor automatic reference names#Request for comment|request for comment]]. '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/13|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W13"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 23 mars 2026 à 17:51 (CET) <!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30268305 --> == Actualités techniques n° 2026-14 == <section begin="technews-2026-W14"/><div class="plainlinks"> Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2026/14|D’autres traductions]] sont disponibles. '''En lumière cette semaine''' * Le version Beta de [[abstract:|Abstract Wikipedia]], un nouveau projet Wikimédia indépendant du langage, a été lancée la semaine dernière. Ce projet permet aux communautés de construire des articles Wikipédia dans leur langue natale, qui peuvent directement être lus par les autres utilisateurs et utilisatrices dans leur propre langage. Le wiki fonctionne grâce à des instructions de Wikifunctions et au contenu structuré issu de Wikidata. [[:f:Special:MyLanguage/Wikifunctions:Status updates/2026-03-26|En savoir plus]]. '''Actualités pour la contribution''' * L'équipe Croissance mène un test A/B afin d'évaluer l'effet d'un message plus clair et plus convivial encourageant à la création de comptes sur les wikis. Actuellement, lorsqu'un utilisateur mobile non connecté lance la modification, un message d'avertissement s'affiche, pouvant paraître abrupt et décourageant. Il présente également la modification par compte temporaire comme option par défaut, au lieu d'inciter à la création d'un compte. Le test est mené sur dix Wikipédia, dont les versions en arabe, français, espagnol et allemand. [[mw:Special:MyLanguage/Contributors/Account Creation Experiments#2. Improve logged-out warning message (T415160)|En savoir plus]]. * L'équipe des applications Wikimédia sollicite vos commentaires sur [[mw:Special:MyLanguage/Wikimedia Apps/Team/Future of Editing on the Mobile Apps|comment devrait fonctionner l'édition dans les applications mobiles Wikipédia]]. La discussion porte sur l'amélioration de l'accès aux outils d'édition lorsque les utilisateurs appuient sur « Modifier ». Cette initiative s'inscrit dans un effort plus large visant à offrir aux lecteurs intéressés par la contribution une expérience utilisateur plus intuitive. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:45|la tâche soumise|les {{formatnum:45}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:45||s}} la semaine dernière]]. Par exemple, un problème avec la récupération de citations à partir du site d'archive de journaux [https://www.newspapers.com Newspapers.com], qui ne fonctionnait plus en raison d'un blocage des requêtes [[mw:Special:MyLanguage/Citoid|Citoid]], a maintenant été résolu. [https://phabricator.wikimedia.org/T419903] '''Actualités pour la contribution technique''' * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.46/wmf.22|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/14|Traduire]]&nbsp;• [[m:Tech|Obtenir de l’aide]]&nbsp;• [[m:Talk:Tech/News|Donner son avis]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].'' </div><section end="technews-2026-W14"/> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 30 mars 2026 à 21:25 (CEST) <!-- Message envoyé par User:STei (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30329462 --> == Action Required: Update templates/modules for electoral maps (Migrating from P1846 to P14226) == Hello everyone, This is a notice regarding an ongoing data migration on Wikidata that may affect your election-related templates and Lua modules (such as <code>Module:Itemgroup/list</code>). '''The Change:'''<br /> Currently, many templates pull electoral maps from Wikidata using the property [[:d:Property:P1846|P1846]], combined with the qualifier [[:d:Property:P180|P180]]: [[:d:Q19571328|Q19571328]]. We are migrating this data (across roughly 4,000 items) to a newly created, dedicated property: '''[[:d:Property:P14226|P14226]]'''. '''What You Need To Do:'''<br /> To ensure your templates and infoboxes do not break or lose their maps, please update your local code to fetch data from [[:d:Property:P14226|P14226]] instead of the old [[:d:Property:P1846|P1846]] + [[:d:Property:P180|P180]] structure. A [[m:Wikidata/Property Migration: P1846 to P14226/List|list of pages]] was generated using Wikimedia Global Search. '''Deadline:'''<br /> We are temporarily retaining the old data on [[:d:Property:P1846|P1846]] to allow for a smooth transition. However, to complete the data cleanup on Wikidata, the old [[:d:Property:P1846|P1846]] statements will be removed after '''May 1, 2026'''. Please update your modules and templates before this date to prevent any disruption to your wiki's election articles. Let us know if you have any questions or need assistance with the query logic. Thank you for your help! [[User:ZI Jony|ZI Jony]] using [[Utilisateur:MediaWiki message delivery|MediaWiki message delivery]] ([[Discussion utilisateur:MediaWiki message delivery|discussion]]) 3 avril 2026 à 19:11 (CEST) <!-- Message envoyé par User:ZI Jony@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Non-Technical_Village_Pumps_distribution_list&oldid=29941252 --> 3aasrkbqa3cgd535drt8wqg1ykcf2a1