Programmation parallèle et distribuée



Documents pareils
M2-Images. Rendu Temps Réel - OpenGL 4 et compute shaders. J.C. Iehl. December 18, 2013

Introduction au calcul parallèle avec OpenCL

Introduction à CUDA.

. Plan du cours. . Architecture: Fermi ( ), Kepler (12-?)

Une bibliothèque de templates pour CUDA

Initiation au HPC - Généralités

Segmentation d'images à l'aide d'agents sociaux : applications GPU

Introduction à la programmation des GPUs

Architecture des calculateurs

1 Architecture du cœur ARM Cortex M3. Le cœur ARM Cortex M3 sera présenté en classe à partir des éléments suivants :

Rappels d architecture

Architecture des ordinateurs

Calcul multi GPU et optimisation combinatoire

Contrôle Non Destructif : Implantation d'algorithmes sur GPU et multi-coeurs. Gilles Rougeron CEA/LIST Département Imagerie Simulation et Contrôle

Limitations of the Playstation 3 for High Performance Cluster Computing

INF6500 : Structures des ordinateurs. Sylvain Martel - INF6500 1

Génération de code binaire pour application multimedia : une approche au vol

Fonctionnement et performance des processeurs

Vers du matériel libre

Exécution des instructions machine

Argument-fetching dataflow machine de G.R. Gao et J.B. Dennis (McGill, 1988) = machine dataflow sans flux de données

Calcul scientifique précis et efficace sur le processeur CELL

Potentiels de la technologie FPGA dans la conception des systèmes. Avantages des FPGAs pour la conception de systèmes optimisés

Une dérivation du paradigme de réécriture de multiensembles pour l'architecture de processeur graphique GPU

Concept de machine virtuelle

Architecture des Ordinateurs. Partie II:

Parallélisme et Répartition

Métriques de performance pour les algorithmes et programmes parallèles

La technologie Java Card TM

<Insert Picture Here> Solaris pour la base de donnés Oracle

TP n 2 Concepts de la programmation Objets Master 1 mention IL, semestre 2 Le type Abstrait Pile

Plan du cours Cours théoriques. 29 septembre 2014

Introduction à la Programmation Parallèle: MPI

THEME 1 : L ORDINATEUR ET SON ENVIRONNEMENT. Objectifs

td3a correction session7az

Cahier des charges. driver WIFI pour chipset Ralink RT2571W. sur hardware ARM7

TPs Architecture des ordinateurs DUT Informatique - M4104c SUJETS. R. Raffin Aix-Marseille Université romain.raffin-at-univ-amu.fr

Conception de circuits numériques et architecture des ordinateurs

La mémoire. Un ordinateur. L'octet. Le bit

03/04/2007. Tâche 1 Tâche 2 Tâche 3. Système Unix. Time sharing

ASR1 TD7 : Un microprocesseur RISC 16 bits

1. Structure d un programme C. 2. Commentaire: /*..texte */ On utilise aussi le commentaire du C++ qui est valable pour C: 3.

DE L ALGORITHME AU PROGRAMME INTRO AU LANGAGE C 51

TP 1. Prise en main du langage Python

Machines Virtuelles. et bazard autour. Rémi Forax

Structure d un programme et Compilation Notions de classe et d objet Syntaxe

Cours Programmation Système

EPREUVE OPTIONNELLE d INFORMATIQUE CORRIGE

Programmation parallèle et ordonnancement de tâches par vol de travail. Thierry Gautier MOAIS, INRIA, Grenoble

ARDUINO DOSSIER RESSOURCE POUR LA CLASSE

MapReduce. Malo Jaffré, Pablo Rauzy. 16 avril 2010 ENS. Malo Jaffré, Pablo Rauzy (ENS) MapReduce 16 avril / 15

Systèmes et traitement parallèles

Programmation système I Les entrées/sorties

Hiérarchie matériel dans le monde informatique. Architecture d ordinateur : introduction. Hiérarchie matériel dans le monde informatique

Cours intensif Java. 1er cours: de C à Java. Enrica DUCHI LIAFA, Paris 7. Septembre Enrica.Duchi@liafa.jussieu.fr

Module.NET 3 Les Assemblys.NET

TP SIN Traitement d image

TP1 : Initiation à Java et Eclipse

Principes. 2A-SI 3 Prog. réseau et systèmes distribués 3. 3 Programmation en CORBA. Programmation en Corba. Stéphane Vialle

Introduction à MATLAB R

Modélisation des interfaces matériel/logiciel

SYSTÈME DE GESTION DE FICHIERS

Table des matières PRESENTATION DU LANGAGE DS2 ET DE SES APPLICATIONS. Introduction

Choix d'un serveur. Choix 1 : HP ProLiant DL380 G7 Base - Xeon E GHz

Projet de développement

Exécutif temps réel Pierre-Yves Duval (cppm)

Analyse de sécurité de logiciels système par typage statique

Surveillance de Scripts LUA et de réception d EVENT. avec LoriotPro Extended & Broadcast Edition

Big Data et Graphes : Quelques pistes de recherche

Licence Bio Informatique Année Premiers pas. Exercice 1 Hello World parce qu il faut bien commencer par quelque chose...

1-Introduction 2. 2-Installation de JBPM 3. 2-JBPM en action.7

Analyse de performance, monitoring

Brefs rappels sur la pile et le tas (Stack. / Heap) et les pointeurs

Pour signifier qu'une classe fille hérite d'une classe mère, on utilise le mot clé extends class fille extends mère

Exigences système Edition & Imprimeries de labeur

Cours Informatique 1. Monsieur SADOUNI Salheddine

Programmation parallèle et distribuée

imvision System Manager

Temps Réel. Jérôme Pouiller Septembre 2011

Software and Hardware Datasheet / Fiche technique du logiciel et du matériel

Tests de performance du matériel

Formation IPICO du 10/11 Février 2011 :

UEO11 COURS/TD 1. nombres entiers et réels codés en mémoire centrale. Caractères alphabétiques et caractères spéciaux.

Comme chaque ligne de cache a 1024 bits. Le nombre de lignes de cache contenu dans chaque ensemble est:

Centre CPGE TSI - Safi 2010/2011. Algorithmique et programmation :

Windows Server Chapitre 1: Découvrir Windows Server 2008

Eléments d architecture des machines parallèles et distribuées

Vulgarisation Java EE Java EE, c est quoi?

Chapitre V : La gestion de la mémoire. Hiérarchie de mémoires Objectifs Méthodes d'allocation Simulation de mémoire virtuelle Le mapping

Programmation parallèle et distribuée

INTRODUCTION A JAVA. Fichier en langage machine Exécutable

SYSTÈME DE GESTION DE FICHIERS SGF - DISQUE

UE Programmation Impérative Licence 2ème Année

Compilation (INF 564)

Bases Java - Eclipse / Netbeans

Conventions d écriture et outils de mise au point

Gestionde la conformité des licenses

INITIATION AU LANGAGE C SUR PIC DE MICROSHIP

Transcription:

Programmation parallèle et distribuée (GIF-4104/7104) Programmation massivement multifilaire sur GPU avec OpenCL (hiver 2013) Marc Parizeau, Département de génie électrique et de génie informatique

OpenCL Open Computing Language spécification ouverte et libre Initialement conçu par Apple proposé au Khronos Group en 2008 (version 1.0) consortium sans but lucratif, financé par diverses compagnies dont ATI, Intel, NVIDIA, SGI et Sun/Oracle Framework pour écrire des programmes qui s'exécutent à la fois sur des CPUs et GPUs CPU = Central Processing Unit GPU = Graphics Processing Unit GPGPU = General Purpose computing on Graphics Processing Unit Version actuelle: OpenCL 1.2 (nov. 2011) 2

Les GPUs sont caractérisés par des centaines de cœurs de traitement 3

Plan Architecture des GPUs ATI/AMD NVIDIA Cell OpenCL la plateforme les principales fonctions de l'api les étapes d'un programme la mémoire l'ordonnancement des fils d'exécution CUDA (Compute Unified Device Architecture) 4

CPU Beaucoup de surface investie en logique de contrôle optimisé pour réduire la latence des opérations unifilaires cache multi-niveau peu de registres réordonnancement des instructions pour alimenter les pipelines Control Logic ALU L2 Cache L1 Cache ~ 25GBPS System Memory L3 Cache A present day multicore CPU could have more than one ALU ( typically < 32) and some of the cache hierarchy is usually shared across cores Perhaad Mistry & Dana Schaa, Northeastern Univ Computer Architecture Research Lab, with Ben Gaster, AMD 2011 5

GPU Beaucoup de cœurs moins de logique beaucoup de registres Beaucoup de fils d'exécution parallèles commutation à faible latence Beaucoup d'unités arithmétiques par cœur mémoire rapide permet d'alimenter toutes les unités arithmétiques Optimisé pour le débit Simple ALUs Cache High Bandwidth bus to ALUs On Board System Memory Perhaad Mistry & Dana Schaa, Northeastern Univ Computer Architecture Research Lab, with Ben Gaster, AMD 2011 6

CPU vs. GPU - GFLOPs 7

Bande passante mémoire 8

GPU AMD 9

Par exemple, la carte AMD 5870 - Cypress 20 compute units 16 cœurs par unit 5 multiply-add par cœur 2.72 téraflops (simple précision) 544 gigaflops (double précision) 10

Moteur SIMD Un engin SIMD est aussi appelé «compute unit» un compute unit regroupe 16 cœurs un cœur peut effectuer jusqu'à 5 multiply-add par cycle les cœurs d'un même groupe exécutent tous les mêmes séquences d'instructions T-Processing Element One SIMD Engine One Stream Core Instruction and Control Flow General Purpose Registers Branch Execution Unit Processing Elements Source: AMD Accelerated Parallel Processing OpenCL Programming Guide 11

AMD et OpenCL Compute device un GPU physique Compute unit ensemble de processeurs élémentaires qui exécutent la même instruction Processing element un des coeurs (Stream Core) pouvant exécuter X opérations par cycle 12

Architecture mémoire (AMD 5870) SIMD Engine LDS, Registers Compute Unit to Memory X-bar L2 Cache L1 Cache LDS Write Cache Atomic Path Perhaad Mistry & Dana Schaa, Northeastern Univ Computer Architecture Research Lab, with Ben Gaster, AMD 2011 Mémoire locale à chaque compute unit local data store (LDS) registres Cache L1 pour chaque compute unit Cache L2 partagée entre les compute units 13

AMD et OpenCL Une partie de la mémoire globale est exposée à OpenCL La mémoire locale correspond au LDS La mémoire privée utilise les registres La mémoire constante utilise la cache L1 Private Memory Workitem 1 Compute Unit 1 Local Memory Private Memory Workitem 1 Compute Device Global / Constant Memory Data Cache Global Memory Compute Device Memory Private Memory Workitem 1 Compute Unit N Local Memory Private Memory Workitem 1 14

GPU NVIDIA CUDA Core Dispatch Port Operand Collector FP Unit Int Unit Result Queue Par exemple, la carte GTX 480 15 streaming multiprocessors (SM) chaque SM comporte 32 cœurs CUDA (processeurs élémentaires) total de 480 cœurs CUDA Un SM exécute des fils en groupe de 32 appelé «warp» Les cœurs CUDA comportent un ALU et un FPU chacun Warp Scheduler Core Core Core Core Core Core Core Core Dispatch Unit Core Core Core Core Core Core Core Core Instruction Cache Register File 32768 x 32bit Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Interconnect Memory LDST L1 Cache / 64kB Shared Memory L2 Cache Warp Scheduler Dispatch Unit LDST LDST LDST LDST LDST LDST LDST LDST LDST LDST LDST Perhaad Mistry & Dana Schaa, Northeastern Univ Computer Architecture Research Lab, with Ben Gaster, AMD 2011 SFU SFU SFU SFU 15

GPU NVIDIA 16

Architecture mémoire La Cache L1 est configurable pour chaque SM mémoire partagée mémoire globale La mémoire partagée sert entre les fils d'un même groupe Chaque SM possède aussi une banque de 32Ko pour les registres Registers Shared Memory Global Memory Thread Block L1 Cache L2 Cache Perhaad Mistry & Dana Schaa, Northeastern Univ Computer Architecture Research Lab, with Ben Gaster, AMD 2011 17

NVIDIA et OpenCL Private Memory Workitem 1 Compute Unit 1 Local Memory Private Memory Workitem 1 Compute Device Global / Constant Memory Data Cache Global Memory Compute Device Memory Private Memory Workitem 1 Compute Unit N Local Memory Perhaad Mistry & Dana Schaa, Northeastern Univ Computer Architecture Research Lab, with Ben Gaster, AMD 2011 Private Memory Workitem 1 Comme pour AMD, un sous-ensemble de la mémoire est exposée à OpenCL La mémoire partagée configurable sert de mémoire locale pour chaque compute unit La mémoire privée utilise les registres 18

Cell SPE 0 SPE 1 SPE 2 SPE 3 Processeur de la PS3 1 processeurs PPC pour le host jusqu'à 8 processeurs «synergétiques» SPU LS LS = Local store per SPE of 256KB PPE SPU LS SPU LS 25 GBPS 25 GBPS 25 GBPS Element Interconnect ~ 200GBPS L1 and L2 Cache POWER PC SPU LS Memory & Interrupt Controller 25 GBPS 19

GPGPU en pratique Un usage efficace des GPUs, quels qu'ils soient, implique: la décomposition du problème en milliers de petites tâches indépendantes, de manière à pouvoir exploiter tout le matériel et minimiser la latence que ces tâches soit souvent les mêmes pour exploiter le modèle SIMD que les tâches soient surtout de nature arithmétique (éviter à tout prix la combinatoire) 20

Compilateur OpenCL OpenCL utilise la technologie de compilateur LLVM («low level virtual machine») pour compiler les noyaux (et pour GPU et pour CPU) Processus bourré d optimisations, différent selon le hardware OpenCL Compute Program LLVM IR LLVM Front-end Nvidia PTX AMD CAL IL x86 21

Plan Architecture des GPUs ATI/AMD NVIDIA Cell OpenCL la plateforme les principales fonctions de l'api les étapes d'un programme la mémoire l'ordonnancement des fils d'exécution CUDA 22

Architecture de OpenCL OpenCL est un environnement pour la programmation parallèle dans un contexte hétérogène CPU + GPU(s) Articulé autour de quatre modèles: plateforme exécution mémoire programmation 23

Plateformes Chaque implantation OpenCL définit la plateforme qui permet à l'hôte (CPU) d'interagir avec le ou les «devices» (GPUs) plusieurs plateformes différentes peuvent coexister un hôte + une ou plusieurs plateformes ex. : une implémentation d OpenCL fournie par NVidia pour parler à votre carte graphique, une autre par Intel pour le CPU ou pour utiliser votre carte graphique on-board 24

Chaque périphérique (compute device) contient plusieurs unités de calcul Chaque unité de calcul contient plusieurs processeurs élémentaires Chaque processeur élémentaire peut faire plusieurs opérations par cycle 25

26

27

Points saillants API des plateformes (sur l hôte) Couche d abstraction pour les ressources Interroger, choisir et initialiser des périphériques Créer des contextes d exécution et des files de travail (workqueues) API runtime (sur l hôte) Configurer et lancer l exécution de noyaux Gérer l allocation et les transferts de mémoire Langage OpenCL Noyaux de calcul qui seront exécutés sur le périphérique Langage basé sur le C Peut être compilé en ligne par votre programme ou hors ligne 28

Structure de base d un programme OpenCL 1. Identifier la/les plateformes et périphériques 2. Créer des contextes et des files 3. Créer et compiler des noyaux 4. Créer des objets mémoire 5. Initialiser les objets mémoire, y placer des données 6. Lancer l exécution des noyaux 7. Attendre la fin de l exécution 8. Récupérer le résultat 9. Répéter 5-8 tant qu il reste des traitements 29

Sélection d'une plateforme Permet d'obtenir une liste de plateformes disponibles num_entries spécifie le nombre d'entrées disponibles dans le tableau platforms num_platforms retourne le nombre total de plateformes disponibles 30

Information à propos d'une plateforme Retourne de l'information spécifique à propos de la plateforme platform 31

Sélection d'un device Permet d'obtenir une liste de devices pour la plateforme platform 32

Information sur un device Retourne de l'information à propos du device device des tonnes d'information sont disponibles! surtout à propos de ses capacités sert à optimiser le code... 33

Création d'un contexte Permet de créer un contexte OpenCL pour un ou plusieurs devices Permet de gérer dynamiquement le «runtime» de OpenCL la fonction pfn_notify sera appelée par OpenCL en cas d'erreur pour ce contexte 34

Information sur le contexte Retourne de l'information concernant le contexte context 35

Création d'une file de commandes Permet de créer une file de commandes sert à enfiler des opérations à effectuer sur le GPU on peut créer plusieurs files de commande dans un même contexte chaque file sera indépendante; aucune synchronisation nécessaire 36

Information sur une file Retourne des information spécifiques sur la file de commandes command_queue 37

Création d'un buffer Permet de créer un buffer dans la mémoire du GPU pour transférer des données dans le contexte context le CPU est responsable d allouer la mémoire sur le GPU tous les transferts entre le CPU et le GPU doivent être explicites copie les données dans host_ptr en même temps 38

Lecture d'un buffer Insère une opération de lecture dans la file command_queue permet de transférer le contenu d'un buffer vers la mémoire du CPU encore une fois, opération initiée par le CPU 39

Écriture d'un buffer Insère une opération d'écriture dans la file command_queue permet de transférer de l'information depuis la mémoire du CPU vers un buffer du GPU 40

Copie d'un buffer Permet de copier le contenu d'un buffer vers un autre buffer 41

Création d'une image 2D Permet la création d'un objet image de dimension: image_width (pixels) image_height (pixels) image_row_pitch (octets) Format 3D également. 42

Images Types supportés : R, RG, RBG, RGBA, luminance, etc. 8 / 16 / 32 bits signés/non-signés, floats Interpolation linéaire, gestion des effets de bord Pourquoi? Accès mémoire accéléré par le hardware sur le GPU Permet réutilisation de features existants dans les GPUs pour les textures Désavantages L écriture est plus lente Limite au niveau des formats supportés 43

Formats d'image 44

45

Formats d'image supportés Permet d'obtenir la liste des formats d'image supportés pour une certaine plateforme 46

Les formats suivants sont toujours supportés: On peut lire, écrire et copier des images: clenqueuereadimage() clenqueuewriteimage() clenqueuecopyimage() 47

Création d'un programme Permet de créer un noyau qui s'exécutera sur le GPU à partir de code source les lignes du code sont contenues dans strings elles sont terminées par des zéros sinon les longueurs des lignes sont spécifiées par lengths 48

Compilation d'un programme Permet de compiler et de linker le code source d'un programme on peut spécifier plusieurs devices si non nul, la fonction pfn_notify est appelée pour signaler la fin de l'opération 49

Création d'un noyau Création d'un objet noyau à partir du programme program le programme doit avoir été compilé préalablement le noyau kernel_name doit avoir été déclaré dans le programme avec le qualificatif «kernel» c est l objet qu on va exécuter 50

Permet de créer tous les noyaux déclarés dans le programme program Permet de spécifier les arguments d'un noyau 51

Exécution d'un noyau Permet d'exécuter un noyau sur un device en utilisant work_dim dimensions le nombre total de «work-items» (le «local_work_size») doit être inférieur ou égal à CL_DEVICE_MAX_WORK_GROUP_SIZE le global_work_size doit être divisible par le local_work_size la commande attend la fin des événements de la event_wait_list 52

Exemple - vecadd Addition de deux vecteurs de valeurs flottantes 53

Cas à 2 dimensions indices locaux: sx et sy indices globaux: gx et gy 54

Modèle d'exécution L'exécution d'un noyau provoque la définition d'un espace d'indices NDRange = «N Dimensional Range» Une instance de noyau = 1 «work-item» correspond à 1 point dans l'espace d'exécution c'est la granularité la plus fine Les «work-items» sont organisés en groupes correspond à 1 point dans l'espace des «compute-units» tous les «work-items» d'un même groupe s'exécutent à l'intérieur du même «compute unit» c'est un deuxième niveau de granularité Les instances de «work-group» s'exécutent en parallèle sur les «compute-units» disponibles 55

Fonction diverses uint get_work_dim (): retourne le nombre de dimensions du NDRange size_t get_global_size (uint dimindx): retourne le nombre global de fils dans la dimension dimindx size_t get_global_id (uint dimindx): retourne l'indice global du fil d'exécution appelant, dans la dimension dimindx les gx et gy de tout à l heure size_t get_local_size (uint dimindx): retourne le nombre de fils par groupe dans la dimension dimindx 56

size_t get_local_id (uint dimindx): retourne l'indice local du fil d'exécution appelant, pour la dimension dimindx les sx et sy de tout à l heure size_t get_num_groups (uint dimindx): retourne le nombre de groupes dans la dimension dimindx size_t get_group_id (uint dimindx): retourne l'indice du groupe dans la dimension dimindx 57

Types de mémoire 58

59

Ce sont à peu près les mêmes étapes qui doivent être effectuées, peu importe l'application c'est donc ardu, mais pas nécessairement difficile OpenCL nous permet tout de même d'exploiter du matériel hétérogène à l'aide d'un même programme exécutable on peut aussi exploiter plusieurs GPUs sans changer le programme il faut donc accepter de faire un certain nombre d'initialisations Avec le wrapper C++ d OpenCL on réduit la quantité de poutine requise. 60

Restrictions Pour les noyaux: pas d'appels récursifs librairie de fonctions standards peut fonctionner avec CPUs Group_size: au moins la dimension d'un «warp» (NVIDIA) ou d'un «wavefront» (AMD) idéalement une puissance de 2 61

Plateformes de développement Apple: OpenCL est installé par défaut avec les outils de développement (Xcode) AMD: http://developer.amd.com/gpu/amdappsdk/ DOWNLOADS/Pages/default.aspx NVIDIA: http://developer.nvidia.com/object/opencldownload.html 62

Structure de base d un programme OpenCL 1. Initialiser plateformes, périphériques, contextes, files d opérations 2. Créer et compiler des noyaux 3. Créer des objets mémoire, les remplir (clenqueuewritebuffer) 4. Lancer l exécution des noyaux (clenqueuendrangekernel) 5. Attendre la fin de l exécution 6. Récupérer le résultat (clenqueuereadbuffer) 7. Répéter 3-6 tant qu il reste des traitements 63

64

Cas à 2 dimensions indices locaux: sx et sy indices globaux: gx et gy 65

Contrôle du flot dans un work-group Instruction SIMD division du programme en warps (nvidia) ou wavefronts (amd) exécution d une même instruction pour tous les processeurs du compute unit (appelé lockstep execution) Malgré le lockstep, chaque work-item peut prendre un chemin d exécution différent les instructions des deux chemins doivent être lancées pour tous les threads les processeurs qui n ont pas pris le branchement attendent Une façon optimale d exploiter les branchement et de les utiliser avec une granularité correspondant au work-groups 66

Définir la taille des workgroups global_size = global_size_0 * global_size_1 *... Le choix optimal des local_work_size dépendra de l architecture de la carte. le nombre de threads éxécutant les mêmes instructions en parallèle pour chaque Compute Unit est différent (32 pour nvidia, 64 pour ATI) cela dépend du nombre de coeurs dans chaque Compute Unit Utiliser un multiple de ce chiffre, sinon des coeurs seront inutilisés. Truc simple, puissance de 2 : nvidia : 32, 64, 128, 256, 512 amd : 64, 128, 256, 512 les local_size_i doivent être un multiple des global_size_i (possible de tricher) Tenir compte des besoins en mémoire (locale et privée) de vos work-groups, si ceux-ci sont gourmands, utiliser des valeurs plus petites 67

L occupation (occupancy) Les work-groups sont assignés aux compute units Lorsqu un work-group est assigné à un compute unit, il ne peut pas être swappé avant d avant terminé l exécution de ses threads S il y a suffisamment de ressources, plusieurs work-groups peuvent s exécuter sur un même compute unit en alternance permet de cacher la latence des accès mémoire Le terme occupation est utilisé pour décrire combien les ressources d un compute unit sont utilisées 68

L occupation - registres La disponibilité des registres et un facteur limitatif Le nombre maximum de registres demandés par votre noyau doit être disponible pour tous les threads du work-group ex: GPU avec 16384 registres par compute unit roulant un noyau qui nécessite 35 registres par thread Chaque compute unit peut exécuter combien de threads au maximum? Qu est-ce que ça implique pour la taille du work-group? 69

L occupation - autre facteurs Mémoire locale 32-64 KB pour un même compute unit tous les work-group doivent se partager cette mémoire Nb. max de threads Les cartes ont un nombre maximum de threads pouvant être actifs en même temps Varie selon la carte, selon le fabricant. NVidia : limite par Compute Unit. AMD : limite globale pour le GPU au complet + limite locale. 70

Autres outils pour noyaux Types vecteur : typen, où n est une puissance de 2 (e.g., float2, float4,...) n = 3 est supporté, aligné en mémoire comme n=4 Fonctions built-ins math pour valeurs continues ou discrètes sin, asin, log, exp, etc., pour tous les types incluant vecteurs fonctions spéciales pour entiers, supportant le bit-shifting, la troncation et bien d autres. Exemple : upsample(int hi, int lo) // retourne (hi << 8) lo fonctions géométriques: produit scalaire, produit vectoriel, norme de vecteurs, calcul de distances 71

Produit de matrices Problème à 2 dimensions Chaque fil permet de calculer un élément du résultat le global size correspond au nombre d'éléments dans la matrice 72

Solution séquentielle // A(NxP) * B(PxM) = C(NxM) // Iterate over the rows of Matrix A for(int n = 0; n < N; n++) { // Iterate over the columns of Matrix B for(int m = 0; m < M; m++) { C[n*M + m] = 0; // Multiply and accumulate the values in the current row // of A and column of B for(int p = 0; p < P; p++) { C[n*M + m] += A[n*P + p] * B[p*M + m]; } } } 73

kernel void mmul(const int M, const int N, const int P, global float* A, global float* B, global float* C) { int p; int col = get_global_id(0); int row = get_global_id(1); float tmp; if((row < N) && (col < M)) { tmp = 0.0; for(p=0; p<p; p++) tmp += A[row*P + p] * B[p*M + col]; } C[row*M+col] = tmp; } 74

Temps d exécution Multiplication de deux matrices 1000x1000 0.0435s pour le code GPU (GTX Nvidia 660 Ti) 2.59s pour une variante avec OMP (Intel Core i5, 4 coeurs sans hyperthreading) 7.20s pour un seul processeur (même CPU) Speedup GPU-séquentiel : 165 GPU-OpenMP : 60 Pour être équitable, il manquerait une comparaison avec opérations SSE 75

Problème? on accède plusieurs fois aux éléments des matrices qui sont stockées dans la mémoire globale la mémoire globale est lente surtout lorsqu'elle doit fournir beaucoup de fils d'exécution Solution? copier les matrices dans la mémoire locale chaque fil doit participer à la recopie compliqué, donc s assurer qu on a besoin d un telle optimisation 76

#define BLOCK_SIZE 8 kernel attribute ((reqd_work_group_size(block_size, BLOCK_SIZE, 1))) void floatmatrixmultlocals( global float * MResp, global float * M1, global float * M2, global int * q) { //Identification of this workgroup int i = get_group_id(0); int j = get_group_id(1); } //Identification of work-item int idx = get_local_id(0); int idy = get_local_id(1); //matrixes dimensions int p = get_global_size(0); int r = get_global_size(1); int qq = q[0]; //Number of submatrixes to be processed by each worker (Q dimension) int numsubmat = qq/block_size; float4 resp = (float4)(0,0,0,0); local float A[BLOCK_SIZE][BLOCK_SIZE]; local float B[BLOCK_SIZE][BLOCK_SIZE]; for (int k=0; k<numsubmat; k++) { //Copy submatrixes to local memory. Each worker copies one element //Notice that A[i,k] accesses elements starting from M[BLOCK_SIZE*i, BLOCK_SIZE*j] A[idX][idY] = M1[BLOCK_SIZE*i + idx + p*(block_size*k+idy)]; B[idX][idY] = M2[BLOCK_SIZE*k + idx + qq*(block_size*j+idy)]; barrier(clk_local_mem_fence); for (int k2 = 0; k2 < BLOCK_SIZE; k2+=4) { float4 temp1=(float4)(a[idx][k2], A[idX][k2+1], A[idX][k2+2], A[idX][k2+3]); float4 temp2=(float4)(b[k2][idy], B[k2+1][idY], B[k2+2][idY], B[k2+3][idY]); resp += temp1 * temp2; } barrier(clk_local_mem_fence); } MResp[BLOCK_SIZE*i + idx + p*(block_size*j+idy)] = resp.x+resp.y+resp.z+resp.w; 77

Mémoire locale Accessible pour tous les work-items d un work-group Définition dans le noyau directement, ou passé comme paramètre préfixe local Il faut synchroniser les accès pour s assurer que l écriture est bien acheminée (même si parfois la connaissance du hardware nous permettrait de sauter cette étape) Utilisation de barrières (local, global) 78

Voici un exemple de programme avec mémoire locale: kernel void localaccess( global float* A, global float* B, local float* C ) { local float alocalarray[1]; if( get_local_id(0) == 0 ) { alocalarray[0] = A[0]; } } C[get_local_id(0)] = A[get_global_id(0)]; barrier( CLK_LOCAL_MEM_FENCE ); float neighborsum = C[get_local_id(0)] + alocalarray[0]; if( get_local_id(0) > 0 ) neighborsum = neighborsum + C[get_local_id(0)-1]; B[get_global_id(0)] = neighborsum; Allocation du paramètre local err = clsetkernelarg(kernel_object, 3, sizeof(float)*len_c, NULL); 79

Accès à la mémoire Soit un tableau d'entiers x (32 bits) X: 0x00001232 Un fil veut accéder à l'élément x[0] int temp = x[0]; 0 1 2 3 4 5 6 7.. 0x00001232 0x00001236... 0x00001248.. 81

Problème... Les bus de mémoire sur les GPUs sont très larges par exemple 256 bits (32 octets) permet des bandes passantes plus élevées Les accès en mémoire doivent cependant être alignés avec la largeur du bus: Desired address: 0x00001232 Bus mask: 0xFFFFFFE0 Bus access: 0x00001220 Tous les accès de 0x00001220 à 0x0000123F produiront l'adresse 0x00001220 82

Histogramme Statistiques sur un ensemble de données Pratique pour faire des statistiques, mesures des quantiles, etc. 83

Histogramme Solution séquentielle : for (int i = 0; i < BIN_COUNT; i++) result[i] = 0; for (int i = 0; i < data.size(); i++) result[computebin(data[i])]++; Comment paralléliser? Est-ce qu on peut lancer un thread pour chaque point dans data? 84

Version naïve kernel void naive_histo( global int *d_bins, const int *d_in, const int BIN_COUNT) { int myid = get_global_id(0); int myitem = d_in[myid]; int mybin = myitem % BIN_COUNT; d_bins[mybins]++; } 85

Version naïve (fixed) kernel void naive_histo( global int *d_bins, const int *d_in, const int BIN_COUNT) { int myid = get_global_id(0); int myitem = d_in[myid]; int mybin = myitem % BIN_COUNT; atomic_inc(&d_bins[mybins]); } Fonctionne, mais sous-optimal à cause de l accès atomique Un seul thread incrémente un bin à la fois.. presque séquentiel pour un petit nombre de bins le GPU cache la latence en switchant à d autres blocs de threads pour calculer, mais pas grand chose à calculer dans ce cas-ci... Solution : calculer un histogramme local sur chaque work-group 86

Histogramme local Supposons que nos données sont une image : histogramme sur les couleurs 87

Histogramme local Opération de réduction requise à la fin réalisable en parallèle 88

Paramètres pour l exécution Nombre de work-groups : Choisir un nombre le plus petit possible Idéalement, nb work-groups = nb compute units Limite la complexité de l opération de réduction à la fin Nombre de work-items par work-group Le premier défini le second (ou vice versa) De ce côté, les critères définis précédemment sont encore bons Avoir 2-4 fois le nombre de threads habituellement dans un warp ou wavefront permet d utiliser le compute unit plus efficacement Compromis à faire 89

Optimisation des accès mémoire L opération histogramme ne dépend pas de l ordre des données Faire une seule lecture à la fois est inefficace (e.g., data[0]) Supposant un GPU AMD : lecture de 128 bit sur chaque work-item le hardware est optimisé pour faire la lecture de pixels à quatre composantes 32 bits, donne la meilleure performance lecture de vecteurs de taille 4 90

Accès mémoire Accès mémoires consécutifs pour work-items consécutifs : uint g_id = get_global_id(0); uint stride = get_global_size(0); for (i=0, idx=g_id; i<n4vectorsperworkitem; i++, idx += stride) { uint4 temp = Image[idx];... Chaque work-item lit ses pixels en série : uint g_id = get_global_id(0); for (i=0, idx=g_id*n4vectorsperworkitem; I<n4Vectors PerWorkItem; i++, idx++) { uint4 temp = Image[idx]; 91

Accès mémoire (illustré) 92

Remplissage des histogrammes locaux Encore une fois, opération atomiques (évite les courses critiques) uint4 px = read_imageui(img, flags, uint2(idx, y)); atom_inc(&tmp_histogram[px.x]); atom_inc(&tmp_histogram[256+px.y]); atom_inc(&tmp_histogram[512+px.z]); 93

Réduction Concatener les résultats : opération standard de réduction Définir pour un histogramme avec NUM_BINS * 3 valeurs (3 couleurs) il faut voir ces 3 histogrammes comme un long vecteur réduction (addition) sur global_work_size vecteurs de NUM_BINS*3 valeurs 94

Réduction simple Comment? Supposons une réduction simple (somme) pour un vecteur d entiers (plutôt qu un vecteur de vecteurs) Paramètres : {1, 2, 3, 4}, + Résultat : 1 + 2 + 3 + 4 = 10 En parallèle : (1 + 2) + (3 + 4) Pour de très grands vecteurs, possible de procéder en deux phases 95

Réduction d histogrammes Noyau exécutant la somme de tous les histogrammes partiels/locaux créés dans la première étape Un thread par bin (boîte), effectue la somme de tous les histogrammes locaux pour une seule tranche opération assez simple, car besoins différents... il s agit d un cas spécial de réduction. lorsqu on a plus de données, on va plutôt dans l autre sens imaginer un vecteur de 2^20 données 96

Résultat Histogramme pour image 1920x1080 Temps d exécution séquentiel ~ 17.3ms Temps d exécution parallèle ~ 0.453ms Possible d aller optimiser encore plus en utilisant d autres stratégies... Il y a des conflits dans la mémoire locale, au niveau des opérations atomiques Utiliser plusieurs histogrammes locaux par work-group, effectuer un reduce de plus (temps requis minimal) Dépend du nombre de bins encore une fois, plus il est petit, plus ça sera intéressant (car plus de conflits) 97

Analyser la performance Profilage de la performance avec les évènements Doit être activé préalablement dans la queue Permet de savoir quand une tâche (read, write, kernel) a été mise dans la file, lancée, terminée. Activation : queue = clcreatecommandqueue( context, device, CL_QUEUE_PROFILING_ENABLE, &err); Pour la suite, on passe un objet event à chaque appel de fonction : err = clenqueuendrangekernel(queue, kernel,..., &event); 98

Analyser la performance Pour récuperer l info : cl_int clgeteventprofilinginfo (cl_event event, "cl_profiling_info param_name, "size_t param_value_size, "void *param_value, "size_t *param_value_size_ret) enum cl_profiling_info: CL_PROFILING_COMMAND_QUEUED, CL_PROFILING_COMMAND_SUBMIT, CL_PROFILING_COMMAND_START, CL_PROFILING_COMMAND_END Retourne une valeur de temps absolue en ns 99

AMD Accelerated parallel processing profiler (APP) Deux modes d utilisation: plug-in pour Visual Studio 2008/2010 outil ligne de commande pour Windows/Linux Essentiellement, le plug-in lit la sortie de l outil ligne de commande et le dessine plus élégamment Permet de déterminer si votre application est limitée par l exécution du noyau ou bien les transferts de données 100

CHAPTER 12 OpenCL profiling and debugging APP (visualisation) 101FIGURE 12.1

Débugger votre code Il existe un debugger : gdebugger fonctionne pour OpenCL et OpenGL à vous de voir si vous voulez vous en servir Plus simple : extension AMD printf attention, un printf dans un noyau va imprimer pour chaque work_item ceci étant dit, on l active comme suit : #pragma OPENCL EXTENSION cl_amd_printf : enable 102

Autres façons de faire du GPGPU Utilisation de primitives OpenGL, du pipeline de rendu graphique il faut programmer des shaders en GLSL Exploiter les textures, les viewports Demande de réfléchir beaucoup avant de trouver une solution (ex. comment faire une FFT avec des triangles?) 103

Exemple : réduction avec OpenGL Stocker les données dans une texture 2D Effectuer le rendu d un carré sur lequel la texture est appliquée Aligner le carré avec la caméra Le carré doit faire le 1/4 de la taille de la texture, cela va forcer une interpolation Utiliser la sortie (le rendu) de ce carré comme entrée pour la passe suivante Répéter jusqu à la fin 104

Shader résultant uniform sampler2d u_data; in vec2 fs_texcoords; out float out_maxvalue; void main(void) { float v0 = texture(u_data, fs_texcoords).r; float v1 = textureoffset(u_data, fs_texcoords, ivec2(0, 1)).r; float v2 = textureoffset(u_data, fs_texcoords, ivec2(1, 0)).r; float v3 = textureoffset(u_data, fs_texcoords, ivec2(1, 1)).r; out_maxvalue = max(max(v0, v1), max(v2, v3)); } Intéressant, mais compliqué. Plus simple avec OpenCL 105

CUDA À peu de choses près, fonctionne de la même façon les GPU NVidia sont très similaires à ceux d AMD Un compilateur externe vient remplacer le compilateur c standard. nvcc Permet d avoir les noyaux dans le même fichier source que votre programme principal (le compilateur reconnaît les attributs et traite le code différemment) Ne fonctionne pas sur des CPUs on ne parle plus de calcul hétérogène, mais strictement de GPGPU (pour cartes NVidia). 106

Comparaison avec CUDA CUDA OpenCL 107

Programme CUDA (noyau) global void vectoradd(const float *A, const float *B, float *C, int numelements) { int i = blockdim.x * blockidx.x + threadidx.x; } if (i < numelements) { C[i] = A[i] + B[i]; } Différence dans les indices de thread 108

Programme CUDA (host) Fichier... 109

Programme CUDA Beaucoup moins de gestion requise dans le programme hôte On sait à quoi s attendre... le compilateur fait également une bonne partie du travail Remarquez que le noyau est pratiquement identique 110

Conclusion OpenCL permet d'exploiter la puissance de calcul des GPGPUs de manière indépendante de la plateforme définir le contexte (device + queue) définir le programme et les noyaux spécifier les paramètres des noyaux exécuter un NDRange (dimension du problème + dimension du groupe de travail Attention à la mémoire avec des centaines de fils d'exécution parallèles, la mémoire globale devient vite un goulot d'étranglement 111

Documents et information: http://www.khronos.org/opencl/ http://www.khronos.org/registry/cl/specs/ opencl-1.1.pdf http://www.khronos.org/registry/cl/specs/openclcplusplus-1.1.pdf http://www.khronos.org/files/opencl-1-1-quickreference-card.pdf Tutoriels: http://developer.amd.com/zones/openclzone/ universities/pages/default.aspx? cmpid=devbanner_univkit http://opencl.codeplex.com/wikipage?title=opencl %20Tutorials%20-%201 112