Programmation parallèle et mise en œuvre des GPU Jean-Ferdy Susini! Maître de Conférences - CNAM! Département Informatique Paris, 21/10/14
Plan du cours 2 Objectif : sensibiliser à la programmation parallèle des GPU Rappel sur la programmation concurrente et le parallèle Présentation des GPU et ouverture de ces «coprocesseurs» à la programmation Définition du modèle de calcul d OpenCL Mise en œuvre d OpenCL Démo en Java TP
Présentation du cours 3 http://jeanferdysusini.free.fr/cours/fip-2/
Programmation parallèle Paris, 21/10/14
Concurrence et/ou parallélisme 5 La concurrence est un problème ancien en informatique et en particulier dans les systèmes d exploitation : la mise à disposition d un nombre limité de ressources à un nombre arbitraire de tâches utilisatrices conduit à des situations de concurrence. Plusieurs solutions existent, à commencer par la possibilité de simuler des exécutions parallèles à travers de l interleaving."! Ordonnancement de tâches
Ordonnancement dans un SE 6 Objectif : gestion des ressources matérielles, dont la ressource processeur Architecture Monoprocesseur" optimiser l utilisation de la ressource processeur" améliorer la réactivité du système Architecture Multiprocesseur" profiter des ressources CPU supplémentaires" équilibrer la charge Au sein d un même processus, les threads satisfont ces mêmes besoins
Ordonnancement dans un SE 7 Les processus sont des tâches lourdes qui communiquent peu entre elles (à travers certains services du système)" L ordonnancement défini les règles d accès à la ressource processeur" Les communications inter-processus doivent guider la politique d ordonnancement" Définir les règles d ordonnancement est difficile en particulier dans un OS déjà existant, qui requiert la connaissance de nombreux mécanismes sous-jacents de l OS
Programmation 8 Au niveau des langages de programmation, la problématique de la concurrence et du parallélisme a été finalement peu considérée" Pourtant de nos jours le parallélisme est omniprésent" Mis à part quelques langages assez confidentiels, l essentiel des langages ont introduit la notion de parallélisme à travers les outils fournis par les SE, zn particulier les threads
Les threads 9 Un exemple : le langage Java"! Chaque thread exécute un code séquentiel! Modèle de programmation hérité de la programmation système : processus légers (partage de l'espace d'adressage, pile et registres séparés)! S appuie sur un service d ordonnancement! Correspondances ("Mapping") avec les processus légers : un à un, plusieurs à un, plusieurs à plusieurs
Les threads 10 Issus des systèmes d exploitation => on retrouve des stratégies d'ordonnancement :" Ordonnancement coopératif : le fil d'exécution "qui a" le processeur doit le relâcher explicitement. La stratégie d'ordonnancement diffère dans l'élection du processus auquel le processeur doit-être alloué! Ordonnancement préemptif: l'ordonnanceur du système peut contraindre un fil d'exécution à relâcher le processeur qui lui a été alloué en fonction de critères (quota, priorité, interruption,... ) qui constituent une part de la politique d'ordonnancement
L ordonnancement coopératif 11 Simple à programmer (coroutines)! Efficacité : maîtrise des "context switchs", -verrous, - changements de mode de protection! Pb avec les mauvais citoyens (threads non coopérative)! Discipline de programmation => transformation du "legacy software
L ordonnancement préemptif 12 pas de discipline de programmation (mais subsiste problèmes de réentrance,...)! Service d ordonnancement plus riche! Mieux adapté aux systèmes multiprocesseurs et/ou distribués! plus difficile à implanter (politiques plus complexes)! problème d'efficacité "context switchs" forcés arbitrairement, protection des données partagées nécessaires
Les threads 13 En Java :" API standardisée : Thread, chaque objet possède un verrou, mot clés synchronized et volatile! sémantique faible (ouverture, portabilité,...)! omniprésence : AWT, GC,...! contrôle fin problématique (les méthodes stop, suspend, resume sont étiquetées "deprecated" à partir de Java 1.2)! Mais les choses changent car enjeux majeur! plusieurs refontes du modèle de gestion mémoire et des threads de la JVM depuis Java 5.0
Le parallélisme 14 De nos jours, le parallélisme des ressources de calcul est partout :" pipelining, exécution out of order, architecture superscalaire : pose des problèmes de parallélisme et de concurrence que la plupart des compilateurs modernes arrivent à masquer" architectures distribuées, ont vu émerger des architectures logiciels de haut niveau, permettant de profiter de ce parallélisme souvent sur un modèle de tâches faiblement couplées." architectures multi-processeurs et multi-cœurs et de nos jours les architectures GPU, font émerger de nouveaux besoins pour la prise en compte efficace du parallélisme.
Le parallélisme 15 Les threads constituent une réponse bas niveau (empruntée au SE) qui reste une affaire de spécialistes" La sémantique est souvent faible voir trop sous spécifiée pour des raisons de portabilité pour être utilisées de façon fiable" Les threads ne permettent généralement pas le contrôle de la politique d ordonnancement qui les gère." Les threads dans un langage de programmation interfèrent souvent avec les principes de structuration mis en avant par le langage (fonctionnel, objet ) de nouveaux modèles font leur apparition
Modèles de programmation parallèle 16 De nombreuses solutions apparaissent, proposant de nouveaux modèles de structuration du code parallèle au dessus des solutions bas niveaux :" Task/Workers" MapReduce" Open MP" MPI" Cuda" OpenCL"
Principe généraux autour de la programmation des GPU Paris, 21/10/14
Qu est-ce qu un GPU 18 Graphics Processing Unit est un (co-)processeur graphique capable d effectuer très efficacement des calculs sur des images (2D, 3D, vidéos )
Qu est-ce qu un GPU 18 Graphics Processing Unit est un (co-)processeur graphique capable d effectuer très efficacement des calculs sur des images (2D, 3D, vidéos ) Caractéristiques :" un nb d unités de calcul vertigineux, souvent > 1000" dédiés au calcul, ils sont peu performants en cas de traitements conditionnels (pipeline très long)" chaque coeur accède efficacement à une quantité relativement faible de données correctement localisée et applique une série de traitement répétitif sur ces données
Qu est-ce qu un GPU 19 Traite principalement 2 pipelines :" Le traitement des vertex :"! Infographie 2D-3D tesselation vertices pipeline clipping culling! rotation échelle translation réflexion réfraction Le traitement des pixels : Passage progressif de pipelines! statiques aux shaders! programmables alpha test blending Z buffer pixel treatments trammage texture light mapping composition
Qu est-ce qu un GPU 20 Un exemple : Kepler GK110" > 7 milliard de transistor" 15 unités SMX de :" 192 cœurs opérant en float" 64 cœurs en double" 32 cœurs pour des fonctions particulières" 32 unités de transferts mémoire" 65536 registres de 32 bits
Peut-on exploiter cette puissance de calcul? 21 Calcul répétitif sur de grandes quantités de données : un problème bien connu dans le HPC : SIMD-SPMD" Évolution des shaders vers le GP-GPU (OpenCL-Cuda)" Modèle de programmation parallèle" Indépendamment chaque cœur est beaucoup plus lent qu un CPU (peu de cache, peu de mémoire, fréquence relativement faible ). C est le parallélisme massif qui fait la différence.
Petite histoire du GP-GPU 22 2006 : Nvidia lance Cuda, ATI lance close to Metal" 2008 : Apple propose OpenCL au Kronos Group, constitue un groupe de travail avec AMD/ATI et Nvidia : le Khronos Compute Working Group" dec 2008 OpenCL 1.0" août 2009 SnowLeopard implantation complète" sept 2009 Nvidia sort son implantation OpenCL 1.0" juin 2010 : OpenCL 1.1" juin 2011 : Intel SDK OpenCL 1.1 GPU/CPU" novembre 2011 : OpenCL 1.2" juillet 2013 : OpenCL 2.0" sept 2013 : Mavericks Open CL 1.2
Programmation complexe 23 La programmation GP-GPU reste quelque chose de complexe à maitriser, qui nécessite une transformation des algorithmes pour tenir compte des spécificités matérielles" En particulier le paramétrage du matériel constitue souvent la plus grosse partie du travail. Peu d heuristiques existent pour guider le développeur dans cette tâche" Le débugging et le profiling restent encore limités
Le modèles de programmation 24 OpenMP : API multi plate-forme pour les langages Fortran, C et C++ pour programmer des architectures parallèles à mémoire partagée. Elle consiste en en certain nombre de directives de compilation (pragma), des bibliothèques additionnelles et des variables d environnement (AMD, IBM, Intel, Cray, HP, Nvidia )" Cuda : modèle de programmation propriétaire des cartes Nvidia, propose des directives de compilation (pragma OpenACC), des librairies de fonctions optimisées, des extensions de C, C++ et Fortran, utilisant un compilateur dédié mvcc basé sur LLVM" DirectCompute : solution propriétaire de Microsoft associé à DirectX 11. Partage de nombreux points commun avec ses concurrents." Enzo : de PathScale qui génère du code Cuda optimisé à partir de C C++ ou Fortran annoté par des directives de compilations (pragma)." OpenCL : langage dérivé de C99 et des APIs pour contrôler la plate-form, plus général que Cuda les deux outils sont cependant très proches dans l esprit orienté parallélisme de tâche ou parallélisme de donnés. Comme les modèles sont proches et les performances voisines, nous n étudierons qu OpenCL.
Le modèle OpenCL Paris, 21/10/14
Modélisation d une application Open CL 26 On superpose 4 modèles pour appréhender une application OpenCL :" Le modèle matériel qui abstrait la plateforme et organise les ressources." Le modèle d exécution qui définit l organisation des traitements et leur répartition sur les ressources de calcul matériel" Le modèle mémoire qui définit la hiérarchisation d accès aux éléments de mémoire traités" Le modèle de programmation qui décrit comment les programmes sont spécifiés
Une plateforme OpenCL 27 L organisation d une plateforme OpenCL, suppose l existence d un processus hôte qui se connecte à un ensemble de périphériques OpenCL (devices). Par exemple une carte graphique." Un périphérique OpenCL peut être divisé en une ou plusieurs unités de calcul (Computing Units - CU)" Les CU se divisent en cœurs (Processing Elements - PE)" L objectif pour l hôte est d envoyer des traitements s exécuter sur les PE qui permettent des calculs selon un paradigme SIMD/SPMD.
Une plateforme OpenCL plateforme OpenCL 28 device OpenCL CU CU CU PEs device OpenCL processus" hôte CU CU CU CU device OpenCL CU CU
Modèle d exécution 29 Le programme hôte est le programme du processus hôte et ne s exécute pas sur les devices OpenCL" Un traitement sur un élément de donné est appelé un Work-Item (Exécution d un kernel - programme à exécuter - sur un cœur et sur un élément de donné)" Pour un même kernel, les WorkItems sont organisés dans un espace à N dimensions ND-Range (1D, 2D, 3D). Chaque WorkItem est repéré par des index dans cet espace à N dimension" Pour un même kernel, les WorkItems sont regroupés en Workgroups
Modèle d exécution Exemple en 2D 30 WorkGroup WorkGroup G MAX y WorkGroup G MAX x L MAX x WorkGroup L MAX y Nb total de WorkItem :" G MAX x * L MAX x" + G MAXy * L MAXy "! Chaque WorkItem a un" index local" 0 lx < L MAX x" 0 ly < L MAX y" Chaque Work Group à un" index de groupe" 0 Gx < G MAX x" 0 Gy < G MAX y
Modèle mémoire Une mémoire globale partagée entre tous les WorkItems et les WorkGroups et accessible en lecture et en écriture" 31 Des constantes globale partagées entre tous les WorkItems et les WorkGroups et accessible en lecture seule" Une mémoire locale partagée entre tous les WorkItems d un même WorkGroup et accessible en lecture et en écriture" Une mémoire privée accessible à un seul WorkItem" Les échanges mémoires suivent le chemin suivant :" hôte -> globale -> locale -> privée et retour
Modèle mémoire 32 device WorkGroup WorkGroup Privée Privée Privée Privée Privée Privée Privée Privée WorkItem WorkItem WorkItem WorkItem WorkItem WorkItem WorkItem WorkItem Locale Locale Globale Constantes hôte Mémoire hôte
Modèle de programmation 33 Kernel : élément de base d exécution : programme court similaire à une fonction en langage C," Program : une ensemble de fonctions C. Certaines seront éligibles pour constituer un kernel (point d entrée d un programme à exécuter sur un PE)." Application : un processus qui compile des programmes et place dans des files d exécutions des kernels et leur données pour être traités par le GPU. Les files d attente peuvent-être traitées dans l ordre ou bien explicitement sans respecter l ordre
Les kernels Création de programmes portables :" 34 Définition d un programme sous la forme d une chaine de caractères représentant le texte du programme en langage OpenCL C (source). Ou un binaire précompilé pour faciliter la compilation finale" Le programme représente un petit ensemble de fonctions et de kernels organisés comme une bibliothèque" Compilation à la volée à travers une API en spécifiant le périphérique cible de la compilation et des paramètres de compilation" Création d un kernel par sélection d une fonction et la spécification des arguments et donc des éléments de donnés sur lesquels le kernel va opérer
Le files de commandes 35 Le processus hôte soumet les kernels et les données à la plateforme OpenCL (program objects -> kernel objects et memory objects)" Des files de commandes sont associées aux devices OpenCL, permettant de soumettre un à un les kernels, des objets mémoire et des commandes de synchronisation (attente de la fin d exécution d une tâche )" Le contenu des files de commandes peut être traité dans l ordre ou dans un ordre privilégié par la plateforme (out of order), les événements de synchronisation permettent alors de remettre les choses en ordre
Cohérence mémoire et synchronisation C est un problème critique de la programmation parallèle, comment est-il traité dans le cas d OpenCL?" 36 Ne devrait pas être nécessaire entre les WorkItems (chaque WorkItem travaille sur sa part de données)." A sein d un WorkItem les opérations de lecture et d écriture en mémoire sont atomiques" Entre différentes tâches OpenCL la cohérence est obtenue par l utilisation des événements de synchronisation" Les WorkGroups permettent à des WorkItems de coordonner leurs accès mémoire et à partager de la mémoire entre eux par synchronisation de leur exécution
Mise en œuvre d OpenCL Paris, 21/10/14
Anatomie d une application OpenCL 38 Récupération des informations sur la plateforme Construction du Context OpenCL et selection des devices à utiliser Chargement et compilation des programmes Création des objets mémoires Création des files de commandes et transfert des données et des programmes sur la plate-forme Exécution des kernels, synchronisation et lecture des résultats
Récupération des infos 39 cl_int oclgetplatformid (cl_platform_id *platforms) cl_int clgetdeviceids (cl_platform_id platform, cl_device_type device_type, cl_uint num_entries, cl_device_id *devices, cl_uint *num_devices)
Création du context 40 cl_context clcreatecontext (const cl_context_properties *properties, cl_uint num_devices, const cl_device_id *devices, void (*pfn_notify)(const char *errinfo, const void *private_info, size_t cb, void *user_data), void *user_data, cl_int *errcode_ret)
Création des files de commandes 41 cl_command_queue clcreatecommandqueue (cl_context context, cl_device_id device, cl_command_queue_properties properties, cl_int *errcode_ret)
Création des objets mémoire 42 cl_mem clcreatebuffer (cl_context context, cl_mem_flags flags, size_t size, void *host_ptr, cl_int *errcode_ret) cl_int clsetkernelarg (cl_kernel kernel, cl_uint arg_index, size_t arg_size, const void *arg_value)
Kernels OpenCL 43 cl_program clcreateprogramwithsource (cl_context context, cl_uint count, const char **strings, const size_t *lengths, cl_int *errcode_ret) cl_int clbuildprogram (cl_program program, cl_uint num_devices, const cl_device_id *device_list, const char *options, void (*pfn_notify)(cl_program, void *user_data), void *user_data) cl_kernel clcreatekernel (cl_program program, const char *kernel_name, cl_int *errcode_ret)
Exécution 44 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) cl_int clenqueuereadbuffer (cl_command_queue command_queue, cl_mem buffer, cl_bool blocking_read, size_t offset, size_t cb, void *ptr, cl_uint num_events_in_wait_list, const cl_event *event_wait_list, cl_event *event)
OpenCL en Java Paris, 21/10/14
JOCL 46 Plusieurs «bindings» non officiels existent. Nous allons utiliser un binding strict appelé JOCL :" http://www.jocl.org" Offre une interface à travers des méthodes native (s appuie sur JNA) vers des fonctions en C reprenant l ensemble des spécifications OpenCL 1.2