Luiz Angelo Steffenel - Université de Reims Champagne

publicité
Luiz Angelo Steffenel
Université de Reims Champagne-Ardenne
Vendredi 29 mai 2009
Plan
 
 
 
 
 
Pourquoi faire du parallélisme ?
C’est quoi une GPGPU ?
Programmation GPU
Hiérarchie de Threads et de Mémoire
Le GPU pour les problèmes combinatoires
2
Pourquoi faire du
parallélisme ?
Pourquoi utiliser le parallélisme ?
 
 
Pour dépasser la loi de Moore
Pour les différents besoins du calcul intensif :
 
 
large volumes de données
problèmes de simulation
4
Pourquoi utiliser le parallélisme ?
5
Évaluation
 
 
 
 
 
 
Accélération
Efficacité
Échange de données et collaboration
Scalabilité
Plus de processeurs  plus rapide ?
Loi d'Amdhal
 
Coût des parties séquentielles
6
C’est quoi une GPGPU ?
Accélérateurs
Non, pas ce type …
8
Accélérateurs
 
 
 
 
Dans le calcul à haute performance (HPC), un accélérateur
est un matériel dont le rôle est d’augmenter la performance
d’un type d’opération.
Dans les années 80, les supercalculateurs (mainframes)
avaient parfois des processeurs vectoriels, qui faisaient des
opérations parallèles sur un vecteur de données (SIMD)
Les PCs avaient des co-processeurs mathématiques : des
processeurs dédiés pour le calcul à point flottant.
Plus récemment, Field Programmable Gate Arrays
(FPGAs) permettent la reprogrammation des fonctions du
chip.
9
Avantages et Désavantages des
Accélérateurs
Les accélérateurs sont bons car :
  Ils rendent votre application plus rapide
Les accélérateurs sont mauvais car :
  Coûtent cher ;
  Ne sont pas faciles à programmer ;
  Rarement un code fait pour un accélérateur est portable : on
risque de perdre tout l’investissement lorsque la
technologie/mode change.
10
Le Roi des Accélérateurs
Parmi tous les options, le champion est sans doute
le processeur graphique (GPU).
11
Pourquoi les GPUs ?
 
 
Les GPUs ont été créées afin d’accélérer les tâches liées au
rendu d’images.
Ont gagné une immense popularité initialement parmi les
joueurs, car une GPU peut rendre des images plus définies
et avec un taux de rafraîchissement bien plus élevé.
 
 
Premiers GPUs vers 1998-1999.
Les prix sont devenus très abordables
 
 
Intégrés dans la plupart des ordinateurs moyenne gamme
Quelques centaines d’euros pour un modèle haut de gamme
12
Les GPUs sont populaires
 
Le projet d’un processeur est très cher
 
 
Une usine de fabrication coûte vraiment cher
 
 
 
 
Centaines de millions de dollars
Milliards de dollars
Mais pas chers à produire
En 2006 – 2007, autour de 80 millions de cartes graphiques
ont été vendues par an, avec un profit autour de $20
milliards de dollars par an.
Ça veut dire que les fabricants de GPU ont largement pu
payer les coûts de développement.
13
Qu’est-ce que qu’une GPU fait ?
 
 
Les GPUs ont été développées pour des applications en
imagerie telles que l’application d’ombres et de textures.
Ces opérations sont normalement faites avec des opérations
arithmétiques de point flotant – le même type d’opération
que le calcul haute performance utilise.
14
15
Puissance des GPUs
 
ROMEO II
 
 
 
 
Cluster constitué de 11 noeuds et d'un total de
108 cœurs
Machines avec 8, 16 et 32 cœurs (Itanium II).
Puissance de pic : 614 giga flops
Calculateur NVIDIA® Tesla™ S1070
 
 
4 GPUs de 240 processeurs chacune
Puissance de pic : 4,14 tera flops
16
Programmation GPU
C’est difficile de programmer une GPU?
 
Jusqu’à très récemment, la programmation GPU était faite :
 
 
 
Pour programmer une GPU à d’autres fins, on était obligés à
faire passer les données pour des éléments graphiques
 
 
 
Soit à travers un langage pour le rendu graphique comme
OpenGL, ou
Programmation de bas niveau (langage d’assemblage).
charger une matrice comme si c’était une texture, par exemple
Dur, dur… Alors presque personne n’essayait.
Maintenant, les fabricants ont lancé des APIs destinées au
calcul général sur GPU (GPGPU)
 
Encore plus de $$ pour les fabricants ;)
18
Comment programmer une GPU ?
 
APIs ou langages propriétaires
 
 
 
 
NVIDIA : CUDA (C/C++)
AMD/ATI : StreamSDK/Brook+ (C/C++)
OpenCL (Open Computing Language) : un nouveau
standard industriel pour le calcul GPGPU.
Compilateurs Fortran et C (Portland Group) avec directives
de programmation directement reconnues par le
compilateur.
19
NVIDIA CUDA
 
 
 
 
 
 
Approche propriétaire de NVIDIA
Issue de la “Compute Unified Device Architecture” de
NVIDIA
Principe : des extensions au langage C pour mieux contrôler
les ressources des GPUs
Nombre limité d’instructions
Oblige une importante réécriture du code source
N’est pas disponible pour le langage Fortran
 
Les physiques/chimistes/etc normalement ne font que ça
20
Exemple CUDA Partie 1
// example1.cpp : Defines the entry point for the console applicati
on.
//
#include "stdafx.h"
#include <stdio.h>
#include <cuda.h>
// Kernel that executes on the CUDA device
__global__ void square_array(float *a, int N)
{
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx<N) a[idx] = a[idx] * a[idx];
}
21
Exemple CUDA Partie 2
// main routine that executes on the host
int main(void)
{
float *a_h, *a_d; // Pointer to host & device arrays
const int N = 10; // Number of elements in arrays
size_t size = N * sizeof(float);
a_h = (float *)malloc(size);
// Allocate array on host
cudaMalloc((void **) &a_d, size);
// Allocate array on device
// Initialize host array and copy it to CUDA device
for (int i=0; i<N; i++) a_h[i] = (float)i;
cudaMemcpy(a_d, a_h, size, cudaMemcpyHostToDevice);
// Do calculation on device:
int block_size = 4;
int n_blocks = N/block_size + (N%block_size == 0 ? 0:1);
square_array <<< n_blocks, block_size >>> (a_d, N);
// Retrieve result from device and store it in host array
cudaMemcpy(a_h, a_d, sizeof(float)*N, cudaMemcpyDeviceToHost);
// Print results
for (int i=0; i<N; i++) printf("%d %f\n", i, a_h[i]);
// Cleanup
free(a_h); cudaFree(a_d);
}
22
AMD/ATI Brook+
 
 
 
 
Propriétaire de AMD/ATI
Connu auparavant comme “Close to Metal” (CTM)
Extension du langage C pour mieux contrôler les ressources
des GPUs
Comme CUDA, n’est pas disponible pour Fortran
23
Exemple Brook+ Partie 1
float4 matmult_kernel (int y, int x, int k,
float4 M0[], float4 M1[])
{
float4 total = 0;
for (int c = 0; c < k / 4; c++)
{
total += M0[y][c] * M1[x][c];
}
return total;
}
24
Exemple Brook+ Partie 2
void matmult (float4 A[], float4 B’[], float4 C[])
{
for (int i = 0; i < n; i++)
{
for (j = 0; j < m / 4; j+)
{
launch_thread{
C[i][j] =
matmult_kernel(j, i, k, A, B’);}
}
}
sync_threads{}
}
25
OpenCL
 
 
 
 
 
Open Computing Language
Standard ouvert développé par le Khronos Group, qui est un
consortium regroupant plusieurs constructeur (dont
NVIDIA, AMD et Intel)
Une version initial du standard OpenCL a été lancée en
Décembre 2008.
On attend que plusieurs compagnies lancent leurs
implémentations
Très probablement, la première version à sortir sera celle
d’Apple, qui inclut OpenCL dans sont système Mac OS X
v10.6 “Snow Leopard”, prévu cette année.
26
Exemple OpenCL Partie 1
// This kernel computes FFT of length 1024. The 1024 length FFT
// is decomposed into calls to a radix 16 function, another
// radix 16 function and then a radix 4 function
kernel void fft1D_1024 (
global float2 *in, __global float2 *out,
local float *sMemx, __local float *sMemy)
{
int tid = get_local_id(0);
int blockIdx = get_group_id(0) * 1024 + tid;
float2 data[16];
// starting index of data to/from global memory
in = in + blockIdx;
out = out + blockIdx;
globalLoads(data, in, 64); // coalesced global reads
27
Exemple OpenCL Partie 2
fftRadix16Pass(data);
// in-place radix-16 pass
twiddleFactorMul(data, tid, 1024, 0);
// local shuffle using local memory
localShuffle(data, sMemx, sMemy, tid,
(((tid & 15) * 65) + (tid >> 4)));
fftRadix16Pass(data);
// in-place radix-16 pass
twiddleFactorMul(data, tid, 64, 4); // twiddle factor multiplication
localShuffle(data, sMemx, sMemy, tid,
(((tid >> 4) * 64) + (tid & 15)));
// four radix-4 function calls
fftRadix4Pass(data);
fftRadix4Pass(data + 4);
fftRadix4Pass(data + 8);
fftRadix4Pass(data + 12);
// coalesced global writes
globalStores(data, out, 64);
}
28
Exemple OpenCL Partie 3
// create a compute context with GPU device
context = clCreateContextFromType(0, CL_DEVICE_TYPE_GPU, NULL, NULL,
NULL);
// create a work-queue
queue = clCreateWorkQueue(context, NULL, NULL, 0);
// allocate the buffer memory objects
memobjs[0] =
clCreateBuffer(context,
CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
sizeof(float)*2*num_entries, srcA);
memobjs[1] =
clCreateBuffer(context, CL_MEM_READ_WRITE,
sizeof(float)*2*num_entries, NULL);
// create the compute program
program =
clCreateProgramFromSource(context, 1, &fft1D_1024_kernel_src, NULL);
// build the compute program executable
clBuildProgramExecutable(program, false, NULL, NULL);
// create the compute kernel
kernel = clCreateKernel(program, "fft1D_1024");
29
Exemple OpenCL Partie 4
// create N-D range object with work-item dimensions
global_work_size[0] = n;
local_work_size[0] = 64;
range = clCreateNDRangeContainer(context, 0, 1, global_work_size,
local_work_size);
// set the args values
clSetKernelArg(kernel, 0, (void *)&memobjs[0], sizeof(cl_mem), NULL);
clSetKernelArg(kernel, 1, (void *)&memobjs[1], sizeof(cl_mem), NULL);
clSetKernelArg(kernel, 2, NULL,
sizeof(float)*(local_work_size[0]+1)*16, NULL);
clSetKernelArg(kernel, 3, NULL,
sizeof(float)*(local_work_size[0]+1)*16, NULL);
// execute kernel
clExecuteKernel(queue, kernel, NULL, range, NULL, 0, NULL);
30
Directives Portland Group
 
 
 
 
Directives propriétaires en Fortran et C (Intel, etc)
Similaire à OpenMP dans son structure
Actuellement en version beta
Si le compilateur ne comprends pas les directives, il les
ignore
 
 
A priori, cela peut marcher sur plusieurs modèles
 
 
 
Le même code peut être exécuté sur GPU ou CPU, dans un
compilateur Portland ou pas
Pour l’instant, on n’a que l’annonce d’une version NVIDIA
Un accord entre PGI et AMD/ATI a été annoncé
Les directives indiquent au compilateur quelles parties du
code doivent utiliser la GPU ; le reste du code s’exécute sur
la CPU.
31
Exemple des directives PGI
!$acc region
do k = 1,n1
do i = 1,n3
c(i,k) = 0.0
do j = 1,n2
c(i,k) = c(i,k) +
&
a(i,j) * b(j,k)
enddo
enddo
enddo
!$acc end region
32
Quels sont les inconvénients ?
 
Dans tous les cas (CUDA, Brook+, OpenCL, PGI) le code
doit être réécrit
  CUDA/Brook+ : Propriétaires, C/C++ uniquement
  OpenCL : portable mais pas vraiment facile à
comprendre
  Directives PGI : pour l’instant il n’est pas clair comment
les blocs de code sont attribués aux GPUs.
33
Hiérarchie de Threads et Mémoire
 
 
La programmation GPU reste complexe
Une GPU est une unité de calcul créée pour le traitement
d’images
 
 
 
Opérations limitées
Répartition des données
Peu d’interaction avec la CPU et la mémoire principale
34
Structure CPU vs GPU
Source : Nvidia CUDA Programming Guide
Mot clé : Kernel
Dans la programmation GPU, un kernel est un code
(normalement une fonction) qui peut tourner sur la GPU.
De manière générale, le code kernel est exécuté de manière
synchrone par les processeurs dans le GPU.
36
Mot clé : Thread
 
 
 
Une thread est une instance d’exécution du kernel avec un
indice.
Chaque thread use son indice pour accéder une partie
spécifique des éléments d’un vecteur cible
Ceci permet que l’ensemble de threads travaille sur la
totalité des données en parallèle.
 
Les threads peuvent avoir des variables partagées et des
variables privées
37
Mot clé : Block
Un block est un groupe de threads.
  Isolées, les threads peuvent être exécutées de manière
concurrent ou indépendante, sans un ordre précis.
  Un block permet de coordonner les threads, utilisant des
fonctions telles que _syncthreads() comme une barrière
  Ceci permet de resynchroniser les threads avant de passer à
la prochaine étape de l’application
38
Mot clé : Grid
Un grid est un groupe de blocks.
Les grids sont totalement indépendants les uns des autres
39
Hiérarchie des GPUs NVIDIA
 
 
 
 
Grids représentent les GPUs
Blocks représentent les unités de
calcul ( MultiProcesseurs)
  Un block est restreint à un
seul MP, mais un MP peut
avoir plusieurs blocks
Threads
sont les Stream Processors (SP)‫‏‬
Warps sont groupes de (32)
threads qui exécutent
simultanement
Hiérarchie de la mémoire
 
Mémoire de la machine
(serveur x86)
 
 
 
Énorme et leeeeeeente
400/600 cycles pour y
accéder
Mémoire du dispositif (GPU)
 
 
 
Global: visible par toutes les
threads dans tous les blocks
– grande et +-lente
Shared: visible par toutes les
threads dans un block – taille
et vitesse moyennes (4
cycles d’horloge)
Local: visible uniquement
par la thread - rapide et
petite
41
Hiérarchie de la mémoire
 
Et encore (GPU)
 
 
Constant: mémoire à
lecture-seule, visible par
toutes les threads
Texture: mémoire à lectureseule, visible par toutes les
threads
42
Exemple :
GPU Nvidia Geforce 8 et 9
 
 
Nombre de multi-processeurs : 2 à 16
Nombre de processeurs / multi-processeurs :
 
 
8 processeurs de flux
Taille : 32 threads, max : 512 threads
 
 
 
Recommandation : 64 threads (optimiser les coûts
d'accès mémoire)
Nombre de registres / multi-processeurs : 8192
Mémoire partagé : 16 Ko, dans 16 banks
43
Programmation Performante
Aujourd’hui, le plus important goulot d’étranglement pour les
cartes GPU est l’interface PCIe :
  PCIe 2.0 x16: 8 GB/sec
  1600 MHz Front Side Bus: 25 GB/sec
  GDDR3 GPU RAM: 102 GB/sec per carte
Et il faut :
  Au début, déplacer les données de la mémoire RAM du
serveur vers la RAM de la GPU.
  Faire la plupart du travail sur la GPU.
  Utiliser le serveur x86 uniquement pour les entrées/sorties et
passage de message (réseau), ce qui réduit le transfert de
données à travers l’interface PCIe.
44
Des applications qui ont réussi
http://www.nvidia.com/object/cuda_home.html#state=home
45
Le GPU pour les problèmes
combinatoires
 
Pourquoi ?
 
 
 
Faible coût du matériel
Grand nombre de processeurs
SAT est un prototype de problème combinatoire
 
 
Père de tout les problèmes NP-complet (Cook, 71)
Au cœur de la conjecture P ≠ NP
46
Le Problème SAT
 
SAT est un problème de décision
 
 
oui (s'il y a une solution) ou non
Une formule SAT est une conjonction de disjonction
 
 
(X1 ou X2 ou -X3)
ET (-X1 ou X4 ou X3)
47
SAT en pratique
 
 
 
 
 
 
Conception de processeurs
Validation des bases de connaissances
Physique statique
Robotique
Reconstruction d'images
...
48
Définition de SAT
 
 
 
 
Une formule logique est représentée sous la forme normale
conjonctive (CNF)
Soit X={x1, ..., xn} un ensemble de n variables booléennes.
Soit C={c1, ..., cm} un ensemble de m clauses.
CNF : une conjonction de clauses
 
Une clause c est une disjonction de littéraux
 
 
un littéral l est une variable x signée, l=x ou l=-x
Exemple de clause : ci=-x1∨x2∨x3
49
Définition de SAT
 
 
 
 
Un littéral x est satisfait ssi x=true
Un littéral -x est satisfait ssi x=false
Une clause c=l1∨...∨lj , est satisfaite ssi ∃ li satisfait avec
i ∈ [1,j]
Un problème est satisfiable ssi chaque clause de C l'est
50
Solveurs
 
Méthode complète (basée sur une recherche arborescente) :
 
 
 
 
 
DPLL
KCNFS
PSATO
...
Méthode incomplète (recherche local) :
 
 
Walksat (B. Selman, H. Kautz)
GSAT (B. Selman, H. Levesque, D. Mitchell)
51
SAT sous forme matricielle
 
 
Pourquoi ? Utiliser la puissance calculatoire du GPU.
Principe :
 
 
 
Une matrice représente l'ensemble des clauses
Chaque ligne représente une clause
Une affectation est un vecteur
 
Un vecteur satisfait la formule, ssi le produit matriciel ne
contient pas de zéro
52
Exemple
53
Produit matriciel sur GPU
 
 
 
 
Multiplication de A x
B=C
Chaque bloc évalue
une sous-matrice de C
Chaque thread d'un
bloc évalue un élément
de la sous-matrice de C
La grille est confiée
aux multi-processeurs
54
SAT sur GPU
 
Une approche complète
 
 
 
 
 
Générer les affectations dans une matrice (2n)
Faire le produit matriciel avec la formule SAT
Évaluer le nombre de conflits pour chaque affectation
Si une solution sans conflit existe : la formule est satisfiable
Limites : taille de la mémoire, traitement de petite formule
(n<16)
55
SAT sur GPU : approche parallèle
 
 
Un ensemble de threads évalue différentes affectations
Une itération de l'algorithme :
 
 
Choisir aléatoirement une clause c insatisfaite par
l'affectation
Calculer l'amélioration des variables de c
 
 
si max f(x) < 0
 
 
f(x) = #clauses_0_x - #clauses_1_x
choisir aléatoirement une variable de la clause c
sinon (avec une probabilité p)
 
choisir une variable de c telle que max f(x)
56
Téléchargement