GPGPU

From air
Jump to navigation Jump to search
  • Enseignant: Didier Donsez et Georges-Pierre Bonneau
  • UE/Module: EAR en RICM5 option S&R
  • Eleve: Cédric Gérard


Contexte

Pour comprendre d’où vient le phénomène de la programmation dédiée au processeur graphique il faut comprendre l’évolution des architectures des CPU ainsi que celle des besoins en puissance de calcul au quotidien. Il faut également prendre en compte le besoins d’améliorer le rapport performance/consommation de prochaine génération d’unité de calcul.

Pendant des années les performances des processeurs étaient relative à leur fréquence d’utilisation. C’est pourquoi chaque nouveau processeur voyait celle-ci grimper, une fréquence plus grande était gage d’une meilleur performance. Celles-ci ont augmentées rapidement et les constructeurs ont longtemps mis leurs efforts de production à permettre leurs croissances (cf. loi de Moore). La théorie de Carver et Mead illustre bien comment ont considérai l’évolution des performances jusqu'à l’apparition des premiers multi-cœurs. Il s’agit de la VLSI scalling theory qui énonce que selon un coefficient λ de réduction de la taille des composants, la densité de composant augmente de λ², la tension diminue de λ, la fréquence d’horloge augmente également de λ alors que la consommation ne change pas. Mais dans la réalité il s’est avéré que la fréquence à bien plus augmenté et la tension n’a que très peu baissé en moyenne. L’industrie du processeur s’heurte donc à des problèmes physiques pour augmenter la puissance de calcul des processeurs. L’augmentation de la chaleur à dissiper devient un problème, la volonté de d’améliorer le ratio performance/consommation et la maîtrise des techniques de gravure de plus en plus complexe. De plus l’augmentation de performance du CPU n’a pas été suivit d’une augmentation proportionnel chez la mémoire. Le constat aujourd’hui c’est qu’un processeur trop performant peut voir ses performances limitées par la mémoire et les BUS qui seraient des goulots d’étranglement pour les flux de données.

C’est pourquoi on parle maintenant de parallélisme plus que d’IPS et du nombre de cœurs plus que de la fréquence brute. C’est bien le parallélisme qui permet d’améliorer les performances d’un ordinateur, tout en gardant un bon compris performance/consommation et un bon équilibre avec le reste de la machine (BUS, RAM, etc).

Depuis quelque années l’idée d’utiliser le GPU pour aider le CPU dans le calcul du quotidien à pris un essor considérable avec l’investissement des constructeurs dans le développement d’API facilitant l’accès à leur technologie.


Graphical Processing Unit

Avant d’aller plus loin il est important de comprend ce qu’est un GPU et plus particulièrement ce qui le distingue en terme d’architecture. Les processeurs graphiques sont des processeurs dédiés au calcul. Ils sont donc très spécialisés et leurs jeux d’instructions sont très différents de ceux des processeurs « généralistes ». Ils font partis de la famille des processeurs parallèles, et c’est cette caractéristique qui les rend particulièrement intéressant dans la quête que se sont lancé les constructeurs sur le parallélisme des taches les plus communes.

Voici une brève explication sur l’architecture d’un CPU permettant d’illustré les différences majeures qui favorisent le parallélisme sur GPU. Prenons l’exemple de l’intel core i7 avec l’architecture Nehalem. Il s’agit d’un quadcore d’ont voici le schéma d’architecture :

Architecture Nehalem (intel)

Ce schéma illustre bien la complexité d’un CPU haut de gamme. On remarque que la partie calcul (zone grise) est très réduite par rapport du reste du processeur. Ceci s’expliquant par la large couverture de tache offerte par un CPU et par l’importance de la prédiction de branchements, la taille du cache, etc. Sur une image au microscope électronique on remarque aisément les structures redondantes des quatre cœurs et l’énorme place occupée par la mémoire cache. Ce processeur est composé de 731 millions de transistors mais seulement environs ¼ de ceux-ci servent aux calculs proprement dit.

Nehalem with met

Les GPU proposent une architecture très différente de celle des CPU, par exemple les unités d’instructions et les unités de calculs ne sont pas organisées de la même manière. Il faut voir le GPU comme un processeur avec un grand nombre d’unité de contrôle associée à des unités de calculs composées d’un très grand nombre d’ALU.

Schéma d'architecture du GF100 de Nvidia

L’architecture du GPU de Nvidia s’articule autour de 4 groupes autonomes appelés GPC (Graphics processing cluster). Ces groupes sont composés de 4 SM (Streaming Multiprocessor) et d’élément partagés entre ces SM comme le contrôleur de mémoire et le cache L2.

Schéma d'un GPC

Ce sont ces Streaming Multiprocessors qui embarquent les unités de calculs. Elles sont appelées Cuda Core chez Nvidia et rassemble une unité arithmétique et logique pour le calcul sur les entiers et une autre pour le calcul sur les flottants. Chaque SM embarque 32 Cuda Core. On trouve également quatre SFU (Special Fonction Unit) permettant d’exécuter des calculs complexes comme, sin, cos, etc. par cycle. Les SM sont dotées de 2 ordonnanceurs permettant l’envoi de 16 instructions chacun. Si on fait les comptes avec 32 cœurs par SM et 4 SM par cluster on obtient 4*4*32 = 512 Cuda Core pour le GF100 de Nvidia. Ce qui fait la force d’un GPU c’est sont jeu d’instruction simple mais efficace. Il faut remarquer que l’architecture est beaucoup plus homogène que celle d’un CPU (Figure 6). Les GF100 de l’exemple est composé de 3.2 milliards de transistors et plus de 80% d’entre eux sont dédiés uniquement aux calculs.

multiprocesseur de flux

Dans un CPU les unités de calcul sont en parallèle permettant de réaliser autant de calcul simultanément mais son présent en petit nombre et les tâches qu’ils exécutent ne doivent pas avoir de dépendance entre elles. Chez un GPU on trouve un très grand nombre d’unité arithmétique en parallèle dans des couples unité d’exécution/unité de calcul en série. Cela permet de traiter un grand nombre de donnée simultanément et de passer au calcul suivant très rapidement. Cette architecture est parfaitement adaptée au traitement de scènes graphique 3D, dans lesquelles il faut traiter un très grand nombre de vertex et de pixels avec plusieurs traitements en série pour générer la prochaine image de la scène.

GF100 au MET

Dans la figure suivante la partie verte illustre la part dédiée aux calculs dans les deux type de processeurs.

Différence d'architecture CPU vs GPU

Et c’est cette différence architecturale qui rend le GPU plus intéressant pour la réalisation d’algorithmes en parallèle. Car il devient difficile de rajouter des cœurs sur les processeurs pour des raisons évidentes de taille, consommation et dissipation thermique. Alors que le grand nombre d’unité de calcul des GPU leurs donnent l’avantage dans la puissance brute.

Evolution de la puissance de calcul GPU vs CPU

Aujourd’hui l’architecture Tesla de Nvidia offre une puissance de calcul de 4 TéraFlops en simple précision et de 80 GigaFlops en double précision.

GPGPU

Le GPGPU ou general purpose computation on graphical processing unit est une technique qui consiste à utiliser un GPU pour réaliser des calculs généraux qu’on attribut d’habitude au CPU. Cette volonté d’utiliser des coprocesseurs n’est pas nouvelle. Déjà les supercalculateurs utilisaient des processeurs vectoriels ou scalaires. L’idée était d’aider le CPU dans les phases de calculs grâce à un processeur dédiée. Les GPU se sont largement répandus depuis, avec comme vecteur le domaine de la visualisation 3D pour les jeux vidéo. Mais à leur début ils n’étaient pas programmables se contentant d’exécuter les calculs demandés par le CPU lorsque les librairies le permettaient.

Seulement il fallait un moyen d’exploiter au mieux la puissance de calcul des GPU cantonné jusque là à de l’affichage ou du rendu 3D. C’est pourquoi les constructeurs ont décidés d’ouvrir un peu plus l’accès à leur matériel. C’est sous l’impulsion d’Nvidia que les API plus haut niveau ont commencées à apparaitre. Grâce à cela les développeurs ne sont plus obligés d’accéder à la carte via la couche basse en assembleur, cela ouvre de nouvelles perspectives pour le GPGPU.

Un élément essentiel à comprend ici est que les GPU sont des processeurs massivement parallèle et qu’ils ne sont par conséquent adapté qu’a des algorithmes parallélisable. Heureusement un très grand nombre d’algorithme existant le sont ce qui permet de les porter sur GPU. Mais cela signifie qu’il faut repenser les algorithmes. Le Stream Processing, c’est le nom communément admis lorsqu’on parle de programmation parallèle sur GPU. Il s’agit d’un paradigme de programmation s’appuyant sur le principe de SIMD (single instruction, multiple data) caractérisant la plus part du temps un GPU. L’idée c’est qu’il est possible à un GPU d’appliquer une instruction à un grand nombre de données en parallèle. La partie hardware ne change pas par rapport à un calcul de rendu 3D dans lesquelles ces processeurs sont spécialisés. Cela signifie qu’il faut détourner le rôle de chacune des parties du GPU pour obtenir le résultat escompté. Il y a quelque concepts important à comprendre la dessous :

  Texture = tableau d’entrée
  Image (frame) = tableau de sortie 
  Instruction =  shaders
  Calculer = dessiner

Cette technique de programmation est applicable dans beaucoup de domaines scientifiques et en général à tous les algorithmes qui peuvent être parallélisés. Le but premier est d’améliorer la puissance de calcul par rapport à un CPU seul. Le GPGU permet aussi d’ouvrir une nouvelle voie vers l’évolution des processeurs.

Les acteurs

Le GPGPU n’est pas qu’un effet de mode. Il s’agit réellement d’une solution tournée vers l’avenir afin d’améliorer les performances. C’est pourquoi les grands designers de GPU ainsi que des grands noms de l’informatique ont décidés de développer cette technologie. Nvidia est l’un des acteurs phare sur ce marché, non seulement le leader mondiale du GPU possède une API et une architecture efficace mais il est aussi le premier à avoir franchis le pas à grande échelle. Cette avance fait de Nvidia le premier fournisseur de solution GPGPU et la marque la plus plébiscité par les professionnels. Leur technologie est aujourd’hui utilisée comme référence, à un tel point que le GPU programmable est un de leur principal argument de vente. Avec le rachat d’ATI, AMD est devenu l’un des plus grands constructeurs de GPU. Même si ils restent derrière Nvidia en terme de vente les solutions d’AMD non rien à leur envier. En ce qui concerne le GPGPU AMD propose une solution qui se veut plus portable et tout aussi puissante que la concurrente de la marque au caméléon. Il serai difficile d’être exhaustif tant de monde prennent par à l’essor de ce domaine. C’est pourquoi le dernière acteur cité est en fait le rassemblement d’un grand nombre de partenaire afin d’établir les standards du GPGPU. Il s’agit du groupe Khronos avec pas moins 16 partenaires :

  Apple
  ARM
  ATI
  Creative
  Dell
  Ericsson
  Freescale
  Imagination Technologies
  Intel
  Motorola
  Nokia
  Nvidia
  Samsung
  Sony
  Sun
 Texas Instruments

L’objectif du Khronos group est d’établir un standard pour le GPGPU afin d’harmoniser les méthodes de programmations, les API et les IDE. C’est pourquoi les partenaires proviennent d’horizons différents, que se soit le monde du logiciel, du matériel ou de l’électronique en général. C’est lorsqu’on voit l’ensemble des entreprises motivés derrières ce projet que l’on comprend que le GPU sera un des enjeux technologiques majeur des ces prochaine années.

Le Marché

Le marché est vaste car comme précisé précédemment le GPU peut être utilisé pour n’importe quel calcul du moment qu’on puisse le paralléliser. L’utilisation première est le calcul haute performance. Dans ce domaine les GPU montrent qu’ils ont un rôle à jouer. Par exemple le deuxième supercalculateur au monde, le Tianhe-I, est équipé de 14 336 processeurs Xeon X5670 et de 7 168 processeurs graphiques Nvidia Tesla M2050 soit environs 21 500 processeurs pour 2,566 PFlops. Deuxième en puissance de calcul mais seulement 7eme pour le nombre de processeurs. Il possède donc un rapport puissance/nombre de processeurs excellent.

Le monde de la simulation est particulièrement intéressé par l’accessibilité à cette nouvelle puissance de calcul. Tout ce qui touche à la modélisation moléculaire, au traitement météorologique, la génétique et les laboratoires de recherches en général. L’informatique embarquée est également très intéressé, il s’avère que la puissance de calcul et la réactivité de ces derniers est parfaite pour un système embarqué temps réelle de plus le rapport puissance/consommation est bien meilleur sur un GPU que sur un CPU. C’est d’ailleurs sur ces principes que le projet OpenGPU est né. Il a pour but de rendre toujours plus accessible la parallélisassions des algorithmes qui ne le sont pas encore (par exemple les algorithmes embarqués) et facilité l’utilisation, par tous les programmeurs, de la puissance de calcul de leur machine par l’intermédiaire d’un Plug in Eclipse.

L’ouverture des API est l’accessibilité croissante des GPU par les programmeurs rendent c’est processeurs de plus en plus intéressant. Mais ils ne le sont pas uniquement pour les supercalculateurs ils le sont aussi dans le marché grand public. Aujourd’hui de nombreuses applications utilisent le GPU pour le décodage vidéo, l’amélioration du calcul scientifique, la simulation etc.

Sur une machine de particulier outre le rendu 3D le GPGPU peut aider aux taches quotidiennes et augmenter la réactivité. Il est utilisé dans de plus en plus de progiciel comme Photoshop, un grand nombre de logiciels d’encodage vidéo, MatLab, des outils de compilation, de simulation, des navigateurs internet sous la forme de Plug in, l’antivirus.


Les API

Les API (Application Programming Interface) sont une part importante de la réussite du GPGPU. En effet c’est grâce à se niveau d’abstraction de la couche matériel que tous les programmeurs peuvent accéder à la puissance de calcul offerte par une puce graphique. Si depuis longtemps on utilise les GPU pour le calcul l’idée de l’exécution de programme habituellement dédiée au CPU est devenue possible parce que les API sont apparues et ne cessent d’évoluer. Il existe plusieurs moyens de dialoguer avec un GPU et de lui faire exécuter des calculs. Ici nous allons nous intéresser uniquement aux API dédiée au GPGPU. Il en existe donc quatre, Cuda par Nvidia, AMD Stream et CTM (Close To Metal) par AMD, Direct Compute par Microsoft et OpenCL par le Groupe Khronos.

Tout d’abord nous allons nous intéressé à l’API qui est la plus connue et la plus utilisé aujourd’hui. Il s’agit de Cuda. Le nom de cette API signifie « Compute Unified Device Architecture » est elle permet, pour faire simple, de programmer sur un GPU en C.

Illustration du flow de calcul avec Cuda

Cette API permet simplement d’utiliser le processeur pour distribuer les instructions sur le GPU celui-ci réalise les calculs en locale et retourne les résultats vers la mémoire centrale. Le GPU agit ici vraiment comme un coprocesseur allégeant la charge du CPU.

Cuda propose un très grand nombre de librairie, de la simple génération de nombre aléatoire avec cuRAND au transformer de Fourier cuFFT en passant du calcul scientifique avec cuBLAS. Il existe également des librairies pour l’algèbre linéaire, le traitement d’image etc.

L’idée derrière Cuda est l’accessibilité aux ressources du GPU. Le modèle de programmation est simple. Pour un développeur Cuda il y a plusieurs hôtes et plusieurs périphériques. Dans une machine simple les hôtes sont les CPU et le GPU est considéré comme un périphérique. Ensuite le code sera séparé à la compilation entre les hôtes et les périphériques, l’hôte se verra attribuer les parties de code non parallélisable alors que tout ce qui peut être parallélisé sera attribué au périphérique.

Donc si on prend l’exemple du produit matriciel, le CPU va initialiser et remplir la matrice. Il va ensuite préparer l’appel au code noyau en le distribuant sur les threads du GPU. Le GPU exécute le calcul avec un très grand nombre de thread et retourne le résultat au CPU. Le CPU termine le calcul et nettoie la mémoire.

Il existe plusieurs méthodes pour programmer avec Cuda suivant IDE que l’on souhaite utiliser ou la chaine de compilation voulue. Il suffit de se renseigner au près de l’éditeur concerné.

Chez la concurrence AMD Stream est la réponse d’AMD à Nvidia. Globalement solution au niveau hardware fournis le même service que la solution Nvidia, adapté à l’architecture des GPU AMD. La plus grande différence se trouve au niveau hardware. AMD à abandonné sa librairie ainsi que sa solution bas niveau (CTM) pour ouvrir sont architecture à OpenCL. AMD souhaite que sa technologie soit le plus ouvert possible, c’est pourquoi le SDK de AMD Stream est basé sur la technologie OpenCL. Ce choix permet de profiter au maximum de la puissance des carte de a marque mais elle garantie aussi de développer des applications portable ou de pouvoir récupérer d’autres application respectant le standard. Les GPGPU respecte tous les même paradigmes de traitement par flux à tel point que AMD va jusqu'à proposer un tutoriel pour porter les algorithmes réaliser avec Cuda sous OpenCL.

Les solutions Cuda et AMD Stream se valent dans la plus part des benchmark trouvé chez toutes les agences spécialisées. La différence notable se trouve dans l’encodage video ou la solution d’AMD se trouve être plus rapide alors que celle de Nvidia produit un résultat de meilleur qualité.

OpenCL est la solution proposé par le Khronos group. Initié par Apple cette API a été mise au point afin de fournir un standard pour le GPGPU. Il s’agit de la combinaison d’une API et d’un langage de programmation dérivé du C. OpenCL permet de programmer des systèmes parallèles hétérogènes sur CPU multi-cœurs et/ou GPU. Il s’agit donc d’une solution intermédiaire entre le CPU et le GPU. Le code dérivé du C s’exécute sur le processeur hôte et va charger l’API OpenCL. Se sont les Kernel qui sont programmé en OpenCl-C qui s’exécuteront sur les cœurs du GPU. Cette présentation rappelle beaucoup la solution Cuda de Nvidia. Bien qu’elle s’en rapproche elle a des objectifs bien plus larges. D’abord elle se veut portable, c'est-à-dire qu’elle ne dépend pas de la partie hardware sous jacente. De plus elle n’est pas destinée qu’au GPU, elle permet également de tirer partie de CPU multi-cœur mais également des processeurs hétérogènes comme le Cell d’IBM ou toute ou partie des puce embarquées (SoC).

Donc OpenCL se place à un niveau différent des API propriétaires. Moins performant en général car elle n’est pas optimisée pour une architecture particulière mais mieux intégrée sur les puce AMD. Elle est néanmoins plus portable et tant à devenir le standard en terme d’API dédiée à la programmation parallèles.

La dernière API qu’il est important de cité est celle de Microsoft, Direct Compute. Il s’agit d’une API qui fait partie de la collection DirectX, mis à disposition avec DirectX 11 elle est supporter par tout les GPU compatible DirectX 10 ou 11 à partir de Windows vista et Windows 7. Nvidia soutient activement l’API de Microsoft et celle-ci peut interface la couche basse de Cuda au même titre que Cuda C.

Bien d’autre API permettre l’utilisation d’un GPU, mais ci-dessus nous avons présenté les quatre principale. Il faut retenir que bien que Cuda soit excellent et bien défendu par Nvidia, OpenCL a plus de chance d’atteindre le grand publique qui ne dépendrai plus de son matériel pour profiter de la librairie.

Les limites

Bien que tout plaide en faveur du calcul sur GPU il y a plusieurs facteurs qui limitent leur utilisation. Le premier concerne uniquement les machines classiques qui embarquent un GPU sur une carte fille disposé sur un port PCI Express. Bien que ce bus soit de bonne qualité il est suffisant pour apporter les données d’entrées à la carte mais pour faire transiter les données en sortie il est une limitation aux performances.

Le jeu d’instruction d’un GPU est loin d’être aussi riche que celui d’un CPU c’est pourquoi il faut que les actions élémentaires à réaliser sur le GPU doivent en tenir compte. Ceci permet une simplification au niveau hardware mais oblige à avoir une façon de programmer que celle habituellement utilisé sur un CPU. De plus les algorithmes ne ressemblent pas du tout à ce qu’on a l’habitude de voir. Ils doivent être parallélisé. Par exemple on n’utilise pas de boucle sur un GPU mais on doit utiliser l’architecture pour réaliser les calculs en parallèles sur les différentes unités de calcul. De même les instructions conditionnelles sont des freins énormes aux performances durant les calculs.

De même si on regarde de près on se rend compte qu’un GPU n’est efficace que si on arrive à le charger suffisamment. Il faut qu’il y ait suffisamment de thread exécutable en parallèles pour obtenir un gain par rapport à la même exécution sur CPU. De plus les calculs doivent être d’un ordre de complexité assez élevé pour favoriser le travail des unités de calcul intégrées au GPU plutôt que les unité de calcul lourde d’un CPU. Enfin il à été démontré par plusieurs universités américaines qu’on ne trouve pas nécessairement un gain de performance en parallélisant un calcul.

Il se peut que la version séquentiel d’un algorithme soit aussi rapide, voir plus rapide, que ça version parallélisé. Le projet OpenGPU a pour but d’offrir des solutions à ces différentes limites pour imposer et uniformiser la programmation sur GPU.


Les perspectives

Malgré tous le GPGPU est très prometteur. Et la mode coté constructeur est à la combinaison sur une même puce d’un CPU et d’un GPU. C’est le cas des processeurs Intel qui embarque tous une partie graphique. Récemment ce sont les APU d’AMD qui se sont montré, il s’agit d’un mélange d’Athlon X4, X3 ou X2 avec une partie graphique basée sur l’architecture des Radeons de la série 6xxx.

Plus généralement la combinaison des deux architectures est utilisée dans les SoC (System on Chip) qui embarquent toutes les fonctionnalités nécessaires au système pour lequel ils sont fabriqués. On retrouve ces puces dans la plus part des systèmes embarqués et plus particulièrement dans tous les Smartphone et la plus part des tablettes tactiles ou des consoles portables. Pour nos téléphone moderne et les tablettes qui en dérive la demande en CPU augmente mais la demande ne puissance graphique également. De se fait tous les SoC embarque une partie graphique dans leur architecture, et cette partie ne cesse d’augmenter en puissance. Aujourd’hui les Smartphone son équipé de processeurs multi-cœurs avec un puce graphique rivalisant avec les consoles portables de l’année précédente. Mais demain on pourrait bien voir l’explosion de la réparation de la puissance de calcul entre la partie CPU et graphique même sur de petites plateformes en mobilités, d’autant qu’il est plus facile de contenir la consommation sur un GPU que sur un CPU.

Un dernier exemple qui illustre bien que l’avenir est à l’hétérogénéité des processeurs est le processeur Cell développé conjointement par IBM, Sony et Toshiba. C’est un processeur optimisé pour le calcul parallèle. Il dispose d’une architecture simplifié In Order, donc pas d’optimisation dans le CPU tout doit être fait par le programmeur, mais plus de place pour les unités de calcul. Sont architecture se découpe autour de 9 cœurs, 1 cœurs principale à base de power PC d’IBM et 8 unité de calcul dédiées. Voici un résumé de performance théorique de la version de 2006 du Cell :

        en simple précision 32 bits : 230,4 GFLOPS (25,6 GFLOPS par SPE, 25.6 GFLOPS pour le PPE)
        en double précision 64 bits : 20,8 GFLOPS (1.8 GFLOPS par SPE, 6.4 GFLOPS pour le PPE)

Le Cell est connu pour équipé la PS3 de Sony mais il équipe aussi le RoadRunner 4eme plus puissant supercalculateur au monde avec 6 480 double cœurs Opteron d'AMD et 12 960 processeurs Cell d'IBM et le premier à franchir la barre du pétaFLOPS.

Ce processeur marque un tournant car il peut supporter des systèmes classiques par exemple linux pour la PS3 et le RoadRunner. Et c’est cette architecture complètement hétérogène qui marquera certainement le prochain tournant de l’ère de l’architecture du micro-processeur.