Veille technologique Des cartes graphiques comme

Transcription

Veille technologique Des cartes graphiques comme
Veille technologique
Des cartes graphiques
comme coprocesseurs algébriques
Philippe Estival
CERFACS
Centre Européen de Recherches
et Formations Avancées en Calcul Scientifique
Mars 2010
2
Table des matières
i
ii
1
Introduction
Les recherches dans l’accélération des calculs de mécaniques des fluides nous ont conduit à
considérer le transfert d’une partie de ces codes sur des processeurs graphiques. On peut désormais
les voir comme des machines vectorielles massivement parallèles.
Ce domaine a été très actif depuis 2007 et l’évolution technologique a poursuivi un rythme
effréné. La veille technologique, l’instrumentation et la mise au point d’une bibliographie permettent de dresser un paysage complet du domaine pour commencer, et d’assurer un projet à
quatres niveaux : le suivi des travaux de recherches et des récentes publications, l’évolution des
processeurs, le transfert du premier à la seconde sur des codes précis
Nous présentons, dans la première partie, l’architecture des cartes graphiques unifiées. En
deuxième partie, les outils de programmation disponibles. Les performances et les précision de
la multiplication de matrices sont aux troisième chapitre. En quatrième partie nous résolvons les
équations de Laplace, se plaçant dans une optique de scalabilité. De futurs axes de recherches
sont évoqués en dernier lieu.
1.1
La simulation numérique
« La monté en puissance des ordinateurs a introduit une rétroaction nouvelle de la technique
informationnelle sur de vastes domaines des techniques de la matière et de l’énergie. La simulation
numérique peut être définie comme une démarche qui consiste à construire, dans la mémoire d’un
ordinateur, une image numérique d’un système physique qui peut être par exemple, un volume
de fluide. Cette image comporte schématiquement deux éléments : d’une part les paramètres
physiques, pression, température, vitesse, qui en chaque point définissent l’état du fluide à un
instant donné – ce sont les conditions initiales ; d’autre part l’ensemble des équations qui régissent
l’évolution du fluide. Il s’agit dans ce cas, d’un ensemble d’équation différentielles non linéaires,
les équations de Navier Stokes. L’ordinateur est programmé de façon à faire évoluer l’image
de l’état du fluide en respectant les contraintes imposées par les équations qui régissent cette
évolution.
Quel sont les usages de cette technique ? En premier lieu elle permet de prévoir l’évolution
d’un système physique en l’assimilant à l’évolution de son image numérique. Rien ne contraint
à faire évoluer l’image au même rythme que l’objet, et une évolution accélérée crée la possibilité
de prévoir. C’est ce que l’on fait dans la prévision météorologique où l’objet est l’atmosphère
terrestre.
La mécanique des fluides offre un domaine de prédilection pour l’usage de cette méthode : si
les équations qui gouvernent le comportement des fluides sont parfaitement connues, elles ne se
prêtent pas à accéder directement à la compréhension des mouvements du fluide. Le détour par
la simulation numérique est le seul accès viable à cette connaissance [1]. »
La science moderne est faite d’observation, de théories, d’expériences... et de simulations
numériques.
1
1.2
Motivations
Dans les années 2000, la tendance affiché par les prédictions de la loi de Moore s’est infléchie.
Si les projections s’était poursuivies au rythme qu’elle connurent dans les années 90, nous serions
équipés en 2010 de processeurs cadencés à 10 GHz. Or la réduction de la taille des transistors et
l’augmentation des fréquence d’horloge atteint ces limites, dues aux phénomènes de transferts et
de dissipations caloriques. Aux échelle nanométriques, des problèmes de nature électronique se
présentent, qui poussent à explorer d’autre voies. Le processeur Von Neumann n’a pas été conçu
pour suivre l’évolution de l’espace disponible.
L’autre difficulté surgit dans l’excès de consommation électrique des puces : un processeur
dont le cycle d’horloge oscille à un gigahertz réclame environ 25 watts pour fonctionner, tandis
qu’environ 100 watts sont nécessaires pour faire fonctionner un processeur cadencé à 3 GHz.
L’étendue du parc informatique mondial nous amène à reconsidérer le bilan énergétique produit par les PC, les serveurs webs, les DNS, les moteurs de recherches et les centres de calculs.
Selon Wire Magazine, rapportant une étude d’AMD [2], l’électricité totale consommée par les
moteurs de recherches les plus important en 2006 avoisine 5 gigawatts.
Puisque les CPUs ne peuvent être accélérés plus avant, les efforts se sont tournés sur le
parallélisme des unités de traitements. C’est actuellement le seul moyen dont nous disposons
pour mettre à contribution davantage de puissance de calcul [3]. La distribution des calculs sur
des ordinateurs massivement parallèle, l’étude des machines et des algorithmes parallèle n’est
plus un exercice académique mais une nécessité.
En l’état actuel, l’algèbre linéaire se manifeste comme un candidat idéal à l’accélération de
code de calculs à grands nombres d’équations. Si le hardware évolue rapidement, mute, parfois devient obsolète, le software, lui, reste et poursuit la logique pour laquelle il a été conçu.
Comme on ne peut pas réécrire tout les codes pièces par pièces, on se tourne vers des solutions
d’accélération intermédiaire : en remplaçant le matériel existant et les librairies logicielles par
des contreparties fonctionnant sur des architectures hybrides, mais tout en conservant les codes
historiques. A terme, il sera possible de mettre à la disposition des chercheurs et des ingénieurs,
l’équivalent de supercalculateur dans leur station de travail.
Ce phénomène n’est pas nouveau, l’architecture des supercalculateurs inspire celle des microprocesseurs : les techniques mises en oeuvre dans les microprocesseurs sont en premier lieu
des principes macroscopiques. En ce sens, Les théories de la programmation parallèles sont plus
vérifiables que jamais. Les techniques de calcul parallèle existantes, dans des outils tels que MPI
ou OpenMP, à l’échelle des clusters pour le premier et des machines multiprocesseurs pour le
second, ont été adaptées, moyennant quelques contraintes, aux GPU. En bref, nous insistons sur
un fait : le matériel évolue, mais le logiciel existe pour toujours. L’intrication logicielle/matérielle,
telle qu’elle existe dans les systèmes d’exploitation par exemple, est levée dans les couches logicielles relevant de la logique métier, mais revient systématiquement dès lors qu’il sera question
d’optimisation et d’arithmétique des ordinateurs, ce qui ne manque par d’arriver dans les codes
de calculs.
2
1.3
Les processeurs graphiques
Les unités de traitement graphiques (Graphical Processing Units, GPU) sont à l’origine dédiés
aux affichages et aux rendus de géométries et d’images. Leader dans ce domaine, l’industrie du
jeu vidéo à guidé leur évolution. A ce titre, depuis 1992, le comité de l’Architecture Review Board
(ARB), réunit un consortium des principaux fabriquants de matériels.
Pour que le développement logiciel puisse reposer sur des standards l’ARB s’est entendu afin
d’unifier le modèle de programmation des cartes graphiques, par un versionnage et des test de
compatibilités rigoureux de ses standards.
Parmis ses résultats, la spécification d’OpenGL a apporté des solutions d’émulations logicielles, qui, au fil des versions, ont été intégrées aux matériels. Une parité logicielle/matérielle a
émergé, l’amélioration du matériel permettant la mise au point de nouvelles solutions logicielles,
solutions qui a leur tour s’intègrent au matériel.
Les GPU, présents dans chaque ordinateur, ont évolués au tournant de la décennie en co-
Figure 1.1 – Le comité de l’ARB : émerge une parité logicielle-matérielle
processeurs d’arithmétiques vectorielles massivement parallèles, ce qui explique aujourd’hui leur
présence dans les domaines du calcul scientifique, numéro un du parallélisme de masse.
1.4
Enjeux industriels
Délivré en 2006 après une longue période de gestation en bêta, CUDA a conquis de nombreux
territoires dans le domaine de l’ingénierie scientifique. Depuis, Nvidia a considérablement revu
la conception et le positionnement de ses GPU et la désignation commune doit les reconnaı̂tre
comme des appareils versatiles dont l’usage dépasse celui des jeux électroniques et du graphisme.
Pour Nvidia, le HPC est à la fois une opportunité de vendre plus de puces et une assurance
contre le futur incertain des GPUs discrets. Bien que les les cartes graphiques de Nvidia aient
longtemps été prisées par les joueurs, le marché a changé. Lorsque AMD acquis ATI en 2006,
Nvidia demeura le plus gros vendeur indépendant de GPU. En réalité le ”géant vert” est même le
seul vendeur indépendant, les autres compétiteurs ayant échoué ou été racheté au cours des ans. Le
statut de dernier survivant est enviable à condition que le marché pour les GPUs discrets demeure
3
stable [4]. AMD et Intel projettent l’intégration de cores graphiques dans leur processeurs futurs
ce qui pourrait considérablement réduire le marché des GPUs discrets. Annoncé pour la fin 2009,
le projet Larabee d’Intel a été repoussé à une date inconnue. Les différents échanges commerciaux
qui ont suivis laissent supposer qu’Intel ne sortira pas de cartes graphiques externes.
L’intérêt de Nvidia pour développer sa recherche est manifeste : une tactique de collecte
des ”success story” [5, 6] dans l’accélération des codes grâce à sa technologie fait non seulement
une bonne publicité, certes, la place aussi dans une situation favorable pour déterminer les
projets profitables au titre du commerce par l’adaptation au marché, mais aussi et surtout par
l’amélioration de sa propre R&D.
Un exemple : le GPU permet d’accélérer les codes de simulations numériques de dissipations
thermiques, qui a leur tour ouvre une nouvelle perspectives dans l’amélioration des chipsets.
Le temps de prototyper et la boucle de rétroaction logicielle/matérielle se referme en quelques
semaines pour le constructeur, en plusieurs mois pour ses clients, et l’écart entre les deux demeure
suffisement important pour que la logique commerciale s’intègre dans le paysage de la stratégie
américaine consistant à maintenir cet écart technologique avec le reste du monde.
Apple est l’un des premiers à proposer une gamme d’ordinateurs équipés de deux processeurs
graphiques, l’un pour l’affichage, l’autre pour l’accélération d’OSX et de ses applications. Apple
supporte vivement la spécification du standard OpenCL.
Microsoft, sorti de l’ARB en 2003, a fait chambre à part en poursuivant l’évolution de DirectX11. Sans grande surprise, les innovations sont rares et inspirées de travaux existants. Windows demeurant la plateforme de prédilection de l’industrie du jeu vidéo, les pilotes graphiques
vont poursuivre encore longtemps leur interface avec le système d’exploitation de Redmond.
On peut supposer que les GPU seront neuf fois plus rapides dans trois ans.
4
2
2.1
Architecture
CPU - GPU : une comparaison
D’un point de vue simplifié, les processeurs multicores sont de trois types. MIMD (Multiple
Instruction, Multiple Data), SIMD ou SPMD (Single Instruction/Program, Multiple Data).
Un CPU multicores dispose sur le même socle de quatre cores, de un ou plusieurs caches
internes, de caches externes, d’un accès à la RAM par un bus. Les CPU vont par paires dans les
Xeon Nehalem pour un total de huit cores. Les CPU sont optimisés pour des accès au mémoires
caches à faibles latences, et la logique de contrôle autorise des exécutions spéculative ou dans le
désordre.
Là où un processeur générique entre dans le modèle de Von Neumann exécutant une opération à la fois – plusieurs, pas plus de 4 – dans les processeurs superscalaires 1 , les processeurs
graphiques entrent dans la classe des ordinateurs parallèles : une passe de calcul exécute un
programme unique (un kernel ou shader) sur tous les éléments d’un flux de données.
Processeur
Paradigme
Cores de calculs
Fréquence d’horloge
Gravure
Millions de transistors
Cache L1
Mémoire
Intel Core 2 Quad
MIMD
4 cores
2.4 GHz
65nm
582
8MB
4GB
NVIDIA 8800 GTX
SPMD
128 ALUs
1.35 GHz
90nm
681
16 × 8K
768 MB
Figure 2.1 – Comparaison des transistors et des mémoires entre CPU et GPU
1. Les accès par cache du Pentium 4 lui permettent de réunir une valeur SSE de 128 bits (un paquet de 4
flottants de 32 bits) en un cycle.
5
En examinant de plus près la distribution des transitors d’un GPU, on peut voir que beaucoup
de transistors sont dédiés aux Unités Arithmétiques et Logiquess (ALU), regroupés en multiprocesseur (Streaming Multiprocessors=SM). Chaque SM possède sa propre unité de contrôle et de
cache, de dimensions nettement plus réduites que sur des cores de CPU. Les données à l’intérieur
du cache d’un SM sont exclusives à celui-ci et ne peuvent être partagées avec les autres. Autrement dit, les flux de données sont indépendants. L’unité de contrôle est moins sophistiquée.
En revanche l’intensité de calcul est très haute ; Le flot d’exécution parallèle tire parti d’une
bande passante élevée et les latence mémoires entre la RAM embarquée sur le GPU et les caches
peuvent être masquées par du calcul, les ALU pouvant travailler pendant que la mémoire est
transférée. Dans la pratique, trouver la taille optimale des flots et l’équilibre entre transfert,
calcul et restitution nécessiteront souvent le chronométrage des trois opérations.
2.2
La carte de calcul Tesla T10
Le carte de calcul Tesla C1060 ou T10 (photo
.
Figure 2.2 – La carte graphique Tesla C1060. Une version modifiée de la GTX280.
Processeur hétérogène (photo
La vue
Sa puissance crête atteint environ 400 Gflop/s en précision simple et 78 Gflop/s en double. Le
paradigme d’accès parallèle à sa mémoire primaire est multithread avec fusion (ou coalescence)
des accès. La bande passante interne est d’approximativement 73,6 Go/s. Le ratio crête flop/octet
en précision double est donc proche de 1.
Les bandes passantes externes sur un bus PCI express 16 pour accéder à la RAM du système
hôte sont respectivement 8 Go/s et 4 Go/s, pour des zones mémoires alignées et non paginées.
Elle chute à 2.5 Go/s en lecture et 2.1 Go/s en écriture sur la RAM du système hôte pour des
mémoires non-alignées ou paginées.
La T10 embarque 4 Go de RAM, organisées en huit banques, ces mémoires sont de catégorie
GDDR3, cadencées à 1100MHz. Les accès mémoires sont uniformes, à lecture concurrente, écri-
6
Figure 2.3 – Le processeur GTX280.
ture aléatoire : plusieurs threads peuvent être ordonnancés pour lire une même zone mémoire,
mais que le comportement d’écritures concurrentes est imprévisible.
Selon la charge et les estimations, leur puissance électrique de fonctionnement varie entre 450
(puissance crête) et 235 Watts.
7
Le multiprocesseur GT200 est un microprocesseur conçu avec une précision de 65nm. De type
multithread SIMD. Cadencé à environ 1.3GHz, il délivre 2.6 Gflop/s et embarque 64Kb (16 384
registres de 32 bits) de mémoire partagée entre ces 11 unités arithmétiques et logiques : les 8
unités scalaires de précisions flottantes simples (SP), l’unité scalaire de précision flottante double
(DP) et les deux unités de fonctions spéciales (SFU).
Chaque unité scalaire exécute jusqu’à 128 threads corésidents partageant un fichier de registre
de 2048 entrées. Au total, ce GPU peut exécuter 30720 threads concurrents.
Héritant d’une architecture dédiée au traitement vidéo et le scintillement exceptionnel d’un
pixel isolé étant un phénomène négligeable, les mémoire GDDR2 et 3 des GPUs sont dépourvues
de code correcteur d’erreur (ECC),
Sur la quantité totale de mémoire physique, environ 50 MB ne peuvent pas être adressés,(zone
mémoire < 0x3A9FFF0), où sont stockés les registres locaux, la table d’allocation des pointeurs
et probablement des pilotes.
Le précèdent GPU de Nvidia, GeForce 8800 GTX, dispose de 16 MPs, 128 SP, chacun pouvant
gérer 96 threads concurrents 1024 registres par MP et supporte au maximum 12288 threads
”
concurrents. Conçu à l’échelle de 90nm, il possède 681 millions de transistors et embarque 768
MB de ram DDR2. Chaque processeurs de flux dans une GeForce de série 8 peut gérer 96 threads
concurrents. Les GT200 sont embarqués sur les cartes GeForce GTX 260, 280, 285, 295, Quadro
FX 4800 et 5800 et Tesla C1060.
8
GPU
Paradigme
Socket
Cores
Transistors
Intensité de calcul
SP
DP
# Threads max
Accès mémoire primaire
Bande passante
interne
externe
mémoire alignée
mémoire paginée
Tesla C1060
SPMD
unique
30 MP GT200
1,4 Milliards
400 Gflop/s
78 Gflop/s
30720
multithread
CREW + fusion
73,6 Go/s
ratio flop/octet
Mémoire
Type
EEC
Fréquence
Puissance électrique
8 Go/s
lecture : 2,5 Go/s
écriture : 2.1 Go/s W
0.99
4 Go
GDDR3
non
1100MHz
450 W / 235 W
SMP
Gravure
Fréquence d’horloge
Intensité de calcul
Mémoire cache
SP
DP
SFU
Mémoire constante
# Threads max
fichier de registre
GT200
65nm
1.3GHz
2.6 Gflop/s
16 Ko
8
1
2
65536 o.
128
2048 entrées
Figure 2.4 – Tesla : Tableau des caractéristiques
9
2.3
Fermi/GT300
Les spécifications de la prochaine génération de GPU Nvidia dédiés au HPC sont déjà annoncées [7, 8, 9, 10]. Néanmoins, les informations de cette section sont à prendre avec précaution.
Le GPU fermi est composé de 3 milliards de transistors gravés à une échelle de précision de
40nm, répartis en 16 SMP de classe GT300. Comparativement aux Tesla, le nombre de SMP
diminue car la quantité d’ALU sur chaque quadruple.
Figure 2.5 – Le processeur graphique ”Fermi”
La mémoire globale est de catégorie GDDR5. L’espace d’adressage est en 64-bits pour le
système, 40-bits pour l’utilisateur. L’espace mémoire allouable est donc de l’ordre du terabyte,
organisé en 6 banques, et cadencés à 3 GHz. La bande passante interne serait de 192 GB/s,
sur une interface mémoire de 512-bits. Ces mémoires posséderont des EEC par redondance des
transmissions selon le modèle SECDED 2 .
Ce GPU possède un niveau de cache L2, de 768Ko, situé sur la partie centrale du chipset et
un système d’adressage et de mise en cache automatique des données de petites tailles depuis
l’espace mémoire hôte.
Les mémoires caches embarqués par les SMP passent à 64 Ko, qui pourront être répartis entre
le cache de texture et les mémoires partagées sous une configuration 48-14 ou 16-48. Leur fichier
de registre est de 32,768 x 32 bit.
2. single (bit) error correction, double error detection
10
Figure 2.6 – Architecture schématique du GPU Fermi : 16 SMs portant 32 cores.
Figure 2.7 – Schéma d’organisation du SM GT300
Le multiprocesseur GT300 porte 32 unités d’instructions entières et flottantes 32 bits, organisés en deux groupes de 16, plus 16 unités de lecture/écriture (LD/ST) et 4 unités de fonctions
spéciales. Ils sont cadencés à 1,5GHz.
L’ordonnanceur global gère 1,536 threads (soit 48 par SMP, correspondants au 2x16 core +
les unités LD/ST)
La norme de précision flottante serait améliorée ; Ainsi le résultat intermédiaire des FMA
11
GPU
Paradigme
Socket
Cores
Transistors
Bande passante
interne
Mémoire
Type
EEC
Fréquence
SMP
Gravure
Fréquence d’horloge
Mémoire cache
SP
DP
SFU
fichier de registre
Fermi
MPMD
unique
16 MP GT300
3 Milliards
192 Go/s
6 Go
GDDR5
oui
2000MHz
GT300
40nm
1.5GHz
64 Ko
16
16
4
32720 entrées
Figure 2.8 – Fermi : tableau des caractéristiques
(Fused Multiply Add) aurait une mantisse de 106-bits et 161 bits pendant l’addition.
Les performances annoncées sont respectivement 256 et 512 FMA en précisions simples et
doubles par cycle d’horloge, soit 750 Gflops crête en précision double.
Le bus PCIe serait aussi, transformant les modes d’accès bi-directionnel en canal unique,
multipliant ainsi par deux la bande passante externe.
La capacité d’exécution concurrente de plusieurs kernels classe ce GPU dans la catégorie
MPMD.
12
3
De la perspective du
programmeur
Il y a quelques années, des programmeurs pionniers découvrirent que les GPUs pouvaient être
employés pour d’autres traitement que le graphique [11]. Toutefois leur modèle de programmation
improvisé était bancale et le pixel shader programmable n’était pas le moteur idéal pour du calcul
générique du fait de la liaison avec la partie fixe sur le hardware. Nvidia a évolué ce modèle pour
en proposer un meilleur. Le « streaming multiprocessor »remplace celui de « shaders », amalgame
qui désignait à la fois le core graphique et le programme correspondant.
La programmation des GPUs emprunte deux axes : 1) Par programmation directe en utilisant
les modèles de programmation délivrés par les constructeurs ou le standard du libre. 2) Par la
traduction des programmes existants au moyen de compilateurs appropriés et d’annotations de
codes sources par des directives. Le tableau suivant synthétise l’ensemble des solutions retenues
pour GPU et processeurs multicores.
Solution
Nvidia CUDA/PTX
OpenCL
AMD Brook+/CAL/CTM
Nvidia Cg + OpenGL
GLSL / OpenGL2+
Nvidia Cg + DirectX
DirectX11 Compute
Lib Sh
RapidMind
OpenMP
PGI9
Caps HMPP
Spécifique
NV
Multi-OS
Multi-Cores
Compilateur
ATI
NV
Les modèles reposants sur des API graphiques ne sont plus considérées comme des solutions
viables pour le HPC, mais leur modèle présente un intérêt certain pour les développements
de langages de programmation spécialisés ; DirectX11 Compute vise l’accélération des systèmes
d’exploitation et des stations de travail ; HMPP et PGI9 sont deux compilateurs concurrents ;
OpenMP concerne ici le pilotage des configuration multi-GPU.
Parmis ces solutions, nous retenons pour la suite PGI9, HMPP, CUDA et OpenMP.
13
3.1
Shaders programmables
Avant l’apparition du modèle d’architecture unifiée (programmable par CUDA et OpenCL),
le GPGPU a été limité au domaine des API graphiques. Le mode d’adressage était limité aux
tailles et aux dimensions des textures, les shaders avaient une capacité de sortie limitée, il n’y
avait pas de set d’instruction pour des opérations atomiques entières ou binaires, les échanges
d’information restaient limités entre les pixels. L’opération directe de diffusion (scatter) ne peut
pas être réalisées avec un processeur de fragment du fait de la localisation fixe de chaque fragment
sur la grille lors de sa création. Une opération de diffusion logique implique alors une opération
de collecte (gather) additionnelle sur l’intégralité de ensemble.
Toutefois, les langages de shaders tels que GLSL et Cg présentent les avantage d’être simples,
compacts, dans un style proche du C++, ils proposent des fonctions intrinsèques de manipulation de matrices et de vecteurs et la composition fonctionnelle. Une série de registres fixes
sont préalablement définis, qui correspondent à des zones mémoires segmentées et aux registres
qui les accompagnent : tampons de géométrie, tampons de couleurs et d’images, matrices de
transformations. Suivant le modèle classique de programmation des GPUs avant unification, et
conçus pour le rendu d’images, les langages de shaders se composent en règle générale de deux
programmes ; un programme de traitement des sommets et un programme de traitement des
fragments de pixels. Ceux-ci doivent gérer la liaison avec le pipeline fixe en déclarant quelles sont
les variables locales au shader, et quelles sont celles transmisess sur le pipeline.
Le programme final compilé, composé des deux, reçoit en entrée les données qui alimentent
les tampons : des géométries structurées par sommets indicés, des matrices de transformations,
des coordonnées de textures affectées aux sommets, et des couleurs ou des textures associées. Les
deux premières servent à composer la géométrie projective perçue sur un écran, tandis que les
suivantes travaillent sur la rasterization de l’image pixélisée. L’ensemble des coordonnées ainsi
traité est homogène. Le shader étant séparé du programme hôte et du pipeline fixe, il peut être
recompilé entre deux passes de rendu.
La limitation historique de ce modèle tient au fait que la composition finale est placée dans un
tampon d’image en écriture exclusive pour être restitué à l’écran uniquement, et dont la relecture
est coûteuse. De la même façon, les données placées dans les tampons prédéfinis sont en écriture
seule. L’évolution des matériels a permis progressivement de dépasser cette limite en réservant
des buffers dans la mémoire du GPU sur lesquels il devient possible de lire et d’écrire.
Les shaders présentent un sujet d’étude très intéressants du fait de la présence d’assembleur,
de la liaison fixe avec le matériel et d’un compilateur. Les questions pour mener l’étude à leur
sujets sont nombreuses : où réside le programme final ? Quel est ce compilateur et où se trouvet-il ? Peut-on le reproduire ? L’évolution des GPUs permet-elle d’élaborer un nouveau langage
dans un style proche de celui des shaders ?
Pour une programmation GPGPU rendue encore plus accessible aux scientifiques, on peut
imaginer un langage de programmation simple, disposant d’une interface avec les langages existants, permettant de déclarer les liaisons à l’intérieur d’un programme classique, liaisons attribuées par la suite à un programme écrit dans un paradigme SPMD. C’est ce que font les
shaders, mais avec des registres fixes. C’est aussi ce que fait CUDA, mais avec une large mesure
de complexité supplémentaire.
14
Figure 3.1 – Schéma synthétique du pipeline de rendu graphique.
3.2
CUDA
Nvidia CUDA (Compute Unified Device Architecture) est un framework logiciel adapté aux
architectures que nous avons présenté en section
Figure 3.2 – La plateforme logicielle CUDA : des librairies, un mécanisme d’abstraction matérielle et un branchement de compilateurs.
CUDA est une abstraction matérielle : NVidia a toujours masqué l’architecture de ces GPU
derrière des interfaces de programmation et le programmeur n’écrit jamais directement ”dans
le métal”. L’abstraction offre deux avantages : 1) un modèle de programmation de haut niveau
15
qui isole des aspects difficile de la programmation des GPUs, et 2) l’architecture matérielle peut
changer, mais l’API demeure en place, ce qui permettra de conserver les codes préalablement
écrits.
Nous n’entrerons pas dans les détails de CUDA et insistons pour renvoyer le lecteur aux travaux de Jimmy Petterson et Ian Wainwright ([12]). Leur thèse constitue la ”pièce manquante”
dans la documentation pour parvenir à l’écriture de kernels optimisés. Cette lecture est recommandée en priorité avant même les guides de programmation CUDA [13] et [14].
3.3
OpenCL
OpenCL est un framework pour écrire des programmes tournant sur des machines hétérogènes : multiprocesseurs, GPUs, Cell BE, processeur Fusion et autres. CUDA et OpenCL se
ressemblent par bien des aspects. Un retour d’expérience détaillé se trouve dans [12].
3.4
CAPS HMPP et PGI9
HMPP (Hybrid Manycore Parallel Programming) de CAPS Entreprise [15] et PGI9 de Portland Group prennent une autre approche axée compilateur. La solution existe avant l’avènement
des GPUs à l’origine pour faciliter la programmation de machines vectorielles telles que des
générations de NEC et de systèmes Cray.
Un programme séquentiel classique est annoté de directives en respectant un placement et
une syntaxe qui indiquent où et quand les transferts mémoires surviennent, et quelles fonctions
traduire dans un langage SIMD cible avec les données d’entrées-sorties correspondantes.
L’avantage de cette approche est que le code n’est pas modifié mais seulement annoté. Compilé
avec un compilateur standard, ces directives sont ignorées au même titre que des commentaires.
Compilée avec le compilateur désigné, une portion de code, ou codelet, est généré par le compilateur pour la région à accélérer, produite dans un code source qui peut être ensuite modifiée
et dans une librairie partagée appelée à l’exécution. Bien entendu certains algorithmes n’ont pas
de propriétés concurrentes parallèles et ceux-ci ne devraient pas être candidats au chargement
sur un accélérateur vectoriel. Le choix de la librairie dynamique permet également de porter le
code sur différente architecture. HMPP compile des codelets pour les GPUs d’ATI/AMD, Nvidia
CUDA et les processeurs avec des SSE activées. PGI9 ne le fait que pour les machines supportant
les drivers CUDA, mais offre la possibilité d’écrire des Kernels CUDA directement en Fortran,
rendant le code plus homogène.
Quelques aspects difficiles de la programmation GPU sont pris en charge par ces compilateurs,
tels que les padding mémoires et les recouvrements.
Dans le contexte de code industriel existant, l’emploi de ces directives donne une image plus
claire des choix de transferts mémoires à réaliser et permettent d’estimer d’un premier jet si le
programme est effectivement accéléré, tout en restant portable sur les différentes architectures.
Les kernels critiques peuvent ensuite être modifiés ou réécrits pour gagner davantage en vitesse.
Selon la complexité de l’algorithme, le code généré demeure relativement bien inteprétable. Le
processus de compilation passe du simple au double, et il peut arriver que parfois de légères
modifications du code existant soient nécessaires pour que le compilateur gère correctement
l’économie des registres et de mouvements dans les caches.
16
Au demeurant, les défauts d’HMPP sont nombreux et l’intérêt personnel de maintenir un
code historique par l’adjonction de directives nous semble un exercice à l’intérêt si limité que
nous avons préféront l’éviter. Les licences payantes encouragent les programmeurs à s’orienter
vers des solutions qui garantissent leur indépendance technologique. Un exemple de code annoté
se trouve en section
3.5
Autres solutions
Les travaux qui suivent ont aboutis à un compilateur qu’il est intéressant de noter.
L’accélération d’un solveur de chimie cinétique : une intégration numérique calculatoirement
intensive de l’évolution d’espèces chimiques et de leur réactions – par [16].
Et [17], le modèle d’advection de traceur qui modélise le transport de constituants atmosphériques sous le forçage des champs des prévisions de vents par le modèle Runge-Kutta du WRF. Le
benchmark, employé pour valider le hardware, est un schéma d’advection positif-défini de 5ème
ordre. L’advection est appelée une fois par itération de Runge-Kutta dans le solveur. Chaque
dimension est advectée séparément et ajustée par une approximation en différence-finie.
Les codes des deux projets sont distribués sous leurs versions parallèles (OpenMP) originales
et accélérées [18, 19].
Les travaux de [20], également sponsorisé par NCAR (National Center for Atmospheric Research) sont axés sur le modèle de résolution de la couche nuageuse sur GPU, par sous mailles en
volumes finis. Le modèle historique comporte 8K lignes de Fortran, calcule en précision double.
Le problème est petit, mais présente une grande intensité de calcul. Le portage de ce code a suivi
la démarche adoptée par le groupe cité au-dessus.
Tout ces travaux menés conjointement par NOAA et le WRF ont aboutis à un compilateur
Fortran à C à Cuda (F2C), puis Fortran à Cuda (F2ACC) [21]. Il a été laissé supposé qu’ils soient
complétés par l’écriture de kernels Cuda en Fortran compilés via PGI9. Le premier compilateur,
F2C, exige des modifications du source existant. Le deuxième non, mais il produit parfois des
kernels incorrects. Dans tout les cas, le code généré est passé en revue.
D’autre interfaces semblables à CUDA existent telles que RapidMind [22] et Peakstream [23]
(acquis et absorbé par Google en 2008). RapidMind est même allé encore plus loin en adaptant
sa plate-forme virtuelle sur Cell, ATI et Nvidia, sans utiliser la plate-forme de ce dernier. Citons
enfin GPUocelot, un framework de compilation dynamique pour systèmes hétérogènes, dont un
backend CUDA. Licence BSD.
17
18
4
4.1
Précision numérique
Introduction
Avant de calculer vite, il faut calculer juste. Si on ne rencontre pas un critère d’exactitude,
et c’est le cas dans les standards à virgule flottante, alors il nous faut connaı̂tre le degré de
précision de nos calculs. Le calcul scientifique est très demandeur en précision double de 64 bits,
en particulier lorsque sont appliquées des techniques itératives sujettes à la propagation d’erreurs
d’arrondis ou des modèles à hautes échelles d’amplitudes.
Le calcul généraliste sur GPU est devenu un exercice très attractif en physique, mais dans ces
processeurs et jusqu’à présent, la vitesse a grignoté sur la précision. Introduire davantage d’erreur
de calcul en échange de vitesse nous mène à cette question [24] : « Puisque la plupart des calculs
en virgule flottante ont de toute façon des erreurs d’arrondis, est-ce important si les opérations
arithmétique basiques introduisent un tout petit plus d’erreur d’arrondis que nécessaire ? ». Si
on considère le cas de rendu d’images en temps réel, la réponse est non, le matériel spécialisé
calculant dans l’espace discret.
En élargissant le spectre dans le domaine de la simulation, nous essayons de donner un
début de réponse pour une classe de matériel et une opération par l’étude des performances et
des précisions numériques obtenues par la série des GPU Nvidia T10, en précisions simples et
doubles, sur la multiplication de matrices par CUBLAS, l’implémentation CUDA du BLAS.
La régularité et la prédictabilité des accès de données, le requis hautement parallèle et la
réutilisation des données en cache, placent cette fonction comme un bon candidat à l’évaluation
des performances crêtes des GPUs.
Les résultats sont comparés à deux implémentation de référence du BLAS, largement distribuées : ATLAS et MKL. Ils montrent que les précisions sont plus basses, d’un ordre variant entre
un et deux, en comparaison avec un calcul sur CPU en simple (32 bits) et double (extension à
80-bits) précisions flottantes.
19
4.2
Produit matriciel parallèle
Soit le produit matriciel C = A × B de dimensions N × N . Le produit calculé parallèlement
sans bloc requiert un thread par élément du résultat final. A et B sont chargés N fois depuis la
mémoire globale. La réutilisation de donnée de ce calcul est en O(n).
Sur la base d’un calcul par bloc, un bloc de thread de taille b gère une sub-matrice Csub de
C de dimensions b × b. A et B ne sont chargés que N/b fois depuis la mémoire globale, ce qui
représente l’équivalent en terme de bande passante économisée.
Les blocs résident dans les caches de mémoires partagés.
Le kernel, implémenté dans CUBLAS 2.1, écrit par [25], est une stratégie de mise en cache
avec une définition explicite de la dimension des caches optimale pour un GPU GTX280. Les
spécifications techniques des Tesla sont fermées par le constructeur, mais les informations données
par le driver CUDA indique que le nombre de cores et d’unités de caches est équivalent sur une
Tesla T10. Ce ne sera toutefois pas le cas sur les architectures futures.
La dimension de Csub est bornée par le nombre de threads par bloc. En conservant les blocs
carrés, et le nombre maximum de threads par blocs étant 512, la taille des blocs est en conséquence
162 . Chaque thread porte un identificateur unique sous la combinaison de l’identificateur du bloc
et de celui du thread à l’intérieur du bloc, identifiant quelle donnée calculer.
Avec A, B et C respectivement de dimensions (m, k), (k, n) et (m, n). Csub est égal au produit
de deux matrices rectangulaires : la sub-matrice A de dimension (b, m) qui porte les mêmes
indices de lignes que Csub et la sub-matrice de B de dimension (n, b) qui a les mêmes indices de
colonne que Csub . Ces deux matrices rectangulaires sont divisées en autant de matrices carrées de
dimension b nécessaires et Csub est calculée comme la somme des produits de ces matrices carrées.
Chacun de ces produits est réalisé en chargeant d’abord les deux matrices correspondantes en
cache, chaque thread gérant un élément de la matrice, puis chaque thread calcule un élément du
produit. Plus précisément, ce ne sont pas deux sub-matrices qui sont chargés, mais des couple
matrice et vecteur variant. Les données du vecteur sont conservées dans les registres. Chaque
thread accumule ensuite le résultat de ces produits dans un registre puis le résultat est retourné
en mémoire globale. Une première proposition d’une telle répartition de charge apparaı̂t dans
[26].
Figure 4.1 – Produit matriciel micro-parallèle par bloc
Calculé de cette façon, en utilisant le maximum de registres et de cache peut être vu comme
une stratégie plus générale pour diviser le nombre de relectures. L’idée est applicable en dehors
20
d’une unité graphique isolée : en divisant de très grande matrices entre plusieurs Tesla connectées par PCIe16, voire à un cluster hybride. Toutes les unités effectuent leur part du calcul et
produisent une matrice m × n. C’est coûteux en mémoire et cela conduit à collecter autant de
sub-matrices qu’il y a de charge distribuées. Le calcul peut être asynchrone et recouvert, mais
ne passe pas à l’échelle.
Figure 4.2 – A une échelle à gros grain, une distribution du calcul impliquant O(n × u) de
surplus mémoire où n est la dimension de la matrice et u le nombre d’unités.
L’autre façon de mettre à contributions plusieurs GPU est par la mise en oeuvre d’une couche
de librairie, ainsi le CUDA Wrapper [27], implémentée en pré-chargement forcé, de telle sorte que
les appels à CUDA aux allocations matérielles soient interceptés par elle pour avoir quelques
avantages.
1) Virtualiser les GPU physiques par un mappage dynamique. Le matériel visible à l’utilisateur
est un ensemble consistant de matériel physique et une barrière intrinsèque sur le système partagé
prévient les saut accidentels d’un appareil à l’autre.
2) Une affinité NUMA, peut être établies entre cores CPU and GPU, pour une économie de
bande passante.
4.3
Spécification de la norme flottante
Selon [13], l’arithmétique binaire flottante des T10 est compatible avec la norme [24]IEEE754, avec plusieurs déviations.
– Les modes arrondis ne sont pas dynamiquement configurables et doivent explicitement
définis.
– Pas de mécanisme pour détecter une exception.
– Les opérations se comportent en masquant les exceptions et retournent la réponse masquée
telle que définie dans l’IEEE-754 en cas d’exception.
– Pour les mêmes raisons, l’encodage des SNaN est supporté, mais il n’est pas signalé.
– La valeur absolue et la négation ne sont pas compatible avec l’IEEE-754 au regard des
NaNs.
Conclusion : pas conforme à l’IEEE-754.
21
4.4
CUBLAS
Dans sa version 2.1, CUBLAS peut substituer toute autre implémentation du BLAS moyennant quelques modifications mineures et avec cependant plusieurs limitations. Cependant, le
comportement par défaut de la librairie est insuffisant : il réalise les allocations, les transferts
et les désallocations mémoires sur GPU à chaque appel de fonction d’une routine depuis l’hôte.
En général, pour une utilisation efficace, seuls les transferts ou une partie des transferts doivent
être effectifs. De plus, la mémoire sur l’hôte doit être alignée et non paginée pour atteindre les
performances maximales [14]. Ceci conduit à introduire du code dans un programme existant.
Ainsi les transferts de bandes de matrices peuvent survenir plusieurs instructions avant l’appel
proprement dit au kernel, et les allocations et désallocations mémoires peuvent avoir lieu une
seule fois dans un cycle d’initialisation et de finalisation, et gardées réservées. Ceci est renforcé si
le calcul a un comportement redondant. L’autre phénomène remarqué est le temps d’initialisation
du GPU, qui a un effet particulièrement notable sur les performances. L’exécution au préalable
d’un kernel quelconque y remédie.
Si le programme est écrit en Fortran, les appels de requête d’allocation à de la mémoire alignée
et non-paginées passe par des liaisons en C pour la manipulation de pointeurs. La comparaison
des performances entre une liaison directe de CUBLAS et son usage optimal se voit sur les
graphes
La distribution de CUBLAS en version 2.1 est incomplète, dépourvue de toute fonctions
complexe de niveau 3. Elles se trouvent en partie dans la version Beta 3.0.
22
Listing 4.1 – Code de liaison pour les requêtes d’allocation de mémoire non–paginées sur l’hôte
module cuda
use iso_c_binding
interface
! cudaMallocHost
integer(C_INT) function cudaMallocHost(buffer,
size) bind(C,name="cudaMallocHost")
use iso_c_binding
implicit none
type (C_PTR) :: buffer
integer (C_SIZE_T), value :: size
end function cudaMallocHost
end interface
! alloue dans la RAM hote m*m elements de p octets dans le tableau A
! de memoire alignees sur 64 bits, non paginees pour garantir
! la bande passante maximale entre hote<->gpu
subroutine allocateforgpu( A, m, p, cptr )
! les zones de memoires constante, pile ou tas allant
! dans le gpu requierent la reference d’un pointeur
real, dimension(:,:), pointer :: A
integer :: m
integer :: r
type(C_PTR) :: cptr
integer(C_SIZE_T) :: p
r = cudaMallocHost (cptr, m*m*p)
! bind the C pointer
call c_f_pointer (cptr, A, (/ m, m /))
end subroutine allocateforgpu
4.5
Précision numérique
Pour deviner la précision numérique de CUBLAS sur T10, nous calculons la fonction GEMM
C = αAB + βC, de matrices carrées de dimensions m, avec B et C égaux à un entier λ positif.
Les colonnes impaires de A sont égales à λ et les colonnes paires de A égales à une valeur flottante
ε comprise entre 0 et 1. Le résultat est une matrice C constante.

λ
A= |
λ
ε
|
ε
...
...
λ
|
λ

ε
| 
ε
B=λ, C=λ
Résultant en
C = α(λ2 + ελ)
k
+ βλ
2
Nous calculons depuis l’espace réel vers l’espace flottant, avec une approximation δ, donnant le
résultat ā.
23
R→F
a → ā
ā = a(1 + δ) ∼ Ψ
où δ est l’epsilon machine. Sur un CPU Intel Xeon Nehalem ou un Core 2 Duo :
epsilon(double précision) = 2.220446049250313E −16
epsilon(simple précision) = 1.1920929E −07
4.5.1
Résultats
Nous prenons λ = 2 et ε = 10−n avec n = 1..N . Nous avons B = C = 2. Comme résultat
C = 4m + 2εm + 4. La dimension m de la matrice varie entre 256 et 12000. La précision est la
mesure de la différence relative entre le résultat exact calculé sur le CPU en précision double et
le résultat fourni par le GPU.
Les graphes
La variabilité et l’annulation des précisions est soit due au mode d’arrondis, à la dénormalisation des nombres ou un calcul exact. Le dernier cas ne survient qu’avec ATLAS pour ε = 10−1 .
GEMM avec MKL montre une grande stabilité dans la précision. ATLAS n’est exécuté que
sur un seul core de CPU et minimse davantage l’erreur relative. Toutefois la fluctuation de la
précision est en elle-même un facteur d’imprédictabilité, donc d’imprécisions.
4.6
Performances
En 32-bits la fonction SGEMM atteint 370 Gflops de performances crêtes. Ceci survient pour
des matrices à partir de carré 12K. A partir de carré 4K, le calcul atteint 300 Gflops. C’est un fait
connu en GPGPU : l’efficacité est atteinte avec des matrices suffisamment grandes. En dessous de
carré 2K éléments, la latence mémoire à plus d’impact sur le calcul et en dessous d’une certaine
dimension, des ALUs sont laissées inactives.
L’augmentation de la taille des matrices à moins d’impact sur l’exécution du kernel que sur le
transfert des données. L’utilisation de flots, de copie et d’exécution concurrente permet de diviser
les matrices A et C en entrée en bande par lignes et de les transférer en asynchrone, tandis que
le kernel calcule une autre bande de données.
Le facteur de gain au regard de la comparaison des deux librairies survient en précision simple
pour des matrices supérieures à 10002 , atteignant 2.6 à 70002 . En double précision, MKL gagne,
et les deux solutions convergent vers une puissance maximale de 67 Gflops.
24
Computationnal Power
400
350
GFLOPS
300
250
200
150
CUBLAS dll
CUBLAS pinned memory
MKL
ATLAS
100
50
0
2000
4000
6000
8000
10000
12000
Matrix dimension
Figure 4.3 – Puissance de calcul de la fonction GEMM en simple précision
Sur des mémoires dépourvues d’EEC, le calcul répété de grande matrices constantes peut
fournir, moyennant l’ajout d’un algorithme de scan parallèle, une détection des occurrences de
fautes mémoires et donner une estimation du Temps Moyen Entre Défaillance (TMED). Par
exemple, si le TMED pour un core isolé est de 6 mois, sur des unités de 240 cores, il ne serait
que de 16 heures et 48 minutes (en établissant une distribution indépendante du temps).
Au regard des performances, une image plus précise des mécanismes de synchronisation et de
transferts mémoires s’avère nécessaire pour les kernels critiques, à travers l’examen de l’assembleur PTX et l’usage de désassembleurs, tels que Decuda [28]. Celui-ci peut fournir davantage
d’information pour l’optimisation des mouvements de caches, comprendre les cycles d’horloge et
de manière générale une meilleure compréhension du hardware.
Pour finir, il faut considérer ces architectures multicoeurs sous leur aspect hybride et dans
leur ensemble pour obtenir le meilleur entre les deux mondes. Les techniques d’autotuning [29]
qui ont déjà inspirées les développement d’ATLAS en sont le meilleur exemple. Sur de vrais
problèmes, il est crucial d’obtenir la contribution de toutes les unités de calculs, fonctionnants
de concerts lorsque c’est possible, ou d’utiliser les plus adaptées aux problèmes à résoudre.
25
Computationnal Power
70
60
GFLOPS
50
40
30
CUBLAS dll
CUBLAS pinned memory
MKL
ATLAS
20
10
0
1000
2000
3000
4000
5000
6000
7000
8000
Matrix dimension
Figure 4.4 – Puissance de calcul de la fonction GEMM en double précision
Speedup Cublas / MKL
3
2.5
speedup
2
1.5
1
single precision
double precision
0.5
0
1000
2000
3000
4000
5000
6000
7000
8000
Matrix dimension
Figure 4.5 – CUBLAS vs MKL : facteur d’accélération fonction de la dimension de la matrice.
Figure 4.6 – CUBLAS GEMM Performance
26
= 10−1
SGEMM - accuracy : relative difference
0.0001
1e-05
1e-05
Relative difference
Relative difference
SGEMM - accuracy : relative difference
0.0001
1e-06
1e-07
1e-08
2000
4000
6000
8000
10000
1e-06
1e-07
CUBLAS
MKL
ATLAS/xeon
ATLAS/core2
CUBLAS
MKL
ATLAS/xeon
ATLAS/core2
1e-08
12000
2000
4000
Matrix dimension
= 10−3
0.0001
1e-05
1e-05
1e-06
2000
4000
6000
8000
10000
1e-08
12000
2000
= 10−4
4000
6000
8000
10000
12000
Matrix dimension
= 10−5
SGEMM - accuracy : relative difference
= 10−6
1e-06
Relative difference
1e-05
Relative difference
12000
CUBLAS
MKL
ATLAS/xeon
ATLAS/core2
Matrix dimension
SGEMM - accuracy : relative difference
10000
1e-06
1e-07
CUBLAS
MKL
ATLAS/xeon
ATLAS/core2
1e-08
8000
SGEMM - accuracy : relative difference
0.0001
1e-07
6000
Matrix dimension
Relative difference
Relative difference
SGEMM - accuracy : relative difference
= 10−2
1e-06
1e-07
CUBLAS
MKL
ATLAS/xeon
ATLAS/core2
1e-08
2000
4000
6000
8000
Matrix dimension
10000
1e-07
CUBLAS
MKL
ATLAS/xeon
ATLAS/core2
1e-08
12000
2000
4000
6000
8000
10000
12000
Matrix dimension
Figure 4.7 – Précisions de SGEMM : différences relatives, epsilon variant entre 10−1 to 10−6 :
CUBLAS 2.1 sur Nvidia GT200, ATLAS 3.9.15 et MKL 10.0 sur CPU Xeon Nehalem 8 cores
X5570 @ 2.93GHz
27
DGEMM - accuracy : relative difference
= 10−1
DGEMM - accuracy : relative difference
1e-12
CUBLAS
MKL
ATLAS
CUBLAS
MKL
ATLAS
Relative difference
Relative difference
1e-13
= 10−2
1e-13
1e-14
1e-14
1e-15
1e-15
1e-16
1e-16
2000
4000
6000
8000
10000
12000
2000
4000
Matrix dimension
DGEMM - accuracy : relative difference
= 10−3
10000
12000
= 10−4
1e-13
CUBLAS
MKL
ATLAS
Relative difference
CUBLAS
MKL
ATLAS
Relative difference
8000
DGEMM - accuracy : relative difference
1e-13
1e-14
1e-15
1e-16
1e-14
1e-15
1e-16
2000
4000
6000
8000
10000
12000
2000
4000
Matrix dimension
DGEMM - accuracy : relative difference
6000
8000
10000
12000
Matrix dimension
= 10−5
DGEMM - accuracy : relative difference
1e-13
= 10−6
1e-12
CUBLAS
MKL
ATLAS
1e-13
Relative difference
Relative difference
6000
Matrix dimension
1e-14
1e-15
CUBLAS
MKL
ATLAS
1e-14
1e-15
1e-16
1e-16
2000
4000
6000
8000
Matrix dimension
10000
12000
2000
4000
6000
8000
10000
12000
Matrix dimension
Figure 4.8 – Précisions de DGEMM : différences relatives, epsilon variant entre 10−1 to 10−6 :
CUBLAS 2.1 sur Nvidia GT200, ATLAS 3.9.15 et MKL 10.0 sur CPU Xeon Nehalem 8 cores
X5570 @ 2.93GHz
28
DGEMM - accuracy : relative difference
= 10−7
DGEMM - accuracy : relative difference
1e-13
CUBLAS
MKL
ATLAS
Relative difference
Relative difference
CUBLAS
MKL
ATLAS
1e-14
1e-15
1e-16
1e-14
1e-15
1e-16
2000
4000
6000
8000
10000
12000
2000
4000
Matrix dimension
DGEMM - accuracy : relative difference
= 10−9
8000
10000
12000
DGEMM - accuracy : relative difference
= 10−10
1e-13
CUBLAS
MKL
ATLAS
CUBLAS
MKL
ATLAS
Relative difference
Relative difference
6000
Matrix dimension
1e-12
1e-13
= 10−8
1e-13
1e-14
1e-14
1e-15
1e-15
1e-16
1e-16
2000
4000
6000
8000
10000
12000
2000
4000
Matrix dimension
DGEMM - accuracy : relative difference
= 10−11
10000
12000
= 10−12
1e-13
CUBLAS
MKL
ATLAS
CUBLAS
MKL
ATLAS
Relative difference
Relative difference
8000
DGEMM - accuracy : relative difference
1e-12
1e-13
6000
Matrix dimension
1e-14
1e-14
1e-15
1e-15
1e-16
1e-16
2000
4000
6000
8000
10000
12000
2000
4000
Matrix dimension
DGEMM - accuracy : relative difference
= 10−13
8000
DGEMM - accuracy : relative difference
1e-13
10000
12000
= 10−14
1e-14
CUBLAS
MKL
ATLAS
Relative difference
CUBLAS
MKL
ATLAS
Relative difference
6000
Matrix dimension
1e-14
1e-15
1e-16
1e-15
1e-16
2000
4000
6000
8000
Matrix dimension
10000
12000
2000
4000
6000
8000
10000
12000
Matrix dimension
Figure 4.9 – Précisions de DGEMM : différences relatives, epsilon variant entre 10−7 to 10−14 :
CUBLAS 2.1 sur Nvidia GT200, ATLAS 3.9.15 et MKL 10.0 sur CPU Xeon Nehalem 8 cores
X5570 @ 2.93GHz
29
30
5
Cas test
Figure 5.1 – Inscription de la méthode dans la CFD
Le calcul selon une méthode aux différence finies, de stencil ou d’interpolations linéaires sur
des matrices de l’ordre du teraoctet ne tient pas à l’intérieur de la mémoire d’un ordinateur
isolé. Ce traitement, parfaitement parallèle, avec une forte localité des données, nous oblige à
utiliser un cluster où les noeuds portent plusieurs coprocesseurs algébriques. Le seul moyen de
dépasser les hautes latences de bandes passante entre les unités est de recouvrir les calcul par le
transferts asynchrones de fragments de données. Selon un modèle similaire à celui qui se trouve
dans une architecture CUDA, avec des blocs de threads calculant pendant que d’autres stockent
les données, nous montrons que les délais de synchronisation peuvent être réduits en calculant
d’abord les cellules aux interfaces. Nous allons détailler ceci étape par étape.
31
5.1
Méthode aux différences finies
Une méthode d’approximation des dérivées premières, secondes et mixtes utilisant l’expansion
des séries de Taylor et les polynômes. Point de départ : les équations de conservations en formes
différentielles. Le domaine de la solution est recouvert par une grille. En chaque point de la grille
est fait une approximation de l’équation différentielle en remplaçant les dérivées partielles par
des approximations des termes aux valeurs nodales des fonctions. Le résultat est une équation
algébrique en chaque noeud de la grille. La valeur des variables sur ce noeud et un certain nombre
de voisins apparaissent comme des inconnues[30].
∂
∂(ρuj φ)
=
∂xj
∂xi
∂φ
Γ
+ qφ
∂xj
Ici, ρ, ui , Γ et qφ sont connues, seule φ est inconnue. Toutes les autres variables sont fixées à
leur valeur de l’itération précédente. Chaque noeud porte une inconnue et fournit une équation
algébrique, soit une relation entre la variable et celles des voisins.
La solution du système est une approximation de la solution de l’EDP. Chaque terme de
l’EDP est remplacée par une approximation au différence finie. Le nombre d’équation égale le
nombre d’inconnues.
Aux limites, les valeurs des variables sont données (conditions de Dirichlet), donc aucune
équation n’est nécessaire. Quand les conditions limites impliquent des dérivées (comme dans le
cas des conditions de Neumann), les conditions de bord doivent être discrétisées pour fournir les
équations.
Dans un schéma aux différences centrées, les dérivées secondes s’expriment
2
∂ φ
∂x2
≈
i
∂φ
∂x2
−
i− 12
1
2 (xi−1
∂φ
∂x2
i+ 12
− xi+1 )
≈
φi+1 (xi − xi−1 ) + φi−1 (xi+1 − xi ) − φi (xi+1 − xi−1 )
1
2 (xi+1 − xi+1 )(xi+1 − xi )(xi − xi−1 )
les derivées premières
∂φ
∂x
i+ 21
φi+1 − φi
≈
xi+1 − xi
et
∂φ
∂x
≈
i− 21
φi − φi−1
xi − xi−1
et en espacement équidistant
∂2φ
∂x2
où ∆x est l’espacement des cellules.
32
≈
φi+1 + φi−1 − 2φi
(∆x)2
(5.1)
5.2
Le système d’équation algébriques
Une équation par point de la grille portant la valeur des variables en ce point ainsi que les
valeurs des noeuds voisins. Cette équation peut inclure des termes non linéaires. Le processus de
résolutions numériques implique une phase de linéarisation. Le système est de la forme :
X
Ap φ p +
Al φl = Qp
(5.2)
l
p est le noeud où l’EDP est approximée et l’index l traverse les noeuds voisins impliqués dans
l’approximation en différence finie : p et ses voisins réprésente une ”molécule de calcul”.
Ce système tridiagonal, creux, est noté matriciellement : Aφ = Q.
A est la matrice carrée des coefficients, φ le vecteur contenant les valeurs des variables aux
noeuds de la grille et Q le vecteur contenant les termes de la parties droite de l’équation
Figure 5.2 – Aφ = Q : le système d’équation algébrique. A droite, une représentation compressée
de la matrice A.
Dans la pratique, on ne réalise pas expressement le produit matrice-vecteur mais une convolution en chaque pixel (ou voxel, en 3D) du domaine discret. Des méthodes plus élaborées, calculant
sur des maillages 3D déformables ou non structurés montreront un système similaire, pentadiagonal, non régulier, dans lequel le produit est réalisé. Le système étant creux, une représentation
complète de la matrice est prohibitf 1 .
En 2D, φ représentera une surface, sous forme d’un tableau linéarisé en mémoire. A est
l’opérateur de convolution. Il rassemble et pondère les cellules voisines avec la cellule centrale
pour réaliser le calcul
1. Ceci est renforcé s’il faut faire des transferts mémoire depuis l’hôte vers les unités de calcul. Le produit
vecteur – matrice creuse fait l’objet de nombreux travaux dans le monde du calcul scientifique. Le problème
est bien adapté au GPU : traité sur des architectures non-unifiés avec des méthodes de GPGPU traditionnelle
[31] ou sur des architectures plus récentes (CUDA et CTM) dans [32, 33] pour la méthode du gradient conjugué
préconditionné par Jacobi. Plusieurs formats on été testés sur des variétés de matrices courantes dans leur domaine
d’application[34]. Les meilleurs cas atteignent respectivement 15 et 10 Gflops, en précisions simple et double.
33
5.3
L’opération Stencil
Plus généralement, dans le monde du traitement d’image, les filtres de convolutions sont des
filtres d’images et le Laplacien est employé comme détécteur sommaire de contour.
En CFD, l’opération stencil évalue les sommes des flux (masses, vélocités, énergies) en chaque
point discret de la grille.
Figure 5.3 – Stencils a un, deux et trois dimensions
Listing 5.1 – L’algorithme de calcul du stencil en deux dimensions
do pp=1,p
do j=2,nlocy+1
do i=2, nlocx+
ll=i+(j-1)*(nlocx+2)
VV(ll,pp) = s(2)*V(ll-nlocx-2,pp)
+ s(4)*V(ll-1,pp)
+ s(5)*V(ll,pp)
+ s(6)*V(ll+1,pp)
+ s(8)*V(ll+nlocx+2,pp)
end do
end do
end do
Nous examinons le kernel dans deux cas.
Dans le premier cas, on suppose que la charge de calcul est distribuée entre les noeuds. Les
interfaces sont échangées à chaque itération sur un canal de communication de haute latence.
Dans le deuxième cas les calculs restent dans le GPU jusqu’à convergence : soit pour une
méthode de résolution par itération jacobienne, soit pour la résolution des équations de Laplace. 2
Le calcul de stencil à 5, 7, 9 ou 27 points a aussi été éxaminé [36] dans le cas d’utilisation
d’une machine hybride pour décomposer et calculer spécifiquement le domaine parallèlement sur
CPU et GPU.
2. Une résolution spécifique de l’équation de Laplace en 3D éxige des méthodes numériques plus musclées pour
réduire la compléxité en temps et en mémoire. Multipôle rapide [35] et convolutions/déconvolutions par FFT.
34
5.3.1
Transfert des interfaces
Entre chaque itération, les interfaces (ou halos) sont communiquées aux unités de calcul
traitant les sous-domaines partageant une interface.
Figure 5.4 – Dans cet exemple en dimension 1, le domaine est décomposé en deux. Le stencil est
calculé en chaque cellule intérieure du sous domaine (cellules blanches) puis les interfaces sont
échangées.
Reprenons la topologie du domaine sur l’architecture de notre machine à calculer. Le nombre
d’interface est fonction du niveau de granularité :
−1
x
2RΠN
i=1 ai Π (mx − 1)
(5.3)
soit le diamètre du stencil fois la taille des interfaces fois le nombre de segmentations. La
distribution de la charge est équivalente dans toutes les unités de traitements. Concrètement,
nous avons R, le rayon du stencil de 1 à 5, un problème de dimension 3, et deux subdivisions,
m0 = 4, entre 4 GPUs et m1 , le nombre de flots à estimer sur la base de la taille du taux
d’occupation des GPUs et de la dimension du domaine.
Avec une restriction du problème à un noeud équipé d’une Tesla S1070 (soit 4 Tesla C1060
sur un même bus) le processus d’itération Jacobienne distribué est proposé ainsi :
Subdivision cartésienne du domaine en sous-domaine
Subdivision des sous-domaine en bandes
Chargement asynchrone des bandes dans chaque GPUs
Subdisivion des bandes en blocs
Tant que la solution n’a pas convergé
Mise en cache des blocs
Exécution des kernels
Calcul de la norme résiduelle (Préfixe parallèle)
Si la solution n’a pas convergé
Copie des interfaces dans la mémoire de l’h^
ote
Section critique : transit des interfaces
Chargement des halos dans les GPUs
fin
Ceci est un cas d’école de traitement parallèle. Il est préférable que la dimension des blocs
soit le plus uniforme possible. Les délais de communication sont l’étape de synchronisation entre
les unités de traitements. La durée du temps de synchronisation est majorée par le temps de
transmission de l’interface la plus importante.
35
Remarque : les changement de contextes pour le pilotage de plusieurs GPUs à partir d’un
seul noeud sont également en environnement parallèle (pthread ou OpenMP) et la situation de
concurrence sur la bande passante lors de l’échange des interfaces est une section critique.
5.3.2
Kernel CUDA
Chaque bloc de threads CUDA traite traite à son tour un sous-ensemble du domaine. La
taille des blocs doit être très largement inférieure à la dimension totale de la grille, afin d’avoir
suffisemment de blocs pour maintenir tout les multiprocesseurs occupés.
Dans le cas 3D, la quantité limitée de mémoire cache oblige à répéter plusieurs fois le placements de 3 tranches de données du bloc en cache. Le kernel a donc une boucle extérieure pour
mise en cache avant synchronisation.
Figure 5.5 – Convolution 3D : stratégie de mise en cache par tranche
A chaque itération, la tranche inférieure n’est plus nécessaire, la tranche supérieure est chargée, et les pointeurs associés décalés avant le calcul (fig
L’optmisation de ce kernel concerne :
–
–
–
–
La fusion des accès mémoires de chargements et de stockages
Le recouvrement entre les warps et/ou blocs actifs pour limiter la latence mémoire
La minimisation du nombre d’opérations entières d’indexation des tableaux
L’utilisation maximale du cache et des registres
Ce kernel est encore largement sous-optimal :
Il est prévu pour des domaines de toute dimension, et la grille étant dynamiquement configurée, le module peut ne pas être entier : les blocs de threads doivent déterminer s’il se situent sur
la partie non-entière d’un module et à l’intérieur de ce bloc, tester s’ils sont encore à l’intérieur
du domaine. Or comme on le sait, les GPUs n’aiment pas les branchements conditionnels.
Les accès des halos latéraux sont non fusionnés, divergents, les conflits de banques multiplient
par 2x les lectures mémoires.
Les 16 Ko de mémoire cache sont le facteur limitant à la dimension des blocs : dépasser
cette limite impose une stratégie de mise en cache circulaire. Cependant, l’absence de contrôle
de l’ordonnanceur, excepté la barrière de synchronisation, rend la performance du globale kernel
relative au thread le plus long. Il convient donc de réduire le nombre d’itérations individuelles
des threads 3 .
36
70.7
GPU KERNEL
26.4
8.0
GPU + E/S
3.8
32 bits
2.0
CPU
64 bits
1.3
0
10
20
30
40
50
Computational Intensity (GFLOPS)
60
70
80
Figure 5.6 – Convolution 3D de rayon 1 sur Tesla C1060 et CPU Nehalem. Intensité de calcul
crête, transferts mémoires synchrones.
L’histogramme
HMPP
Une autre solution consiste à annoter le code fortran de directives HMPP et de compilation
via hmppfort. Le code ainsi compilé produit un kernel CUDA. Le kernel est ensuite compilé dans
une librairie partagée (.so). Cette librairie est appelée au runtime, après que les librairies HMPP
aient validé la présence du GPU.
Listing 5.2 – Code fortran de la fonction de stencil 3D annoté de directives HMPP
!$HMPP <G1> group, target=CUDA
!$HMPP <G1> stencil3d codelet, args[V,ss].io=in, args[VV].io=out
subroutine stencil3d(nlocx,nlocy,nlocz,V,VV,s)
!$HMPPCG noparallel
do pp = 1,p
!$HMPPCG parallel
do k=2,nlocz+1
!$HMPPCG parallel
do j=2,nlocy+1
!$HMPPCG unroll(4)
do i=2,nlocx+1
ll = i + (j-1)*(nlocx+2)+(k-1)*(nlocx+2)*(nlocy+2)
VV(ll,pp)=
s(5)*V(ll,pp)
+ s(4)*V(ll-1,pp)+s(6)*V(ll+1,pp)
+ s(2)*V(ll-nlocx-2,pp) + s(8)*V(ll+nlocx+2,pp)
+ s(14)*V(ll-(nlocx+2)*(nlocy+2),pp)
+ s(23)*V(ll+(nlocx+2)*(nlocy+2),pp)
end do
3. Nota : pas dans un cas général sans branchement conditionnel et où tous les accès seraient alignés.
37
Figure 5.7 – Convolution 3D de rayon 1 sur Tesla C1060 et CPU Nehalem. temps d’exécutions
Listing 5.3 – Appel au codelet HMPP
! allocation memoires dans l’accelerateur
!$HMPP <G1> allocate
...
! chargement des donnees dans l’accelerateur
!$HMPP <G1> stencil3d advancedload, args[nlovx,nlocy,nlocz,p,V,ss]
! appel au codelet
!$HMPP <G1> stencil3d callsite, \&
!$HMPP args[nlocx,nlocy,nlocz,p,V,ss].advancedload=true
call stencil3d(nlocx,nlocy,nlocz,p,V,VV,ss)
...
! chargement du resultat dans l’hote
!$HMPP <G1> stencil3d delegatedstore, args[VV]
...
! liberation des ressources de l’accelerateur
!$HMPP <G1> stencil3d release
En comparant les résultats
5.3.3
En gardant le problème sur un seul GPU
Les graphes
38
Analyse du kernel CUDA
Listing 5.4 – Kernel CUDA : stencil2D
__global__ void kernel_convolution(float* a, size_t a_pitch,
float* b, size_t b_pitch,
int x_offset, int y_offset,
int padding_a_gpu, int padding_res_gpu,
int x_elem, int y_elem ){
int
int
int
int
X = threadIdx.x + blockIdx.x * BLOCK_X;
Y = threadIdx.y + blockIdx.y * BLOCK_Y;
x_in = X + x_offset + padding_a_gpu;
x_out = X + x_offset + padding_res_gpu;
int index_y = Y + y_offset;
__shared__ float shared_mem[BLOCK_Y+2][BLOCK_X+2];
int x = threadIdx.x + x_offset;
int y = threadIdx.y + y_offset;
shared_mem[y][x] = get_tab(a,x_in,index_y,a_pitch);
if((threadIdx.x) < x_offset){
shared_mem[y][x-x_offset] = get_tab(a,x_in-x_offset,index_y,a_pitch);
}
// else
if(threadIdx.x) > (blockDim.x-2*x_offset)){
shared_mem[y][x+x_offset] = get_tab(a,x_in+x_offset,index_y,a_pitch);
}
if((threadIdx.y) < y_offset){
shared_mem[y-x_offset][x] = get_tab(a,x_in,index_y-y_offset, a_pitch);
}
// else
if((threadIdx.y) > (blockDim.y-x_offset)){
shared_mem[y+x_offset][x] = get_tab(a,x_in,index_y+y_offset, a_pitch);
}
__syncthreads();
if(X<x_elem-2 && Y<y_elem-2 )
get_tab(b,x_out,index_y,b_pitch) =
0.25*( shared_mem[y][x-1]+shared_mem[y][x+1]
+ shared_mem[y-1][x]+shared_mem[y+1][x] );
}
39
On relève plusieurs points dans l’analyse de ce kernel CUDA :
• Le padding mémoire pour l’alignement sur 64 bits n’est pas nécessaire puisqu’il peut être
géré automatiquement par l’API CUDA.
• La zone de mémoire partagée allouée dépend du rayon de la convolution, empêchant le
passage du rayon en tant que paramètre .
• Les branchements conditionnels peuvent être réduits.
Pour ce dernier point, il suffit d’allouer autant de mémoire partagée que de threads et d’y
inclure le halo. La taille des blocs est redéfinie pour inclure le halo. Enfin, si la dimension de la
matrice est un multiple du nombre de bloc, il est inutile de fournir la dimension du problème.
Chaque bloc calcule indépendamment et s’assure qu’il n’inscrit rien dans le halo.
Ce qui aboutit au kernel suivant.
Listing 5.5 –
__global__ void kernel_convolution(float* a, size_t a_pitch,
float* b, size_t b_pitch,
int x_offset, int y_offset){
extern __shared__ float shared_mem[blockDim.x][blockDim.y];
int X = threadIdx.x + blockIdx.x * blockDim.x;
int Y = threadIdx.y + blockIdx.y * blockDim.y;
int x = threadIdx.x + x_offset;
int y = threadIdx.y + y_offset;
// toutes les donness sont placees en cache
shared_mem[threadIdx.y][threadIdx.x] = get_tab(a,X,Y,a_pitch);
__syncthreads();
if( threadIdx.x > x_offset
&& threadIdx.x > blockDim.x - y_offset
&& threadIdx.x < blockDim.x - x_offset
&& threadIdx.y > blockDim.y - y_offset )
get_tab(b,X,Y,b_pitch) =
0.25*( shared_mem[y][x-1]+shared_mem[y][x+1]
+ shared_mem[y-1][x]+shared_mem[y+1][x] );
}
40
Relaxation des synchronisations
Reprenons le cas d’utilisation d’une ou de plusieurs Tesla S1060 (4 GPUs). Une relaxation des
synchronisations peut être envisagée pour que l’échange des donnés recouvre les calculs. On peut
également mettre en oeuvre une barrière de synchronisation fine pour que, moyennant un coût
mémoire supplémentaire, plusieurs itérations successives soient réalisés sur un domaine auquel est
soustrait un halo interne à chaque itération. Pour chaque itération, le halo soustrait qui est généré
autour du domaine restreint correspond au sous-ensemble non calculé et en attente des halos
voisins. Ceci introduit une nouvelle difficulté sous la forme d’une subdivision moins uniforme :
la texture représentant le domaine prend la forme d’une pyramide discrète, dont il faut calculer
le complément. Cette solution maximize l’utilisation du cache et occupe plus d’espace mémoire.
Les itérations successives respectent une compléxité logarithmiques. En revanche le calcul des
compléments introduit davantage de cellules fantômes.
Calculer les cellules fantômes en premier
Revoyez la figure
41
42
6
Sur l’accélération des codes
Quelques pistes pour l’accélération des codes du CERFACS sur GPU.
A partir du niveau 3, et en simple précisions, les fonctions BLAS sont plus rapides sur GPU
à condition que les dimensions de matrices soient suffisantes. A cet effet, nos benchmarks sur les
précision numériques (
Cet exercice présente un intérêt à la fois technique, académique et bref.
Une librairie, GBLAS, a été instruite pour fournir les interfaces C d’appel aux fonction BLAS
que peuvent invoquer les programmes écrits en Fortran. Selon les scénarios résultants des benchmarks évoqués ci-dessus, le code final devra faire appel aux fonctions présentes dans l’une ou
l’autre des librairies.
.
Figure 6.1 – Branchement des librairies d’algèbres linéaires
In fine, une librairie hybride, destinées à supplanter LAPACK, avec des capacités d’autocalibrage en compilation et en exécution pour pouvoir déterminer le choix précédent. C’est la
technique mise en oeuvre dans ATLAS. L’objectif est de garder cette librairie adaptable aux
changements rapides de matériels. Le projet MAGMA s’y emploie, développé à l’ICL [37, 38,
39]. L’évolution matérielle modifie le comportement optimal d’un kernel car celui-ci est lié aux
quantités de mémoires partagées et de registres disponibles [29].
Le calcul des valeurs propres est un problème qui se prète bien au traitement sur unités
vectorielles [40],[41].
Les résultats en précision double ont montré que les performances crêtes obtenues sur une
Tesla était au dessous des bi-quad cores. Ceci ne tient qu’au nombre de FPU des Tesla, et le
rapport 1/8 unités SP et DP devrait passer à 1/2 dans les architectures Fermi.
43
Axes de recherches sur la représentation des maillages non-structurés en carte graphique :
– Commencer par calculer les normales des faces.
– Rechercher une représentation compacte en mémoire qui réduisent les caches-manqués et
puissent tenir dans les caches des multiprocesseurs.
– Algorithmes de coloration de graphes [42].
Pour finir, des travaux sur la transformée rapide de Fourrier obtiennent des gain d’accélération remarquables (jusqu’à 150x pour un cas de détection radar [12]), mais avec des précisions
numériques très légèrement inférieure (ce qui importe peu dans les traitements de signaux en
temps rééel d’un signal d’office bruité).
44
7
Conclusions
Les nouveaux modèles de programmation des GPU n’ont plus grand chose à avoir avec la
programmation GPGPU via des API graphiques.
Nous avons évoqué l’idée d’un langage qui aurait l’avantage de la simplicité des shaders. Des
kernels écrits en Fortran pourraient bien remplir ce rôle.
Si nous avons comparé GPU et CPU, on peut aussi constater que l’architecture des cartes
graphiques unifiées s’assimile peu à peu à un micro-cluster. La principale différence depuis la
perspective du programmeur devient le contrôle de la mémoire très explicite, et l’adaptation
des problèmes de calculs aux contraintes de ce hardware simple, mais puissant. La plupart des
difficultés avec la programmation parallèle, telle que le passage des messages et les deadlocks,
n’existent pas dans CUDA pour la simple raison que le passage de message est impossible à la
façon des grappes de CPU. Un mécanisme de synchronisation globale et locale le remplace.
Le bénéfice, au delà de plus de puissance de calcul est que les problèmes associés aux fonctionnalités mentionnées manquantes n’arrivent jamais : l’architecture interdit tout simplement
le programmeur de tomber dans un tel piège.
Les cartes GPU sont peut-être amenée à disparaı̂tre, comme disparurent les cartes sons, et
à se retrouver intégrés dans les CPUs. Le modèle de programmation restera. A terme, on peut
supposer que les compilateurs et les processeurs seront devenus suffisamment sophistiqués pour
réinterpéter automatiquement la logique des codes historiques en termes d’exécution parallèle.
45
46
A
A.1
Annexe
Liste des GPU compatibles CUDA
et capacité de calcul 1.3
Les cartes compatibles avec CUDA et de capacités de calcul 1.3 implémentent l’ensemble des
spécifications de CUDA et peuvent calculer en double précision IEEE-754 déviée. Elles sont
toutes basées sur le chipset GT200, exceptée les chipset T10 de Tesla, qui sont une version
modifiée des GT200.
Le multiplicateur indique le nombre de GPU embarqué dans la carte.
Série Tesla
Tesla S1070
#MPs
4 x 30
Quantité de mémoire 4 x 4 GB
#Nb SPU 4 x 240 Type de mémoire
GDDR3
Prix
$13,000 Conso. électrique
700W
Une S1070 est un rack de 4 TeslaC1060. Le GPU le plus puissant en
attendant que Fermi ne prenne le relais. Présente un intérêt pour
le HPC qui nécessite impérativement une solution redondante ou
les 4Go de Mémoire qu’elle embarque.
Tesla C1060
#MPs
#Nb SPU
Prix
Conso. électrique
30
240
$1,219.99
250W
Quantité de mémoire
Type de mémoire
Interface mémoire
Bande passante
4 GB
GDDR3
512-bit
73.6 GB/S
Une solution HPC à l’intérieur d’une station de travail. Deux
cartes autorise à débuter en programmation environnement MultiGPU, ce qui présente un challenge. Consommation électrique : 150
W. Fréquence des SPU : 1.296 à 1.44 Ghz
47
Série Quadro
Quadro Plex 2200 D2
#MPs
2 x 30
Quantité de mémoire 2 x 4 GB
#Nb SPU
2 x 240 Type de mémoire
GDDR3
Prix 17 500$
Sous forme de station de travail ou de rack. Cher. 4 port DVI.
jusqu’à 8 écran peuvent s’y brancher. Spécialisé pour le rendu
d’image complexe ou interactif.
Quadro FX 5800
#MPs
#Nb SPU
Prix
Conso. électrique
30
240
$3 499
189W
Quantité de mémoire
Type de mémoire
Interface mémoire
Bande passante
4 GB
GDDR3
512 bits
102 GB/sec
Un GPU pour le calcul ou la visualisation. A comparer à une Tesla
mais avec une sortie vidéo. Ce qui facilite et rend plus agréable la
programmation.
Quadro FX 4800
#MPs
#Nb SPU
Prix
Conso. électrique
24
192
1 700=
C
150W
Quantité de mémoire
Type de mémoire
Interface mémoire
Bande passante
Similaire à une 5800 mais avec des capacités moindres.
48
1.5 GB
GDDR3
384 bits
76.8 GB/sec
GeForce GTX serie
GeForce GTX 295
#MPs
2 x 30
#Nb SPU
2 x 240
Prix
$536.95
Conso. électrique 289 W
Quantité de mémoire
Type de mémoire
Interface mémoire
Bande passante
2 x 896MB
GDDR3
896-bit ( 448-bit per GPU )
2 x 111.9 GB/sec
Deux GTX200 à l’intérieur d’une seul carte. Le système verra
deux cartes. Nécessite une alimentation électrique d’au minimum
680W. Recommandé pour les phases de prototypages.
GeForce GTX 285
#MPs
#Nb SPU
Prix
Conso. électrique
Fréquence d’horloge
30
240
$379.99
204 W
1476 MHz
Quantité de mémoire
Type de mémoire
Interface mémoire
Bande passante
1024MB
GDDR3
512-bit
159 GB/sec
Alim. Électrique 550 W minimum. Fortement recommandé pour
la phase de prototypage.
GeForce GTX 280
#MPs
24
#Nb SPU
192
Prix
$214.99
Conso. électrique 182 W
GeForce GTX 260
#MPs
24
#Nb SPU
192
Prix
180=
C
Conso. électrique 182 W
Quantité de mémoire
Type de mémoire
Interface mémoire
Bande passante
Quantité de mémoire
Type de mémoire
Interface mémoire
Bande passante
896 MB
?
448-bit
111.9 GB/sec
896MB
?
448-bit
111.9 GB/sec
49
50
Bibliographie
[1] André Lebeau. L’engrenage de la technique. Gaillmard, 2005.
[2] Leo Gerat. Le calcul scientifique se met au vert, Juillet-Août 2009.
[3] Herb Sutter. The Free Lunch Is Over : A Fundamental Turn Toward Concurrency in Software, 2005.
[4] Jon Peddie. GPU market defies gravity so far, April 2009.
[5] CUDA Zone. http://www.nvidia.com/cudazone.
[6] Michael Garland, Scott Le Grand, John Nickolls, Joshua Anderson, Jim Hardwick, Scott Morton,
Everett Phillips, Yao Zhang, and Vasily Volkov. Parallel Computing Experiences with CUDA. IEEE
Micro 28, August 2008.
[7] Tom Halfhill. Looking Beyond Graphics. Technical report, In-Stat, 2009.
[8] David Patterson. The Top 10 Innovations in the New NVIDIA Fermi Architecture and the Top
3 Next Challenges . Technical report, Parallel Computing Research Laboratory, U.C. Berkeley1,
September 2009.
[9] Glaskowsky. NVIDIA’s Fermi : The First Complete GPU Computing Architecture. Technical report,
In-Stat, 2009.
[10] Nathan Brookwood. NVIDIA Solves the GPU Computing Puzzle. Technical report, September
2009.
[11] Gpgpu.org. http://www.gpgpu.org.
[12] Jimmy Pettersson and Ian Wainwrigth. Radar Signal Processing with Graphics Processors. January
2010.
[13] NVIDIA CUDA – Programming Guide. Technical report, NVIDIA, 4 2009. Version 2.3.
[14] NVIDIA CUDA – Programming Best Practices Guide. Technical report, NVIDIA, July 2009. Toolkit
v2.3.
[15] Romain Dolbeau, Stéphane Bihan, and François Bodin. HMPP : A Hybrid Multi-core Parallel
Programming Environment. October 2007. Workshop on General Processing Using GPUs, Boston,
October 2007.
[16] J. Linford, J. Michalakes, A. Sandu, and M. Vachharajani. Multi-core acceleration of chemical
kinetics for simulation and prediction. 2009.
[17] John Michalakes and Manish Vachharajani. GPU Acceleration of Numerical Weather Prediction.
2009.
[18] John Michalakes and Manish Vachharajani. GPU Acceleration of Scalar Advection. http://www.
mmm.ucar.edu/WG2bench/.
[19] John Michalakes, John Linford, , Manish Vachharajani, and Adrian Sandu. GPU Acceleration of a
Chemistry Kinetics Solver. http://www.mmm.ucar.edu/wrf/WG2/GPU/Chem_benchmark/.
[20] Evan F. Bollig. Gpu acceleration of the cloud resolving. Technical report, 2009.
51
[21] UCAR. F2C. http://www-ad.fsl.noaa.gov/ac/Accelerators.html.
[22] Writing Applications for the GPU Using the RapidMind Development Platform. Technical report,
Rapidmind, 2006.
[23] Matthew Papakipos. The PeakStream Platform : High-Productivity Software Development for
multi-Core Processors. Technical report, April 2007.
[24] David Goldberg. What every computer scientist should know about floating-point arithmetic, volume 23. ACM, New York, NY, USA, 1991.
[25] Vasily. Volkov and James Demmel. LU, QR and Cholesky factorizations using vector capabilities
of GPUs. Technical Report No. UCB/EECS-2008-49, June 2008.
[26] K. Fatahalian, J. Sugerman, and P. Hanrahan. Understanding the Efficency of GPU Algorithms for
Matrix-Matrix Multiplication. Graphics Hardware, october 2004.
[27] National Center for Supercomputing Applications University of Illinois, Innovative Systems Lab.
Cuda Wrapper. http://sourceforge.net/projects/cudawrapper/.
[28] Wladimir J. Van Der Laan. Decuda. http://wiki.github.com/laanwj/decuda.
[29] Yinan Li, Jack Dongarra, and Stanimire Tomov. A Note on Auto-tuning GEMM for GPUs. January
2009.
[30] J. H. FERZIGER and MILOVAN PERIC. Computational Methods for Fluid Dynamics. 2002.
[31] Jeff Bolz, Ian Farmer, Eitan Grinspun, and Peter Schröoder. Sparse matrix solvers on the GPU :
conjugate gradients and multigrid. In SIGGRAPH ’03 : ACM SIGGRAPH 2003 Papers, pages
917–924, New York, NY, USA, 2003. ACM.
[32] Luc Buatois. Algorithmes sur GPU de visualisation et de calcul pour des maillages non-structurés.
PhD thesis, Institut National Polytechnique de Lorraine, 2008.
[33] Luc Buatois, Guillaume Caumon, and Bruno Lévy. Concurrent Number Cruncher, A GPU implementation of a general sparse linear solver. 2007.
[34] Nathan Bell and Michael Garland. Efficient Sparse Matrix-Vector Multiplication on CUDA. December 2008.
[35] Eric Darrigrand, Sébastien Giraud, and Vincent Pit. Une méthode multipôle rapide pour l’équation
de Laplace 3D.
[36] Sundaresan Venkatasubramanian. Tuned and asynchronous stencil kernels for CPU/GPU systems,
May 2009.
[37] Vasily Volkov and James Demmel. Benchmarking GPUs to tune dense linear algebra. November
2008.
[38] Marc Baboulin, Jack Dongarra, and Stanimire Tomov. Some Issues in Dense Linear Algebra for
Multicore and Special Purpose Architectures. 2008.
[39] Marc Baboulin, Jack Dongarra, and Stanimire Tomov. Towards dense linear algebra for hybrid
GPU accelerated manycore systems. 2008.
[40] Vasily Volkov and James Demmel. Using GPUs to Accelerate the Bisection Algorithm for Finding
Eigenvalues of Symmetric Tridiagonal Matrices. Technical Report UCB/EECS-2007-179, EECS
Department, University of California, Berkeley, Dec 2007.
[41] Chritian Lessig. Eigenvalues computation with CUDA, October 2007.
[42] K.A. Hawick, A. Leist, and D.P. Playne. Parallel Graph Component Labelling with GPUs and
CUDA. June 2009.
52