Post on 29-Aug-2020
Introduction a la programmation massivement paralleleL’exemple d’opencl
F. Sailhan
CNAM, departement informatique
November 21, 2014
Plan
1 Introduction
2 Notions de base sur le parallelismeArchitecture
3 OpenCLModelesModele d’executionMon premier ProgrammePremier bilanDeuxieme Programme
4 Conclusion
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 2 / 54
Pourquoi etudier la programmation parallele?
Les contres1 Les performances d’un meme logiciel s’ameliorent a chaque nouvelle
generation de processeur,
2 Nous sommes habitues a la programmation sequentielle,
3 La programmation parallele, ce n’est pas nouveaux et pourtant, peud’applications l’utilisent ...
4 Depuis 2003, le multi coeur ...
5 Les data centers, le cloud ...
@AFP Facebook Data Center on April 19, 2012 in Forest City, NorthCarolina.
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 3 / 54
Pourquoi etudier la programmation parallele?
Oui mais...1 Cette cadence se ralentit en 2003 : la consommation d’energie et la
dissipation de chaleur limitent la frequence des horloges,2 Plus besoin d’ordinateurs couteux... : la programmation devient
parallele avec le multi-coeur, massivement parallele avec les GPU(Graphic Processing Units)
I Exigence temps reelles (toucher l’ecran d’un smartphone)I 20 capteurs par smartphone (cameras, microphones), systemes
embarques (voitures)I Resolution plus poussee (20 fois plus de pixels)I acceleration CPU impossible a moins de vider la batterie
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 4 / 54
Pourquoi etudier la programmation parallele?
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 5 / 54
To be or not to be ? CPU or GPU ? la fausse question
4 multi-coeurs optimises pour une execution sequentielle
1 Une logique de controle sophistiquee au service del’execution parrallele d’un seul thread
2 Large memoire cache avec acces reduit auxintructions + donnees
GPU = pleins de multi-processeurs = parallelisationmassive
1 Memoire globable: DRAM 6= DRAM de carte mereI bande passante GPU et memoire globale = 20 fois
plus rapide qu’avec la DRAM de la carte mere. ChipG80: 8GB/s DRAM-CPU et 86,4GB DRAM-GPU
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 6 / 54
L’histoire des GPUs
En 3 mots
Annees 80-90 : les fonctionalites graphiques sont pipelinees au niveauhardware. Elles sont configurables mais pas programmables
Annees 2000: Nvidia developpe le compilateur CUDA C, C++, leslibrairies a destination des programmeurs,
Consortium Kronos cree pour developper d’un standard ouvert(middleware + langage), cross-plateforme
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 7 / 54
Standards ouverts specifies par le groupe Khronos
Khronos “connecting software to silicon”, https://www.khronos.org
OpenCL = API de calcul que nous verrons
OpenGL = API graphique
WebGL et WebCL = API pour l’acceleration graphique etacceleration des calculs pour le Web (dans les navigateurs)
Collada: exchange d’information 3D entre applications
OpenGL ES : API graphique pour les systemes embarques(smartphones, display, automobile)
Stream input = API pour l’interaction avec les capteurs generant desflux, combiner les flus
OpenKcam : fusion (camera) et controle (actuator)
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 8 / 54
Parallelisme : taxonomie de Michael J. Flynn
Classification basee sur la concurrence des flux d’instructions & donnees
Flux d’instructions = ensemble d’instructions formant un processus
Flux de donnees = donnees sur lesquelles le processus opere
Single Instruction, Single Data stream (SISD): systeme sequentiel : 1flux d’instructions opere sur 1 donnee
Single Instruction, Multiple Data streams (SIMD): 1 instruction
diffusee a plusieurs unites de calcul ;chacune opere la meme instruction sur
differentes donnees
Multiple Instruction, Single Data stream (MISD): plusieurs flux
d’instructions operent sur le meme flux de donnees
Multiple Instruction, Multiple Data streams (MIMD): plusieurs
unites de calculs operent sur plusieurs flux de donnees au moyen de plusieurs
flux d’instructions
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 9 / 54
Parallelisme MISD, MIMD : 2 modeles memoire
Par memoire distribuees
# processeurs (specialises ou non) sont connectes a la memoire viaun reseau haut debit (Myrinet, Infiniband, 10Gbit Ethernet), memoire
Exemple : cluster de serveurs, MPP (Massively Parallel Processor) cfla liste des plus puissants : http://www.top500.org
Chaque processeur dispose de son propre espace d’adressage
Inconvenient : transfert lent compare a un acces locale en memoire →adapte aux applications peu communicantes
Par memoire partagee
1 tous les processeurs partagent le meme espace d’adressage etcommuniquent entre eux par lecture/ecriture dans la memoirepartagee
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 10 / 54
Parallelisme MIMD : 2 modeles memoire
Par memoire distribuees
Les interactions se font par passage de message entre les processeurs
Inconvenient : portage difficile d’un programme sequentiel
Exemple: langage MPI (Message Passing Interface) utilise pour desclusters distribues (jusqu’a 100 000 noeuds)
Par memoire partagee
1 Coherence forte → quelques centaines de noeudsExemple : Open MP (Open Multi-processing)
2 Coherence relaxee : une memoire partagee de petite tailleExemple : Cuda, Opencl
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 11 / 54
Parallelisme logiciel
La parallelisation consiste a:
1 Analyser des dependances au niveau des structures de donnees et desprocessus
2 Determiner le meilleure algorithme/framework pour executer le code
3 Reecrire du code avec un framework tel que Message PassingInterface (MPI) ou OpenCL
Parrallelisme des donnees
Les sections de programme sont caracterisees par un parallelismeimportant des donnees ce qui permet d’effectuer des operationsarithmetiques en parallele sans danger
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 12 / 54
Parallelisme logiciel - Performances
Lois des 90-10
10 % du code est parallelisable,90 % du code est sequentiel,
Idee : en terme de temps d’execution, nous passons la plupart dutemps dans les 10% du code
Evaluation du benefice : loi d’Amdahl
S = 1(1−P)+ P
N
N : nombre de processeurs,
P : fraction du temps pris par la portion de code parallelisable
S : rapidite
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 13 / 54
Parallelisme de controle - Parallelisme de donnees
for(i=0;i<N;i++) {
resultA = taskA(i);
resultB = taskB(i);
resultC = taskC(i);
}
Parallelisme de donnees
Les donnees sont partionnees
Exemple: chaque processeur gere N/3 iterations de la boucle
Avantage : plusieurs processeurs executent le meme programme etfinissent en meme temps
Parallelisme de controle
Chaque processeur execute une tache de la boucle
Inconvenient: desynchronisation
Solution: partage equitable de la charge (gestion d’une queue destaches a assigner sur les processeur)F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 14 / 54
1 Introduction
2 Notions de base sur le parallelismeArchitecture
3 OpenCLModelesModele d’executionMon premier ProgrammePremier bilanDeuxieme Programme
4 Conclusion
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 15 / 54
Architecture GPU
Grappes de multi-processeurs partageant un cached’instructions et la meme logique de controle
Chaque multi-processeur dispose d’une unite MAD(Multiply-Add) et (Add-Multiply) et de fonctionsparticulieres (exemple la racine carree)
Chaque multi-processeur execute un parallelismemassif (plusieurs milliers de threads)
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 16 / 54
Exemple d’architecture: Kepler GK100 de NVIDIA
Derniere generation (2014) :Kepler = Fermi * 2= 1536KB de cache L2 partage par 15 SMX =15 clusters contenant chacun 4 schedulers de32 threads
Chaque SMX dispose de 64KB (memoirepartagee + cache L1) + cache (read only) de48KB
@http://www.nvidia.com/object/nvidia-kepler.html
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 17 / 54
Exemple d’architecture: Kepler GK100
Chaque thread a acces a 255 registres + lesmemoires hierarchisees
Operation atomiques (add, min, mac, compareand swap)
Nouveaute : parallelisme dynamique =recursivite permise = planification a la volee @
http://www.nvidia.com/object/nvidia-kepler.html
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 18 / 54
OpenCL (Open Computing Language)
Definitiona framework suited for parallel programming of heterogeneous systems
Objectifs
Utiliser toutes les ressources :I a la fois CPU multi-coeurs et GPU : meme code executable sur
CPU/GPU (minus quelques optimisations)I la parallelisation des taches et des donnees
Un modele de programmation haut niveau base sur le C
Cross-platform, cross-vendeur, cross-OS (android, ...)I API pour acceder a la plateforme : abstraction du materiel (memoire,
GPU ect...)I API pour l’execution : execution transparente des threads, gestion
transparente des queues, des ressources memoires
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 19 / 54
Modelisation de la memoire
Un modele facilement retransposable sur les architectures materielles.Celui de Cuda est casiment similaire.
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 20 / 54
Modelisation de la memoire
Memoire privee : chaque work-item dispose d’une memoire priveequi est la plus rapide d’acces, sans primitive de synchronisation, et estallouee/partitionnee a la compilation pour ledit kernel et ladite carte :sa taille est inconnue
Memoire locale : Memoire partagee par tout un work group :chaque workitem du meme work group peut y acceder. Generallementsur ship
Memoire globale accessible par tous les workgroup, est nonsynchronisee, possiblement constant (c-a-d en lecture seule) :memoire la plus large (GB),
Memoire globale de l’hote la plus lente d’acces mais la plusconsequente. Memoire a eviter (temps de chargement important :mieux vaut garder le maximum possible, le plus longtemps possiblesur le GPU)
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 21 / 54
Acces a la memoire
Espace d’adressage: private (au work-item), local (workitems d’unmeme workgoup), global (accessible par tous les workitems de tousles work groups), constante (read only pour la memoire globale)
Gestion explicite de la memoire: les types de memoire doivent etreexplicitement definis au niveau du code en raison
I du manque de memoireI des transbordements dont le cout differe suivant le type de memoire
Modele relaxe d’acces a une memoire partageeI La vision d’un work-item n’est pas toujours celle des autresI Necessite de ce synchroniser avec des barrieres. Exemple: attendre que
tous les work-items aient ecris avant de lire. Cette synchronisation esttres couteuse.
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 22 / 54
Vocabulaire
Definition
host = le systeme de calcul = CPU (Central Processing Unit)traditionnel equipe de nombreux arithmetic execution units.
device = GPU
Un programmeur definie des fonctions devant s’executer sur le device.Chaque fonction est appelee un kernel
Organisation du programme
Un programme OpenCL est divise en deux parties :
une partie s’execute sur l’hote (partie non parrallelisee)
Une partie s’executant sur le GPU (partie parallelisee)
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 23 / 54
Modele d’execution
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 24 / 54
Modele d’execution
Work-item: equivalent d’un thread (= plus petite entited’execution). L’execution d’un kernel se fait sur un ensemble dework-items dont le nombre est parametre par le developpeur. Chaquework-item execute le meme code. Chaque work item est identifie (ID),
Work-group: ensemble de work-items cooperant ensemble au seind’un work-groups. Reflete l’organisation des work-items en grille de 1,2, 3 dimensions. Est identifie par un ID
ND-Range : niveau d’organisation des work-groups = 1, 2 ou 3dimensions
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 25 / 54
Premier programme
Objectif : ajouter deux vecteurs1 Developper une fonction=kernel appelee vector add qui ajoute deux
vecteurs. Cette fonction est placee un fichier appele vecteur .cl .2 Developper toute le code permettant de lancer en parallele les threads
permettant d’ajouter les deux vecteurs. Placer ce code dans un fichiervecteur .c .
3 Compiler + executer
Consulter : https://www.khronos.org/developers/reference-cards/
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 26 / 54
Kernel ajoutant deux vecteurs
__kernel void vector_add(__global const float* src_a,
__global const float* src_b, __global float* res,
const int num)
{
const int idx = get_global_id(0);
if (idx < num)
{
res[idx] = src_a[idx] + src_b[idx];
}
}
2 vecteurs en entrees src a src b, un resultat res, #threads s’executent
get local id renvoie l’identifiant du workitem s’executant
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 27 / 54
Kernel ajoutant deux vecteurs
__kernel void vector_add(__global const float* src_a,
__global const float* src_b, __global float* res,
const int num)
{
const int idx = get_global_id(0);
if (idx < num)
{
res[idx] = src_a[idx] + src_b[idx];
}
}
2 vecteurs en entrees src a src b, un resultat res, #threads s’executent
get local id renvoie l’identifiant du workitem s’executant
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 27 / 54
Kernel - DeveloppementLe language utilise est base sur l’ANSI C99. Lors du developpement d’unkernel 3 aspects sont a considerer :
1 Il faut determiner lors du passage des arguments le type de memoireutilise, c’est a dire de l’espace d’adressage qui est soit :
I global : espace d’adressage globalI constant : region de la memoire en lecture seulementI local : memoire partagee par le work-group.I private: memoire du work-item.
2 Attention: chaque type d’adressage est distinct, ce qui signifie quetous les mouvements de donnees doivent etre explicites.
3 Les methodes fournissant un acces au domaine de calcul
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 28 / 54
Premier programme : programmation de l’hote (host)
Vous devez imperativement consulter la specification du language tres bienresumee par l’ OpenCL Quick Reference Card disponible a l’url :https://www.khronos.org/developers/reference-cards/ Ce guide contient les types
de base, les fonctions mathematiques, geometriques ... dont il faut privilegier
l’utilisation
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 29 / 54
Premier programme : programmation de l’hote (host)
Etape suivante
developer l’environnement autours permettant d’executer le kernel :fichier separe : vecteur.c
Pour compiler et executer, taper en ligne de commande :
$ gcc -o vect vecteur.c -lOpenCL
$ ./vect
#include <stdio.h>
#include <stdlib.h>
#ifdef __APPLE__
#include <OpenCL/opencl.h>
#else
#include <CL/cl.h>
#endif
int main(void) {...}F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 30 / 54
Premier programme : programmation de la plateforme
Platform
hote + ensemble de devices geres par OpenCL.
Permet a une application de partager les ressources et d’executer leskernels sur les devices au sein de la plateforme
Est represente par l’objet a cl latform object initialise par la fonctionsuivante :
cl_int oclGetPlatformID (cl_platform_id *platforms)
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 31 / 54
Premier programme : initialisation du device
Device
Deux types de devices sont geres par OpenCl: device type=CL DEVICE TYPE GPU pour GPU, CL DEVICE TYPE CPU pourCPU
Le nombre de devices (typiquement 1) est defini num entries
Le(s) device(s) sont initialises par la fonction ci-dessous :
cl_int clGetDeviceIDs (cl_platform_id platform,
cl_device_type device_type, cl_uint num_entries,
cl_device_id *devices, //pointeur : liste des devices
cl_uint *num_devices) //#devices dont le type a ete defini
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 32 / 54
Premier programme : initialisation du context
Context
est utilise par l’environnement d’execution d’OpenCL (ses kernels,devices, gestionnaire de memoire, queue(s) de commandes)
est reference par l’objet cl context object, initialise par la fonctionci-dessous:
cl_context clCreateContext (const cl_context_properties *properties,
// Bitwise with the properties CL_CONTEXT_PLATFORM
cl_uint num_devices, //#devices
const cl_device_id *devices, // Pointeur sur les devices
void *pfn_notify(const char *errinfo, const void
*private_info, size_t cb, void *user_data), //fonction de call back
utilisee pour les notifications
void *user_data, // parametres de la fonction ci-dessus
cl_int *errcode_ret) // erreur renvoyee (aucune si = NULL)
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 33 / 54
Premier programme : queue de commande
CommandQueue
est utilise pour empiler les commandes a executer.
Plusieurs queues peuvent etre crees (les commandes sont alorsindependantes et non synchronisees)
est reference par l’objet cl command queue, initialise par la fonctionci-dessous:
cl_command_queue clCreateCommandQueue (cl_context context,
cl_device_id device,
cl_command_queue_properties properties, //bitwise
cl_int *errcode_ret) //erreur renvoyee (aucune si = NULL)
Les elements basiques de l’hote sont maintenant configures
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 34 / 54
Premier programme : plateforme, contexte, queue
cl_platform_id platform;
cl_context context;
cl_command_queue queue;
cl_device_id device;
//cl_int error = 0;
// Platform
error = oclGetPlatformID(&platform);
//if (error != CL_SUCCESS) exit(error);
// Device
clGetDeviceIDs(platform,CL_DEVICE_TYPE_GPU,1,&device,NULL);
// Context
context = clCreateContext(0, 1, &device, NULL, NULL, &error);
// Command-queue
queue = clCreateCommandQueue(context, device, 0, &error);
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 35 / 54
Premier programme : allocation de la memoire
Memoire
les variables a passer au kernel (entrees et sorties) doivent etre definies
La memoire allouee sur le device est de type:I CL MEM READ WRITEI CL MEM WRITE ONLYI CL MEM READ ONLYI CL MEM USE HOST PTR : utilisation de la memoire de l’hoteI CL MEM ALLOC HOST PTR : memoire de l’hote allouee et rendue
accessibleI CL MEM COPY HOST PTR :copie la memoire pointee par host ptr
cl_mem clCreateBuffer (cl_context context,
cl_mem_flags flags, //type de memoire
size_t size, // taille exprimee en octets
void *host_ptr, cl_int *errcode_ret)
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 36 / 54
Premier programme : allocation de la memoire
Memoire
Allocation de 3 vecteurs src a d , src b d , res d , initialisation desrc a d , src b d
Allocation des buffers: 2 en lecture et 1 en ecriture (resultat)
const int size = 1234567;
float* src_a_h = new float[size];
float* src_b_h = new float[size];
float* res_h = new float[size];
for(int i=0; i<size; i++) {src_a_h = src_b_h = (float) i;}
const int mem_size = sizeof(float)*size;
cl_mem src_a_d = clCreateBuffer(context, CL_MEM_READ_ONLY |
CL_MEM_COPY_HOST_PTR, mem_size, src_a_h, &error);
cl_mem src_b_d = clCreateBuffer(context, CL_MEM_READ_ONLY |
CL_MEM_COPY_HOST_PTR, mem_size, src_b_h, &error);
cl_mem res_d = clCreateBuffer(context, CL_MEM_WRITE_ONLY,
mem_size, NULL, &error);F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 37 / 54
Premier programme: remarques
Il existe deux facons de compiler :
Compilation en ligne : le fichier source contenant le kernel est lu dansle code de l’hote (solution que nous adoptons par la suite)
I Built du kernel lors de l’execution (OpenCL runtime library)I Avantage : portabilite, facilite les tests du kernelI Inconvenient : la compilation du kernel doit etre effectue par le
terminal ou il est deploye (solution inadaptee aux systemes embarques)
Compilation hors ligne : le fichier binaire du kernel est lu par le codede l’hote
I Pre-built du kernel par le compilateur OpenCL . Le binaire genere estcharge (OpenCL API).
I Avantage : rapiditeI Inconvenient : portabilite
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 38 / 54
Premier programme : lancement du programme
Programme
Un programme (= ensemble de kernels) est cree puis compile
// Creation du programme
cl_program clCreateProgramWithSource (cl_context context,
cl_uint count/*#fichiers*/, const char **strings /*noms
fichiers*/, const size_t *lengths/*longueurs des fichiers*/,
cl_int *errcode_ret)
//Compilation du programme
cl_int clBuildProgram(cl_program program, cl_uint num_devices
, const cl_device_id *device_list,
const char *options, // options compilation
void (*pfn_notify)(cl_program, void *user_data),
void *user_data)
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 39 / 54
Premier programme : lancer le kernelUne fois cree, le kernel est lance apres definission des arguments. Sacompilation a lieu lors de l’execution
// 1. Creation de kernel :
cl_kernel clCreateKernel (cl_program program,
const char *kernel_name, cl_int *errcode_ret)
//2. Definition de chaque argument
cl_int clSetKernelArg(cl_kernel kernel, cl_uint arg_index,//quel
argument size_t arg_size,//taille PROCHAIN argument
const void *arg_value) // Valeur
// 3. Appel du kernel
cl_int clEnqueueNDRangeKernel(cl_command_queue command_queue,
cl_kernel kernel, cl_uint work_dim /*1D,2D,3D*/, const size_t
*global_work_offset, const size_t *global_work_size//#work-items total
, const size_t *local_work_size, //#work-items par work-group
cl_uint num_events_in_wait_list, const cl_event
*event_wait_list, cl_event *event)F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 40 / 54
Premier programme : lancer le kernel
// 1. Creation du programme
size_t src_size = 124;
const char* path = shrFindFilePath("vecteur.cl", NULL);
const char* source = oclLoadProgSource(path, "", &src_size);
cl_program program = clCreateProgramWithSource(context, 1,
&source, &src_size, &error);
error = clBuildProgram(program, 1, &device,NULL,NULL,NULL);
cl_kernel vector_add_kernel = clCreateKernel(program,
"vector_add_gpu", &error);
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 41 / 54
Premier programme : lancer le kernel
// Arguments places dans la queue
clSetKernelArg(vector_add_k, 0, sizeof(cl_mem), &src_a_d);
clSetKernelArg(vector_add_k, 1, sizeof(cl_mem), &src_b_d);
clSetKernelArg(vector_add_k, 2, sizeof(cl_mem), &res_d);
clSetKernelArg(vector_add_k, 3, sizeof(size_t), &size);
// Lancement du kernel
const size_t local_ws = 512; // #work-items par work-group
//shrRoundUp renvoie le + petit multiple de local_ws > size
const size_t global_ws = shrRoundUp(local_ws, size);
//#work-items total
error = clEnqueueNDRangeKernel(queue, vector_add_k, 1, NULL,
&global_ws, &local_ws, 0, NULL, NULL);
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 42 / 54
Premier programme : lecture des resultats, liberation
cl_int clEnqueueReadBuffer (cl_command_queue command_queue,
cl_mem buffer, // a partir de quel buffer
cl_bool blocking_read, // lecture bloquante ou non
size_t offset, // offset depuis le debut
size_t cb, // combien d’octets lire
void *ptr, // pointer sur la memoire de l’hote
cl_uint num_events_in_wait_list,
const cl_event *event_wait_list, cl_event *event)
lecture des resultats et liberation des ressources allouees :
float* check = new float[size];
clEnqueueReadBuffer(queue, res_d, CL_TRUE, 0, mem_size,
check, 0, NULL, NULL);
clReleaseKernel(vector_add_k);
clReleaseCommandQueue(queue);
clReleaseContext(context);
clReleaseMemObject(src_a_d); clReleaseMemObject(src_b_d);
clReleaseMemObject(res_d);F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 43 / 54
Premier bilan: restriction
Le parallelisme des donnees : il existe une correlation entre lesidentifiants et l’organisation des work-items et les acces en memoire.Controle de flot: il est important que les meme instructions soientexecutees. Si plusieurs chemins peuvent etre empruntes, les differentschemins seront serialisesRestrictions :
pas de pointeur passe en argument au kernel,
pas de tableau de taille variable,
pas de recursion (pour les GPUs anciens)
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 44 / 54
Premier bilan: mise en garde
Le GPU parallelise efficacement si :
la meme operation est effectuee sur un ensemble de donnees (de taillerelativement moderee)
Chaque work-item fait des acces coherents en memoire GLOBALE :acces regroupes et sequentiels
La memoire globale est une sucession de mots de 16 ou 32 bits. Ici,chaque thread accede a un float (= 32 bits) en memoire Cas 1: accessequentiel aligne : 1 transaction Cas 2: acces sequentiel non aligne = 2transactions Cas 3: acces sequentiel espace = 2 transactions
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 45 / 54
Premier bilan: calcul de la bande passante theoriqueLe GPU parallelise efficacement si :
la meme operation est effectuee sur un ensemble de donnees (de taillerelativement moderee)Les echanges d’informations entre l’hote et le device sont minimisescar le bus PCI manque de bande passanteLe cout de chargement des donnees de l’hote vers le device (et visversa) est justifie. Cout injustifiable pour l’addition de 2 vecteurs.Pour l’addition de 2 matrices? le carre d’une matrice?
Hypotheses : NVIDIA GeForce GTX 280 DDR (Double Data Rate) RAMavec une cadence d’horloge (memory clock) de 1,107 MHz et une interfacememoire a 512-bitsQuestion: Quelle est la bande passance theorique (max)?Reponse : 1107.106.(512/8).2)/109 = 141.6Gbytes/secBande passante effective = ((Br + Bw)/109)/timeBr : nb d’octets lus par kernel, Bw : nb d’octets ecris par kernel i , time :exprime en seconde.Exemple : pour la copie d’une matrice 2048 x 2048,bande passante effective = 10482.4.2/109/timeF. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 46 / 54
A vous de jouer
Objectif: multiplier une matrice par un vecteur, voir deux matricesRappel: multiplication matrice par vecteura b c
d e fg h i
123
=
a + 2.b + 3.cd + 2.e + 3.fg + 2.h + 3.i
Rappel: multiplication de 2 matricesa b c
d e fg h i
j k lm n op q r
=a.j + b.m + c.p a.k + b.n + c .q a.l + b.o + c .rd .j + e.m + f .p d .k + e.n + f .q d .l + e.o + f .rg .j + h.m + i .p g .k + h.n + i .q g .l + h.o + i .r
Strategie: sachant que l’acces a la memoire globale est tres lent,maximisez le nombre d’acces a la memoire partagee (locale)
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 47 / 54
Rappel: fonction multipliant la matrice M par le vecteur V
La fonction ci-dessous n’est pas parallelisee
matrix_vector_mul(const float* M, uint width, uint height,
const float* V, float* W) {
for (uint y = 0; y < height; ++y)
{
const float* row = M + y * width;
float dotProduct = 0;
for (uint x = 0; x < width; ++x)
dotProduct += row[x] * V[x];
W[y] = dotProduct;
}
}
row pointe au debut de chaque ligne identifiee par y
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 48 / 54
Rappel: fonction multipliant la matrice M par le vecteur V
La fonction ci-dessous n’est pas parallelisee
matrix_vector_mul(const float* M, uint width, uint height,
const float* V, float* W) {
for (uint y = 0; y < height; ++y)
{
const float* row = M + y * width;
float dotProduct = 0;
for (uint x = 0; x < width; ++x)
dotProduct += row[x] * V[x];
W[y] = dotProduct;
}
}
row pointe au debut de chaque ligne identifiee par y
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 48 / 54
Work-item multipliant une matrice par un vecteur
__kernel void MatrixVectorMul0(const __global float* M,
uint width, uint height, const __global float*
V,__global float* W) {
uint y = get_global_id(0);
const float* row = M + y * width;
float dotProduct = 0;
for (uint x = 0; x < width; ++x)
dotProduct += row[x] * V[x];
W[y] = dotProduct;
}
Invonvenient : un work-item calcul une ligne et risque d’etre inoccupe
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 49 / 54
Work-item multipliant une matrice par un vecteur
__kernel void MatrixVectorMul1(const __global float* M,
uint width, uint height, const __global float* V,
__global float* W) {
unint y;
for (y=get_global_id(0); y<height; y+=get_global_size(0))
{
const float* row = M + y * width;
float dotProduct = 0;
for (uint x = 0; x < width; ++x)
dotProduct += row[x] * V[x];
W[y] = dotProduct;
}
}
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 50 / 54
Premier bilan
Le nombre d’elements calcule par chaque work-item = height/ #work-items (+1 pour certains work-items si height n’est pas unmultiple du # work-items)
Avantage : decouplage entre la taille de la matrice et le nombre dework-item s’executant
Remarque: un kernel doit s’executer sur un #work-items≥ #multiprocesseurs.
centaines de work-items par multiprocessor qui seraient un multiple dela taille de warp (= 32).
Le meilleur NDRange depend du kernel, du #registres et doit etre fixepar des experimentations
Autres optimisations : l’acces contigu a la memoire permettant uneseule transaction memoire
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 51 / 54
Acces memoire, multiplication matrice par un vecteur
__kernel void MatrixVectorMul1(...) {
unint y;
for(y=get_global_id(0);y<height;y+=get_global_size(0))
{
const float* row = M + y * width;
float dotProduct = 0;
for (uint x = 0; x < width; ++x)
dotProduct += row[x] * V[x];
W[y] = dotProduct;
}
}
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 52 / 54
Acces memoire, multiplication matrice par un vecteur
__kernel void mul(__global float*M, uint width,uint height,
__global float* V, __global float* W,__local float* partprod)) {unint y;
for(y=get_group_id(0); y<height; y +=get_num_groups(0)){
float sum = 0;
for(uint x=get_local_id(0);x<width;x+=get_local_size(0))
sum += row[x] * V[x];
partprod[get_local_id(0)]=sum;
if (get_local_id(0) == 0) {
float dotProduct = 0;
for (uint t = 0; t < get_local_size(0); ++t)
dotProduct += partprod[t];
W[y] = dotProduct; }
barrier(CLK_LOCAL_MEM_FENCE);}}
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 53 / 54
Conclusion: GPU for ever?
Nous assistons a une renaissance du calcul parallele caracterise par uneutilisation efficace de toutes les ressources, une utilisation avisee de laconcurrence. Critere primordiale : selectionner le bon materiel en fonctionde l’algorithme
GPU = processeur massivement multi-coeur caracterise par uneexecution SIMD
I GPU = special purpose hardware (parallelisme des donnees)I Implication multiples: les algorithmes doivent etre imperativement
designes pour eviter toute divergence au niveau des branchements.I Leur massivite implique de les maintenir occupesI Difficulte a debuger le code
CPU = general purpose hardwareI parfait pour un code serie ou un faible parallelisme avec de petites
unites de travail dont les taches a effectuer ne sont pas reliees,I facile a debuger, outils mature
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 54 / 54
David B. Kirk and Wen-mei W. HwuProgramming massively parallel processors A Hands-on approachlaurel.datsi.fi.upm.es/ media/proyectos/gopac/programming massively parallel processors.pdf
Kronos group
https://www.khronos.org
Kronos grouphttps://www.khronos.org
NvidiaOpenCL Best Practices Guide www.nvidia.com
F. Sailhan (CNAM, departement informatique)Introduction a la programmation massivement parallele November 21, 2014 54 / 54