Transcript
  • Page 1
  • Programmation de cartes graphiques
  • Page 2
  • OpenCL API + langage bas sur le C99 Conu pour programmer des systmes parallles htrognes: CPU multi-coeur, GPU, etc. On distingue le processeur hte des priphriques Un conteneur spcial (context) sert dinterface entre le processeur hte et les priphriques Les donnes et les tches (kernels) sont transfrs laide dune file dattente (command queue)
  • Page 3
  • Un priphrique vu par OpenCL
  • Page 4
  • Comparaison: C standard // Addition de 2 vecteurs de taille N // void vecadd(int *C, int* A, int *B, int N) { for(int i = 0; i < N; i++) { C[i] = A[i] + B[i]; }
  • Page 5
  • Comparaison: Programmation multithread void vecadd(int *C, int* A, int *B, int N, int NP, int tid) { int ept = N/NP; // nbre dlments par thread for(int i = tid*ept; i < (tid+1)*ept; i++) { C[i] = A[i] + B[i]; }
  • Page 6
  • Comparaison: OpenCL __kernel void vecadd(__global int *C, __global int* A, __global int *B) { int tid = get_global_id(0); // fonction OpenCL C[tid] = A[tid] + B[tid]; }
  • Page 7
  • Espace des indices En OpenCL, lespace des indices des processeurs peut avoir 1, 2 ou 3 dimensions. Il y a deux niveaux dindices: Un indice global unique pour chaque work-item du priphrique (NDRange) Un indice local unique pour chaque work-item lintrieur dun mme workgroup.
  • Page 8
  • NDRange et Workgroups
  • Page 9
  • Connatre son indice get_global_id(dim): indice globale du work-item appelant selon la dimension dim=0,1 ou 2 get_local_id(dim): indice local du work-item appelant get_group_id(dim): indice du workgroup auquel appartient le work-item appelant get_local_size(dim): taille de la dimension dim dans le workgroup du work-item appelant.
  • Page 10
  • Relation entre global et local get_global_id(dim) = get_local_size(dim)*get_group_id(dim) + get_local_id(dim)
  • Page 11
  • Plateformes Une plateforme est une implmentation de OpenCL spcifique un manufacturier donn Pour obtenir la liste des plateformes: cl_int clGetPlatformIDs(cl_uint num_entries, cl_platform_id *platforms, cl_uint *num_platforms) clGetPlatformInfo pour obtenir de linformation sur une plateforme donn
  • Page 12
  • Exemple 1 cl_int status; cl_uint numPlatforms = 0; cl_platform_id *platforms = NULL; status = clGetPlatformIDs(0, NULL, &numPlatforms); platforms = (cl_platform_id*)malloc( numPlatforms*sizeof(cl_platform_id)); status = clGetPlatformIDs(numPlatforms, platforms, NULL); char Name[1000]; clGetPlatformInfo(platforms[0], CL_PLATFORM_NAME, sizeof(Name), Name, NULL); printf("Name of platform : %s\n", Name);
  • Page 13
  • Priphriques Pour chacune des plateformes (ex. NVIDIA), on peut obtenir la liste des priphriques associs: cl_int clGetDeviceIDs(cl_platform_id platform, cl_device_type device_type, cl_uint num_entries, cl_device_id *devices, cl_uint *num_devices)
  • Page 14
  • Exemple 2 cl_uint numDevices = 0; cl_device_id *devices = NULL; status = clGetDeviceIDs( platforms[0], CL_DEVICE_TYPE_ALL, 0, NULL, &numDevices); devices = (cl_device_id*)malloc( numDevices*sizeof(cl_device_id)); status = clGetDeviceIDs( platforms[0], CL_DEVICE_TYPE_ALL, numDevices, devices, NULL);
  • Page 15
  • Contextes Un contexte est un type de conteneur permettant de communiquer des donnes et des instructions des priphriques On utilise la fonction: clCreateContext Le premier paramtre sert limit la port du contexte, par exemple en spcifiant une plateforme particulire. Le callback sert a fournir une fonction qui pourra fournir davantage dinformation sur les erreurs se produisant tout au long de lutilisation du contexte.
  • Page 16
  • Contextes cl_context clCreateContext ( const cl_context_properties *properties, cl_uint num_devices, const cl_device_id *devices, void (CL_CALLBACK *pfn_notify)( const char *errinfo, const void *private_info, size_t cb, void *user_data), void *user_data, cl_int *errcode_ret)
  • Page 17
  • Exemple 3 cl_context context = NULL; context = clCreateContext( NULL, numDevices, devices, NULL, &status);
  • Page 18
  • File de commandes On a dj dit quun contexte est un conteneur permettant de communiquer avec un priphrique. On ajoute et retire linformation dun contexte laide dune file dattente appele file de commande (command queue). Toutes les commandes indiquant une communication hte-priphrique commencent par clEnqueue.
  • Page 19
  • Cration dune file de commandes cl_command_queue clCreateCommandQueue( cl_context context, cl_device_id device, cl_command_queue_properties properties, cl_int* errcode_ret) Note: Le paramtre properties sert, entre autres, indiquer si les lments seront pris dans lordre.
  • Page 20
  • Exemple 4 cl_command_queue cmdQueue; cmdQueue = clCreateCommandQueue( context, devices[0], 0, &status);
  • Page 21
  • Objets mmoire Avant dtre transfrs, les donnes doivent tre converties dans un format particulier. Il y a deux formats: Les tampons (buffers) Les images Les tampons sont lquivalent des tableaux en C et sont stocks de faon contigu en mmoire. Les images sont des objets opaques et sont places en mmoire de faon optimiser les performances.
  • Page 22
  • Cration dun tampon cl_mem clCreateBuffer( cl_context context, cl_mem_flags flags, size_t size, void *host_ptr, cl_int *errcode_ret) Note: Le paramtre flags sert indiquer si le tampon est en lecture, en criture ou les deux.
  • Page 23
  • crire dans un tampon cl_int clEnqueueWriteBuffer ( cl_command_queue command_queue, cl_mem buffer, cl_bool blocking_write, // CL_TRUE pour appel bloquant size_t offset, size_t cb, const void *ptr, cl_uint num_events_in_wait_list, const cl_event *event_wait_list, cl_event *event)
  • Page 24
  • Lire dans un tampon cl_int clEnqueueReadBuffer( cl_command_queue command_queue, cl_mem buffer, cl_bool blocking_read, size_t offset, size_t size, void *ptr, cl_uint num_events_in_wait_list, const cl_event *event_wait_list, cl_event *event)
  • Page 25
  • Exemple 5 cl_mem bufferA, bufferB, bufferC; bufferA = clCreateBuffer( context, CL_MEM_READ_ONLY, datasize, NULL, &status); bufferB = clCreateBuffer( context, CL_MEM_READ_ONLY, datasize, NULL, &status); bufferC = clCreateBuffer( context, CL_MEM_WRITE_ONLY, datasize, NULL, &status); status = clEnqueueWriteBuffer( cmdQueue, bufferA, CL_FALSE, 0, datasize, A, 0, NULL, NULL); status = clEnqueueWriteBuffer( cmdQueue, bufferB, CL_FALSE, 0, datasize, B, 0, NULL, NULL);
  • Page 26
  • Kernels Il faut distinguer le programme C de lordinateur hte du programme C OpenCL qui sera excut sur un priphrique. Un programme C OpenCL est une collection de fonctions appeles kernels Le type de retour de ces fonctions doit tre void Les kernels reprsentent les tches que lon peut ordonnancer de faon dynamique sur les priphriques.
  • Page 27
  • Cration et excution dun kernel 1. Le code source est stock sous la forme dune chane de caractres 2. La chaine est convertie en objet programmes laide de la commande clCreateProgramWithSource() 3. Un objet programme est compil laide de la commande clBuildProgram() 4. Un kernel est obtenu laide de la fonction 5. Les paramtres du kernel sont affects laide de la commande clSetKernelArg() 6. Le kernel est finalement excut laide de la commande clEnqueueNDRangeKernel()
  • Page 28
  • Cration dun objet programme cl_program clCreateProgramWithSource ( cl_context context, cl_uint count, const char **strings, const size_t *lengths, cl_int *errcode_ret)
  • Page 29
  • Compiler un objet programme cl_int clBuildProgram ( cl_program program, cl_uint num_devices, const cl_device_id *device_list, const char *options, void (CL_CALLBACK *pfn_notify)(cl_program program, void *user_data), void *user_data)
  • Page 30
  • Obtenir un kernel cl_kernel clCreateKernel ( cl_program program, const char *kernel_name, cl_int *errcode_ret) Note: *kernel_name contient le nom dune fonction dans le programme source.
  • Page 31
  • Affecter les paramtres cl_int clSetKernelArg ( cl_kernel kernel, cl_uint arg_index, size_t arg_size, const void *arg_value) Note: Les paramtres demeurent affects au kernel tant quil ny a pas de modification explicite laide de clSetKernelArg()
  • Page 32
  • Excuter un kernel cl_int clEnqueueNDRangeKernel( cl_command_queue command_queue, cl_kernel kernel, cl_uint work_dim, const size_t *global_work_offset, const size_t *global_work_size, const size_t *local_work_size, cl_uint num_events_in_wait_list, const cl_event *event_wait_list, cl_event *event)
  • Page 33
  • Organisation de la mmoire
  • Page 34
  • Espaces dadressage Les qualificat __global: Mmoire partage par tous les work-items du kernel. __constant: Mmoire en lecture seule partage par tous les work-items du kernel. __local: Mmoire partage par tous les work-items dun mme work-group. __private: Mmoire prive pour chaque work-item.
  • Page 35
  • Espace dadressage par dfaut Pour amliorer la performance, on utilise autant que possible la mmoire __local ou __private. Sil ny a pas de qualificatif, les paramtres des fonctions et les variables locales sont __private Le qualificatif des paramtres qui sont des pointeurs doit tre spcifi et ne peut pas tre __private. Les mmoires __private et __local ne sont pas prserves la fin de lexcution dun work-item. La seule faon de retourner une valeur lhte est via la mmoire __global.

Top Related