« Les cartes graphiques/Les processeurs de shaders » : différence entre les versions

Contenu supprimé Contenu ajouté
m →‎La hiérarchie mémoire : Déplacement de la section dans un chapitre séparé
typo
Ligne 3 :
==Jeu d'instruction==
 
Les shaders sont souvent écrits dans un langage de haut-niveau : le HLSL pour les shaders Direct X et le GLSL pour les shaders Open Gl. Ils sont traduits (compilés) à la volée par les pilotes de la carte graphique, pour les rendre compatibles avec le processeur de vertex shaders. Au début, ces langages, ainsi que le matériel, supportaient uniquement des programmes simples. Au fil du temps, les spécifications de ces langages sont devenues de plus en plus riches à chaque version de Direct X ou d'Open Gl, et le matériel en a fait autant. Les premiers processeurs de shaders disposaient de peu d'instructions. On trouvait uniquement des instructions de calcul arithmétiques, dont certaines étaient assez complexes (logarithmes, racines carrées, etc). Depuis, d'autres versions de vertex shaders ont vu le jour. Pour résumer, les améliorations ont portéesporté sur :
 
* le nombre de registres ;
Ligne 70 :
|}
 
Comme on le voit, ces instructions sont presque toutes des instructions arithmétiques : multiplications, additions, exponentielles, logarithmes, racines carrées, etc. À cotécôté, on trouve des comparaisons (SDE, SLT), une instruction MOV qui déplace le contenu d'un registre dans un autre, et une instruction de calcul d'adresse. Fait intéressant, toutes ces instructions peuvent s’exécuter en un seul cycle d'horloge. On remarque que parmi toutes ces instructions arithmétiques, la division est absente. Il faut dire que la contrainte qui veut que toutes ces instructions s’exécutent en un cycle d'horloge pose quelques problèmes avec la division, qui est une opération plutôt lourde en hardware. À la place, on trouve l'instruction RCP, capable de calculer 1/x, avec x un flottant. Cela permet ainsi de simuler une division : pour obtenir Y/X, il suffit de calculer 1/X avec RCP, et de multiplier le résultat par Y.
 
S'il n'y avait aucune instruction d'accès à la mémoire sur le processeur de la Geforce 3, la situation a changé depuis : les cartes graphiques récentes peuvent aller lire certaines données depuis la mémoire vidéo. Généralement, les shaders lisent des textures en mémoire vidéo, textures qui servent à colorier les pixels ou à configurer les calculs d'éclairage. Et la plupart des cartes graphiques suivant la Geforce 2 incorporait des instructions de lecture de texture. Les écritures, plus rares, sont venues après, afin de faciliter certaines techniques de rendu dont je ne parlerais pas ici.
Ligne 83 :
====Processeurs VLIW====
 
Sur les processeurs VLIW, les instructions sont regroupées dans ce qu'on appelle des Bundles, des sortes de super-instructions. Les instructions d'un bundle peuvent s'exécuter en parallèle sur différentes unités de calcul, mais le bundle est chargé en une seule fois depuis la mémoire. Chaque instruction d'un faisceau doit expliciter quelle unité de calcul doit la prendre en charge. Vu que chaque instruction sera attribué à une unité de calcul différente, le compilateur peut se débrouiller pour que les instructions dans un bundle soient indépendantes. Mais il se peut que le compilateur ne puisse pas remplir tout le bundle avec des instructions indépendantes. Sur les anciens processeurs VLIW, les bundles étaient de taille fixe, ce qui forcaitforçait le compilateur à remplir d'éventuels vides avec des NOP (des instructions qui ne font rien), diminuant la densité de code. La majorité des processeurs VLIW récents utilise des bundles de longueur variable, supprimant ces NOP.
 
Dans la majorité des cas, ces unités VLIW sont capables de traiter deux instructions arithmétiques en parallèles : une qui sera appliquée aux couleurs R, G, et B, et une autre qui sera appliquée à la couleur de transparence. Cette possibilité s'appelle la '''co-issue'''.
Ligne 89 :
===Jeu de registres===
 
Un processeur de shaders contient beaucoup de registres, sans quoi il ne pourrait pas faire son travail efficacement. Sur les processeurs de vertices des anciennes cartes 3D, il existait un grand nombre de registres spécialisés. Certains registres étaient spécialisés dans le stockage des vertices, d'autres dans le stockage des résultats de calculs, d'autres dans le stockage de constante, etc. Il y avait bien quelques registres généraux, sans fonction préétablie, mais ils étaient secondés par un grand nombre de registres spécialisés assez nombreux. De nos jours, ce n'est plus le cas : tous les registres sont banalisés et peuvent stocker toute donnée utile. Les registres spécialisés ont disparusdisparu. Le fait est que l'usage de registres spécialisés a perdu de son intérêt avec l'unification des ''shaders'', certains registres n'ayant de sens que pour les vertices et rien d'autre.
 
Les registres des processeurs de vertices peuvent se classer en plusieurs types :
Ligne 108 :
Les cartes graphiques sont des architectures massivement parallèles, à savoir qu'elles sont capables de faire un très grand nombre de calculs simultanés, durant le même cycle d'horloge. Il faut dire que chaque vertice ou pixel peut être traité indépendamment des autres, ce qui rend le traitement 3D fortement parallèle. Cela a quelques conséquences sur le nombre de processeurs et d'unités de calcul, ainsi que sur la hiérarchie mémoire. L'architecture doit être conçue pour pouvoir effectuer un maximum de calculs en parallèle, quitte à simplifier fortement la puissance pour des calculs séquentiels (ceux qu'effectue un processeur). La hiérarchie mémoire doit aussi gérer un grand nombre d'accès mémoire simultanés : qui dit plein d'instructions en parallèle qui travaillent sur des données indépendantes dit aussi plein de données indépendantes à lire ou écrire !
 
L'architecture d'une carte grahique récente est illustrée ci-dessous. On y voit plusieurs caractèristiquescaractéristiques typiques : un grand nombre de processeurs/ceurscœurs, un grand nombre d'unités de calculs par coeurcœur, et une hiérarchie mémoire assez étagée avec des mémoires locales en complément de la mémoire vidéo principale.
 
[[File:NVIDIA GPU Accelerator Block Diagram.png|centre|vignette|upright=2.0|Ce schéma illustre l'architecture d'un GPU en utilisant la terminologie NVIDIA. Comme on le voit, la carte graphique contient plusieurs cœurs de processeur distincts. Chacun d'entre eux contient plusieurs unités de calcul généralistes, appelées processeurs de threads, qui s'occupent de calculs simples (en bleu). D'autres calculs plus complexes sont pris en charge par une unité de calcul spécialisée (en rouge). Ces cœurs sont alimentés en instructions par le processeur de commandes, ici appelé ''Thread Execution Control Unit'', qui répartit les différents shaders sur chaque cœur. Enfin, on voit que chaque cœur a accès à une mémoire locale dédiée, en plus d'une mémoire vidéo partagée entre tous les cœurs.]]
Ligne 118 :
Pour profiter au mieux des opportunités de parallélisme, une carte graphique contient de nombreux processeurs, qui eux-mêmes contiennent plusieurs unités de calcul. Savoir combien de cœurs contient une carte graphique est cependant compliqué, les services marketing gardant un certain flou sur le sujet. Il n'est pas rare que ceux-ci appellent cœurs ou processeurs de simples unités de calcul, histoire de gonfler les chiffres. Et on peut généraliser à la majorité de la terminologie utilisée par les fabricants, que ce soit pour les termes ''warps processor'', ou autre, qui ne sont pas aisés à interpréter. D'ordinaire, ce qui est appelé processeur de thread sur une carte graphique correspond en réalité à une unité de calcul.
 
Vu le grand nombre d'unités de calcul, les autres circuits ne peuvent pas trop prendre de place. En conséquence, les unités de décodage et/ou de contrôle sont relativement simples, peu complexes. Leurs fonctionnalités sont limitées au strict minimum, avec cependant quelques optimisations sur les cartes graphiques récentes. On n'y retrouve pas les fioritures des CPU modernes, tant utiles pour du calculscalcul séquentielsséquentiel : pas d’exécution dans le désordre, de renommage de registres, et autres techniques avancées.
 
[[File:Cpu-gpu.svg|centre|vignette|upright=2.0|Comparaison entre l'architecture d'un processeur généraliste et d'un processeur de shaders.]]
Ligne 124 :
===Des unités de calcul autrefois spécialisée, puis devenus banalisées===
 
Même les premiers processeurs de ''shaders'' disposaient de plusieurs unités de calculs séparées, capables de faire des calculs en parallèle. Un bon exemple est le processeur de vertices de la Geforce 6800, illustré ci-dessous. On voit que le processeur contient trois unités de calculs séparées : une unité généraliste de calcul sur les vertices, une unité pour les opérations mathématiques complexes (division, racine carrée, racine carrée inverse, autres) et enfin une unité pour le calcul ''Multiply-And-Add'' (une multiplication suivie d'une addition, opération très courante en 3D). On voit que les unités de calculs sont assez spécialisées, avec une ou plusieurs unités généralistes, secondée par des unités capablecapables de faire des calculs spécialisés.
 
[[File:GeForce 6800 Vertex processor block.png|centre|vignette|upright=2.0|Processeur de shader (vertex shader) d'une GeForce 6800. On voit clairement que celui-ci contient, outre les traditionnelles unités de calcul et registres temporaires, un "cache" d'instructions, des registres d'entrée et de sortie, ainsi que des registres de constante.]]
 
Cela n'a pas changé avec l'unification des ''shaders'', si ce n'est que les unités de calcul ne sont pas aussi spécialisées que dans l'exemple précédent. De nos jours, les unités de calculs dédiées à des opérations bien précises sont plus rares. On ne trouve plus d'unité spécialiséspécialisée pour les opérations complexes, comme dans l'exemple précédent, de telles opérations pouvant être émulées par une suite d'opérations plus simples. Au lieu de mettre une unité spécialisée utile pour une opération sur 10/20, autant mettre une unité généraliste pour accélérer les calculs simples et fréquents, quitte à émuler les calculs complexes. De plus, les unités de calculs sont devenues beaucoup plus nombreuses.
 
Les GPU modernes disposent d'une flopée d'unités de calcul SIMD identiques, à savoir qu'elles calculent des instructions SIMD complètes, chaque pixel étant traité en parallèle.
Ligne 140 :
====Une forme limitée d’exécution dans le désordre====
 
L'unité de texture est située dans le processeur de shaders, à cotécôté des unités de calcul. L'unité de texture peut fonctionner en parallèle des unités de calcul, comme toute unité d'accès mémoire. Ainsi, on peut poursuivre l’exécution du shader en parallèle de l'accès mémoire, à condition que les calculs soient indépendants de la donnée lue. Dans ces conditions, un shader peut masquer a latence de l'acccès mémoire en exécutant une grande quantité d'instructions à exécuter en parallèle : si un accès mémoire dure 200 cycles d'horloge, le processeur de shader doit disposer de 200 instructions à exécuter pour masquer totalement l'accès à la texture. De plus, le shader effectue souvent plusieurs accès mémoire assez rapprochés : si l'unité de texture ne peut pas gérer plusieurs lectures en parallèle, la lecture la plus récente est mise en attente et bloque toutes les instructions qui la suivent.
 
====Multi-threading matériel====