Architecture des GPU et principes de base de CUDA



Documents pareils
Introduction à CUDA.

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

Calcul multi GPU et optimisation combinatoire

Une bibliothèque de templates pour CUDA

Introduction à la Programmation Parallèle: MPI

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

Introduction à la programmation des GPUs

EPREUVE OPTIONNELLE d INFORMATIQUE CORRIGE

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

Windows Server Chapitre 1: Découvrir Windows Server 2008

INF6500 : Structures des ordinateurs. Sylvain Martel - INF6500 1

DE L ALGORITHME AU PROGRAMME INTRO AU LANGAGE C 51

Conventions d écriture et outils de mise au point

Architecture des ordinateurs

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

Programmation C. Apprendre à développer des programmes simples dans le langage C

Processus! programme. DIMA, Systèmes Centralisés (Ph. Mauran) " Processus = suite d'actions = suite d'états obtenus = trace

Mise en oeuvre TSM 6.1

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

Algorithmique et Programmation, IMA

Architecture des calculateurs

Initiation au HPC - Généralités

Plan du cours Cours théoriques. 29 septembre 2014

Techniques de stockage. Techniques de stockage, P. Rigaux p.1/43

Info0101 Intro. à l'algorithmique et à la programmation. Cours 3. Le langage Java

Cours d initiation à la programmation en C++ Johann Cuenin

as Architecture des Systèmes d Information

La technologie Java Card TM

Introduction aux SGBDR

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

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

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

INTRODUCTION A JAVA. Fichier en langage machine Exécutable

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

Architecture des ordinateurs

INITIATION AU LANGAGE C SUR PIC DE MICROSHIP

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

Machines virtuelles Cours 1 : Introduction

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

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

3A-IIC - Parallélisme & Grid GRID : Définitions. GRID : Définitions. Stéphane Vialle. Stephane.Vialle@supelec.fr

Introduction à MATLAB R

Plan global Outils de développement et compilation. Plan. Objectifs des outils présentés. IDE, GCC/Clang, ASAN, perf, valgrind, GDB.

ISC Système d Information Architecture et Administration d un SGBD Compléments SQL

INTERSYSTEMS CACHÉ COMME ALTERNATIVE AUX BASES DE DONNÉES RÉSIDENTES EN MÉMOIRE

Institut Supérieure Aux Etudes Technologiques De Nabeul. Département Informatique

Bases de programmation. Cours 5. Structurer les données

6 - Le système de gestion de fichiers F. Boyer, UJF-Laboratoire Lig, Fabienne.Boyer@imag.fr

Algorithmique I. Algorithmique I p.1/??

GESTION DE LA MEMOIRE

Rappels d architecture

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

Chapitre 4 : Les mémoires

Sur un ordinateur portable ou un All-in-One tactile, la plupart des éléments mentionnés précédemment sont regroupés. 10) 11)

Seance 2: En respectant la méthode de programmation par contrat, implémentez les autres fonctions de jeu.

SHERLOCK 7. Version du 01/09/09 JAVASCRIPT 1.5

NFP 121. Java et les Threads. Présentation : Thierry Escalarasse Mai 2007

Introduction au calcul parallèle avec OpenCL

Info0804. Cours 6. Optimisation combinatoire : Applications et compléments

Introduction au langage C

Contexte et motivations Les techniques envisagées Evolution des processus Conclusion

IN Cours 1. 1 Informatique, calculateurs. 2 Un premier programme en C

Tests de performance du matériel

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

Leçon 1 : Les principaux composants d un ordinateur

Cours 1 : La compilation

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

On distingue deux grandes catégories de mémoires : mémoire centrale (appelée également mémoire interne)

Java - la plateforme

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

Gestion de mémoire secondaire F. Boyer, Laboratoire Sardes

Architecture des Ordinateurs. Partie II:

Gestion mémoire et Représentation intermédiaire

Partie 7 : Gestion de la mémoire

Cours 3 : L'ordinateur

Introduction à Java. Matthieu Herrb CNRS-LAAS. Mars

Conception de circuits numériques et architecture des ordinateurs

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

Ordinateurs, Structure et Applications

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

Cours de Systèmes d Exploitation

Compilation (INF 564)

J2SE Threads, 1ère partie Principe Cycle de vie Création Synchronisation

Systèmes d Exploitation - ENSIN6U3. Aix-Marseille Université

SGM. Master S.T.S. mention informatique, première année. Isabelle Puaut. Septembre Université de Rennes I - IRISA

Limitations of the Playstation 3 for High Performance Cluster Computing

Le langage C++ est un langage de programmation puissant, polyvalent, on serait presque tenté de dire universel, massivement utilisé dans l'industrie

Une méthode de conception de systèmes sur puce

Module.NET 3 Les Assemblys.NET

Programmation parallèle et distribuée

La carte à puce. Jean-Philippe Babau

TP 1. Prise en main du langage Python


Master première année. Mention : Statistiques et Traitement de Données «STD» Rapport de stage

Ordonnancement temps réel

Cours de Système : Gestion de Fichiers

Parallélisme et Répartition

Introduction aux Machines Virtuelles avec VMKit

Machines Virtuelles. et bazard autour. Rémi Forax

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

Transcription:

M2 Informatique/SSSR - ALA Architecture des GPU et principes de base de CUDA Stéphane Vialle Stephane.Vialle@centralesupelec.fr http://www.metz.supelec.fr/~vialle Architecture des GPU et principes de base de CUDA 1 Architecture d un GPU NVIDIA 2 Principes de compilation et d exécution en CUDA 3 Principes de programmation en CUDA 4 Prog. CUDA synchrone à base de registres 5 Démarche et contraintes de développement Architecture des GPU et principes de base de CUDA 1 Architecture d un GPU NVIDIA Vue d ensemble CPU-GPU Détails de l architecture d un GPU Architecture du GPU selon le langage Architecture d un GPU vue de CUDA 1

Architecture d un GPU nvidia Vue d ensemble CPU-GPU Le CPU utilise le GPU comme un coprocesseur scientifique pour certains calculs adaptés aux architectures SIMD. Le CPU et le GPU sont tous les deux des multi-cœurs et sont associés à des hiérarchies de mémoires : CPU multi-cœurs GPU à N multiprocesseurs Cœur 1 Cœur 2 Cœur 3 Cœur 4 Cache(s) interne Cache externe (parfois) PCI express RAM de la carte CPU Mémoire Unités du multipro SIMD #1 Mémoire du multipro. #1 Mémoire du multipro #1 Mémoires du multipro #1 Cache(s) interne RAM de la carte GPU Architecture d un GPU nvidia Détails de l architecture d un GPU Un GPU est un ensemble de N petites machines SIMD indépendantes et partageant une mémoire globale : N «multiprocesseurs» Un multiprocesseur est 1 petite machine SIMD avec : k «ALU» synchronisés (k = 32), 1 décodeur d instructions, 3 types de mémoires partagées entre toutes les ALUs, 32Kregistres distribués entre les ALUs (seront propres à chaque thread) CPU + RAM document nvidia Architecture d un GPU nvidia Détails de l architecture d un GPU Un GPU est un ensemble de N petites machines SIMD indépendantes Terminologie et partageant 1 : une mémoire globale : Multiprocesseur (machine SIMD) N «multiprocesseurs» Processeur = cœur? Un multiprocesseur est 1 petite machine SIMD avec : k «ALU» synchronisés (k = 32), 1 décodeur d instructions, 3 types de mémoires partagées entre toutes les ALUs, 32Kregistres distribués entre les ALUs (seront propres à chaque thread) CPU + RAM document nvidia 2

Architecture d un GPU nvidia Détails de l architecture d un GPU Un GPU est un ensemble de N petites machines SIMD indépendantes Terminologie et partageant 2 : une mémoire globale : Multiprocesseur = Cœur GPU? N «multiprocesseurs» Petite unité de calcul Un multiprocesseur est 1 petite machine SIMD avec : k «ALU» synchronisés (k = 32), 1 décodeur d instructions, 3 types de mémoires partagées entre toutes les ALUs, 32Kregistres distribués entre les ALUs (seront propres à chaque thread) CPU + RAM document nvidia Architecture d un GPU nvidia Détails de l architecture d un GPU Un GPU est un ensemble de N petites machines SIMD indépendantes Terminologie et partageant «S. Vialle une» : mémoire globale : Un multiprocesseur SIMD N «multiprocesseurs» Une ALU (ou «thread hardware») Un multiprocesseur et définition explicite est 1 petite d un machine «cœur SIMD» selon avec : le contexte! k «ALU» synchronisés (k = 32), 1 décodeur d instructions, 3 types de mémoires partagées entre toutes les ALUs, 32Kregistres distribués entre les ALUs (seront propres à chaque thread) CPU + RAM document nvidia Architecture d un GPU nvidia Architecture du GPU selon le langage Le langage de programmation utilisée redéfinit la perception de l architecture du GPU : Grosse mémoire de texture document nvidia Cg document nvidia hardware CUDA Machines virtuelles Perception dans le langage 3

Architecture d un GPU nvidia Architecture d un GPU vue de CUDA Une hiérarchie de mémoires de tailles et temps d accès très variables ngo (avec cache L1-L2) CPU + RAM 64Ko (avec cache RO) Large! (avec cache RO) Architecture d un GPU nvidia Architecture d un GPU vue de CUDA Une hiérarchie de mémoires de tailles et temps d accès très variables 16Ko à 48Ko F : 32Kreg (128Ko) K : 64Kreg (256Ko) ngo (avec cache L1-L2) CPU + RAM 64Ko (avec cache RO) Large! (avec cache RO) Architecture d un GPU nvidia Architecture d un GPU vue de CUDA Une hiérarchie de mémoires de tailles et temps d accès très variables Rapide et souple 0s Comparé à la vitesse de calcul du GPU Moyen-lent & contraint Très lent CPU + RAM ngo (avec cache L1-L2) 64Ko (avec cache RO) Large! (avec cache RO) 4

Architecture d un GPU nvidia Cache génériques des GPU NVIDIA Evolution des caches : de + en + de cache «générique» Avant Fermi Thread Sha red Me m Read- Only caches Read- Only caches D R A M Constant & Textures Constant & Textures Relâchement progressif des contraintes d accès à la RAM du GPU Architecture d un GPU nvidia Cache génériques des GPU NVIDIA 3 stratégies d accès en R/W S appuyer sur le schéma DRAM L1 L2 ASSEZ SIMPLE LENT-RAPIDE Utiliser la shared memory : écrire un algo. De cache dédié au problème et aux données COMPLEXE RAPIDE «faire le cache» Utiliser principalement les registres, puis la shared memory TRES COMPLEXE LE PLUS RAPIDE Architecture d un GPU nvidia Quelques valeurs (http://en.wikipedia.org/wiki/cuda) 5

Architecture des GPU et principes de base de CUDA 2 Principes de compilation et d exécution en CUDA Principes de compilation CUDA & C/C++ Principes d exécution de codes CUDA Compilation et exécution en CUDA Principes de compilation Compilation d applications CUDA entièrement développées en CUDA : Définitions de variables et fonctions avec «qualificateurs» CUDA Code C, ou C++ avec des appels à la lib CUDA Code C, ou C++ «standard» nvcc Fichiers xxx.cu et xxx.h, et xxx.cc incluant si besoin cuda.h et cuda_runtime.h Pour les codes C/C++ simples : Il est possible de simplement tout recompiler en nvcc dans des fichiers xxx.cu (et xxx.cc incluant cuda.h et cuda_runtime.h) Mais les optimisations sérielles peuvent en souffrir binaire Codes CPU et GPU intégrés Compilation et exécution en CUDA Principes de compilation Compilation d applications CUDA avec récupération de code C/C++ : Legacy code Fichiers nvcc -c xxx.cc et xxx.h Code CUDA Fichiers nvcc -c yyy.cu, yyy.cc et yyy.h Fichiers objets Edition de liens binaire Codes CPU et GPU intégrés Code C++ métier : fichiers.cc compilés en nvcc Code CUDA : fichiers.cu compilés en nvcc Edition de liens : des pbs peuvent encore apparaître avec des templates 6

Compilation et exécution en CUDA Principes de compilation Compilation d applications CUDA avec compilateur spécifique : Legacy code Fichiers xxx.cc et xxx.h g++ -c icc -c Code CUDA Fichiers nvcc -c yyy.cu, yyy.cc et yyy.h Edition de liens pbs possibles (g++ ok)!! Fichiers objets binaire Codes CPU et GPU intégrés Code C++ métier : fichiers.cc compilés en gcc/g++ ou en icc ou Code CUDA : fichiers.cu compilés en nvcc Edition de liens : des pbs de nommage peuvent apparaitre (mais pas en gcc) Exécution d applications CUDA : Compilation et exécution en CUDA Principes d exécution On lance un programme CPU d apparence classique. On réalise du «RPC» sur le GPU depuis le CPU : exécution de «kernels». Il faut minimiser les transferts de données (pour être efficace). On peut exécuter les «kernels» en mode bloquant (synchrone) ou nonbloquant (asynchrone) vis-à-vis du programme CPU : possibilité d utiliser simultanément le CPU et le GPU. CPU Transfert de données GPU Exécution de «kernels» Exécution de l application Transfert de résultats Utilisation du coprocesseur scientifique Architecture des GPU et principes de base de CUDA 3 Principes de programmation en CUDA Qualifiers de CUDA Transfert de données CPU-GPU Exécution de grilles de blocs de threads Granularité et nombre de threads Choix du type de données sur GPU 7

Principes de programmation en CUDA 3.1 «Qualifiers» de CUDA Fonctionnement des «qualifiers» de CUDA : Fonctions Variables device Appel sur GPU Exec sur GPU device Mémoire globale GPU Durée de vie de l application Accessible par les codes GPU et CPU host (default) Appel sur CPU Exec sur CPU constant Mémoire constante GPU Durée de vie de l application Ecrit par code CPU, lu par code GPU global Appel sur CPU Exec sur GPU shared Mémoire partagée d un multiprocesseur Durée de vie du block de threads Accessible par le code GPU, sert à cacher la mémoire globale GPU Les «qualifiers» différencient les parties de code GPU et CPU. Principes de programmation en CUDA 3.2 - Transfert de données CPU-GPU Variables allouées sur GPU à la compilation («symboles») : float TabCPU[N]; device float TabGPU[N]; // Array on CPU // Array on GPU (symbol) Transfert de données du CPU vers un «symbole» stocké sur GPU : // Copy all TabCPU array into TabGPU array cudamemcpytosymbol(tabgpu,&tabcpu[0], sizeof(float)*n,0, cudamemcpyhosttodevice); // Copy 2 nd half of TabCPU array into 2 nd half TabGPU array cudamemcpytosymbol(tabgpu,&tabcpu[n/2], sizeof(float)*n/2,sizeof(float)*n/2, cudamemcpyhosttodevice); Transfert de données d un «symbole» stocké sur GPU vers le CPU : // Copy all TabGPU array into TabCPU array cudamemcpyfromsymbol(&tabcpu[0],tabgpu, sizeof(float)*n,0, cudamemcpydevicetohost); Principes de programmation en CUDA 3.2 - Transfert de données CPU-GPU Variables allouées sur GPU à l exécution : float *TabCPU; // Dynamic array on CPU float *TabGPU; // Dynamic array on GPU cudaerror_t cudastat; // Result of op on dynamic CUDA vars. // Allocation of the dynamic arrays from the CPU TabCPU = (float *) malloc(n*sizeof(float)); cudastat = cudamalloc((void **) &TabGPU, N*sizeof(float)); Copie de variables dynamiques (CPU GPU) : // Copy TabCPU dynamic array into TabGPU dynamic array cudastat = cudamemcpy(tabgpu, TabCPU, sizeof(float)*n, cudamemcpyhosttodevice); Copie de variables dynamiques (GPU CPU) : // Copy TabCPU dynamic array into TabGPU dynamic array cudastat = cudamemcpy(tabcpu, TabGPU, sizeof(float)*n, cudamemcpydevicetohost); Libération des allocations dynamiques free(tabcpu); cudastat = cudafree(tabgpu); 8

Principes de programmation en CUDA 3.2 - Variables statiques ou dynamiques? Avant cuda 5.0 Les variables définies sur le GPU à la compilation : device float TabGPU[N]; ne peuvent pas être qualifiées extern! gestion plus contrainte et complexe du jeu de fichiers sources. on termine avec un gros fichier source contenant tout le code CUDA! Les routines du GPU (les kernels) ne connaissent pas les pointeurs de leurs zones mémoires dynamiques déclarées et allouées par le CPU : float *TabGPU; cudastat = cudamalloc((void **) &TabGPU, N*sizeof(float)); Ces pointeurs doivent être passés aux kernels en paramètres : Func<<< Dg,Db >>>(TabGPU, ); programmation plus verbeuse et complexe des appels de kernels mais on peut gérer convenablement son code source. Principes de programmation en CUDA 3.2 - Variables statiques ou dynamiques? A partir de cuda 5.0 Les variables définies sur le GPU à la compilation : device float TabGPU[N]; peuvent être qualifiées extern (ou static) et permettent un développement en plusieurs fichiers sources Mais seulement en activant le mode de compilation séparé (par défaut on reste dans le mode de compilation globale de cuda 4) Compilation : nvcc --relocatable-device-code=true Ou bien : nvcc rdc=true Edition de liens : nvcc --device-link Ou bien : nvcc dlink Voir le document «NVIDIA CUDA Compiler Driver NVCC» Principes de programmation en CUDA 3.3 - Exec. de grilles de blocs de threads Le programme CPU demande l exécution d un ensemble de threads (des «gpu threads») : - threads identiques, - threads organisés en blocs, chaque bloc s exécutant sur un seul multiprocesseur, - blocs organisés au sein d une grille, qui répartit ses blocs sur tous les multiprocesseurs. 9

Principes de programmation en CUDA 3.3 - Exec. de grilles de blocs de threads Syntaxe d appel (d exécution) de threads sur le GPU : Prototype des kernels GPU : // Prototype of kernel function (to run on GPU) global void Func(float* parameter,, ); Exécution des kernels GPU depuis le CPU : // Execution of a GPU kernel from a CPU routine: Func<<< Dg, Db, Ns, S >>>(parameter,, ); // Usually only 2 arguments are specified: Func<<< Dg, Db >>>(parameter,, ); Nombre de GPU threads par bloc 3D i.e.: définition d un bloc 3D de threads Nombre de blocs par grille 3D i.e.: définition d une grille 3D de blocs 3D de threads Principes de programmation en CUDA 3.3 - Exec. de grilles de blocs de threads Définition de blocs de threads et de grilles de blocs : // Classical call from a CPU routine Func<<< Dg, Db >>>(parameter); Dg et Db sont des structures de 3 int (des dim3), à définir avant l appel du kernel. Définition des blocs (sur FERMI) : - Barres, matrices, ou cubes de threads. - Db.x 1024, Db.y 1024, Db.z 64 - Nbr total de threads / bloc 1024 Définition des grilles (sur FERMI) : - Barres, matrices, ou cubes de blocs. - Dg.x 65535, Dg.y 65535, Dg.z 65535 // GPU thread management dim3 Dg, Db; // Block of 128x4 threads Db.x = 128; Db.y = 4; Db.z = 1; // Grid of 512 blocks Dg.x = 512; Dg.y = 1; Dg.z = 1; // Total of = 262144 // threads executed Principes de programmation en CUDA 3.4 - Granularité et nombre de threads Les GPUs ont été conçues pour : switcher d un contexte de thread à un autre TRES rapidement, masquer la latence des accès mémoires par multi-threading, être efficace même sur de petits threads, disposer d une mémoire limitée pour les threads d un même bloc. Ne pas hésiter à créer un grand nombre de petits threads GPU par bloc et un grand nombre de blocs. Exemple : pour traiter une table de N éléments vs Threads traitant chacun UN élément Grille de blocs de N threads au total Threads traitant n éléments chacun Grille de blocs de N/n threads au total Solution à privilégier si les traitements ne sont pas «trop petits» 10

Principes de programmation en CUDA 3.4 - Granularité et nombre de threads Combien de threads/bloc et de blocs/grille? Les threads sont activés par «warp» de 32 (consécutifs en x ) 1 bloc = p 32 threads (sinon gaspillage sur le dernier warp) Beaucoup de threads/bloc permet de masquer les temps d accès Mais maximum de 1024 threads/bloc Mais maximum de 32K/64K registres par multiprocesseur Activation simultanée max de 8/16 blocs par multiprocesseur Et activation simultanée max de 48/64 warps par multiprocesseur Et activation simultanée max de 1536/2048 threads par multipro.. Quelle sera la meilleure répartition des threads en blocs et grille? En général il faut au moins 256 threads/bloc Ensuite difficile de prédire l impact du tuning! Architecture des GPU et principes de base de CUDA 4 Prog. CUDA synchrone à base de registres Kernel traitant 1 donnée par thread Kernel traitant n données par thread Principe d accès coalescent aux données Prog. CUDA synchrone à base de registres 4.1 Kernel traitant 1 donnée/thread (1) Kernel utilisant la mémoire globale et des registres Une barre de threads par bloc, et une barre de blocs par grille (un choix). Un thread traite une seule donnée. Db = {BLOCK_SIZE_X,1,1} Hyp : Nd = k. BLOCK_SIZE_X Dg = {Nd/BLOCK_SIZE_X,1,1} global void f1(void) { int idx; // Registers: float data; // 16 Kreg per float res; // multipro. // Compute data idx of the thread idx = threadidx.x + blockidx.x*block_size_x; // Read data from the global mem data = InGPU[idx]; // Compute result res = (data + 1.0f)*data ; // Write result in the global mem OutGPU[idx] = res; } InGPU[Nd]; OutGPU[Nd]; 11

Prog. CUDA synchrone à base de registres 4.1 Kernel traitant 1 donnée/thread (2) Kernel utilisant la mémoire globale et des registres Une barre de threads par bloc, et une barre de blocs par grille (un choix). Un thread traite une seule donnée. Hyp : Nd k. BLOCK_SIZE_X Db = {BLOCK_SIZE_X,1,1} global void f1(void) if (Nd%BLOCK_SIZE_X == 0) { Dg = {Nd/BLOCK_SIZE_X,1,1} int idx; // Registers: float data; // 16Kreg per else float res; // multipro. Dg = {Nd/BLOCK_SIZE_X + 1,1,1} // Compute data idx of the thread idx = threadidx.x + blockidx.x*block_size_x; // If the elt indexed exists: if (idx < Nd) { // Read data from the global mem data = InGPU[idx]; // Compute result res = (data + 1.0f)*data ; // Write result in the global mem OutGPU[idx] = res; } } Démarche classique : Les threads «en trop» ne font rien Prog. CUDA synchrone à base de registres 4.2 Kernel traitant n données/thread (1) Kernel utilisant la mémoire globale et des registres Une barre de threads par bloc, et une barre de blocs par grille (un choix) Un thread traite n données. Hyp : Nd = k*(block_size_x*n) global void f1(void) { int offset = 0; Db = {BLOCK_SIZE_X,1,1} Dg = {Nd/(BLOCK_SIZE_X*n),1,1} // Registers float data = 0.0f, res = 0.0f; // Compute intitial data idx of the thread offset = threadidx.x + blockidx.x*(block_size_x*n); // Loop with contiguous accesses to data tables for(int i = offset; i < offset + BLOCK_SIZE_X*n; i += BLOCK_SIZE_X) { // - Read one value from the global memory data = InGPU[i]; // - Compute one result res = (data + 1.0f)*data ; // - Write one result in the global memory OutGPU[i] = res; } } Stratégie d accès alignés aux données Prog. CUDA synchrone à base de registres 4.2 Kernel traitant n données/thread (2) Kernel utilisant la mémoire globale et des registres Une barre de threads par bloc, et une barre de blocs par grille (un choix) Un thread traite n données. Hyp : Nd k*(block_size_x*n) global void f1(void) { int offset = 0; Db = {BLOCK_SIZE_X,1,1} if (Nd%(BLOCK_SIZE_X*n) == 0) Dg = {Nd/(BLOCK_SIZE_X*n),1,1} else // Registers Dg = {Nd/(BLOCK_SIZE_X*n) + 1,1,1} float data = 0.0f, res = 0.0f; // Compute intitial data idx of the thread offset = threadidx.x + blockidx.x*(block_size_x*n); // Loop with contiguous accesses to data tables for(int i = offset; i < offset + BLOCK_SIZE_X*n && i < Nd; i += BLOCK_SIZE_X) { // - Read one value from the global memory data = InGPU[i]; // - Compute one result res = (data + 1.0f)*data ; // - Write one result in the global memory OutGPU[i] = res; } } Stratégie d accès alignés aux données 12

Prog. CUDA synchrone à base de registres 4.3 - Principe d accès coalescent aux données Accès contigus aux données depuis un bloc de threads : BLOCK_SIZE_X threads Un multiprocesseur SIMD Tableau de Nd éléments Mémoire globale du GPU Step 1: BLOCK_SIZE_X accès contigus et synchronisés (rappel : architecture SIMD) Prog. CUDA synchrone à base de registres 4.3 - Principe d accès coalescent aux données Accès contigus aux données depuis un bloc de threads : BLOCK_SIZE_X threads Un multiprocesseur SIMD Tableau de Nd éléments Mémoire globale du GPU Step 2: BLOCK_SIZE_X accès contigus et synchronisés (rappel : architecture SIMD) Prog. CUDA synchrone à base de registres 4.3 - Principe d accès coalescent aux données Accès contigus aux données depuis un bloc de threads : BLOCK_SIZE_X threads Un multiprocesseur SIMD Tableau de Nd éléments Mémoire globale du GPU BLOCK_SIZE_X * n elts. Step 3: BLOCK_SIZE_X accès contigus et synchronisés (rappel : architecture SIMD) 13

Prog. CUDA synchrone à base de registres 4.3 - Principe d accès coalescent aux données Accès contigus aux données depuis un bloc de threads : offset = threadidx.x + blockidx.x*(block_size_x*n); Un BLOCK_SIZE_X for(int i threads = offset; i < offset + n*block_size_x multiprocesseur && i < Nd; i += BLOCK_SIZE_X) { SIMD data = InGPU[i]; res = (data + 1.0f)*data ; OutGPU[i] = res; } Tableau de Nd éléments Mémoire globale du GPU BLOCK_SIZE_X * n elts. Mécanisme hardware similaire à celui d une CPU et de son cache. Step 3: Mais démarche de programmation opposé à celle BLOCK_SIZE_X accès contigus et synchronisés (rappel des : architecture threads CPU SIMD) sur multi-cœurs : ici la boucle interne est faite par des threads synchronisés. Prog. CUDA synchrone à base de registres 4.3 Principe d accès coalescent aux données Principe initial : Les threads sont activés par «warp» de 32 consécutifs dans la dimension «x» de leur 3D-bloc Ils doivent accéder à des données consécutives en mémoire Le premier thread doit accéder à une donnée alignée avec un multiple de 32 mots mémoires de 4 octets. Le warp récupère alors ses 32 données de 4 octets en «une fois» Il récupère 128 octets en parfaite coalescence Un warp (32 threads) Un tableau de float Adresse de départ (ex : 128) alignée avec 32x4 octets Lecture de 32x4 = 128 octets consécutifs en une fois Prog. CUDA synchrone à base de registres 4.3 Principe d accès coalescent aux données Ajout des caches génériques dans le mécanisme : Thread L1 L2 DRAM 128o 32o Accès à la mémoire globale : Fermi : accès par L1 et L2 (défaut), et par L2 seul si spécifié accès par bloc de 128o accès par bloc de 32o Kepler : accès par L2 seul (L1 utilisé dans les accès locaux ) accès par blocs de 32o Accès en : 1x128o, ou 4x32o 14

Prog. CUDA synchrone à base de registres 4.3 Principe d accès coalescent aux données Impact du «désalignement» : Hypothèse : les 32 threads d un warp accèdent à 32 données consécutive, MAIS la première adresse n est pas un multiple de 32x4o = 128o. Si le cache L1 est actif dans la lecture de la mémoire globale Alors il y aura lecture de DEUX segments de 128 octets (au lieu d un) Un warp (32 threads) Un tableau de float Accès en : 2x128o Prog. CUDA synchrone à base de registres 4.3 Principe d accès coalescent aux données Impact du «désalignement» : Hypothèse : les 32 threads d un warp accèdent à 32 données consécutive, MAIS la première adresse n est pas un multiple de 32x4o = 128o. Si le cache L2 est le seul actif dans la lecture de la mémoire globale Alors il y aura lecture de CINQ segments de 32 octets (au lieur de 4). Accès en : 5x32o Dans tous les cas : c est plus lent! On extrait trop de données. Un warp (32 threads) Un tableau de float Prog. CUDA synchrone à base de registres 4.3 Principe d accès coalescent aux données Impact d un «stride» (non unitaire) : Stride de 2 : le thread i accède à Tab[offset+ (2*i)] Rmq : on suppose le point de départ aligné (offset = k x (32x4)) Un tableau de float Un warp (32 threads) On va extraire plus que le nécessaire : plus que 1x128o ou 4x32o! Ce sera plus lent! 15

Prog. CUDA synchrone à base de registres 4.3 Principe d accès coalescent aux données Impact d un «stride» (non unitaire) : Dégradation de bande-passante applicative observée par NVIDIA Un stride dans les accès à la mémoire globale se paie très vite très fort Prog. CUDA synchrone à base de registres 4.3 Principe d accès coalescent aux données Soigner la coalescence dès la conception de l algorithme : Les dégradations étaient bien pire avec les premières architectures GPU NVIDIA! La coalescence reste le premier «souci» de développement en CUDA Les caches améliorent les performances mais ne masquent pas les défauts de coalescences Il faut soigner la coalescence dès la conception de l algorithme Il faut concevoir un ensemble stockage des données et accès qui vérifie la coalescence Utiliser la shared memory peut aider (voir plus loin), car on écrit un algorithme de cache dédié au problème. Architecture des GPU et principes de base de CUDA 5 Démarche et contraintes de développement 16

Démarche et contraintes de développement Développement pas à pas Développer et tester step by step! Certaines erreurs ne sont pas détectées à la compilation. Certains messages d erreurs à l exécution sont approximatifs. Certaines erreurs d exécution sont «fatales» au driver et peuvent mener à rebooter le PC (très rare) Il existe des debugers sous Windows, mais le debug sur GPU reste complexe Certaines erreurs ne provoque pas d arrêt du programme, seulement des résultats «légèrement» faux! Développer, tester et valider successivement chaque kernel. Comparer les résultats sur GPU et sur CPU pour valider les kernels. En général, on ne peut pas développer un code GPU sans disposer ou développer le code CPU correspondant (pour comparer les résultats) Démarche et contraintes de développement Utilisation de «templates» Comme dans tous les compilateurs C++ les templates peuvent poser pb : On peut écrire des «kernels templates». Exemple : template < int size > global fk(...) { if (size > 512) { } if (size > 256) { } } fk<data_size><<<dg,db>>>( ) Le compilateur élimine les lignes de code inutiles. Permet de spécialiser un kernel pour un problème donnée, et de gagner en efficacité. Mais les compilateurs C++ sont parfois en désaccord sur des codes templates complexes : l optimisation par template peut mener à des pbs de portabilités (nvcc est toutefois de plus en plus proche de g++) Démarche et contraintes de développement Pb d égalité des résultats avec le CPU Initialement : Des opérations sur les float étaient légèrement fausses sur GPU Les double n étaient pas disponibles sur GPU afin de privilégier la vitesse d exécution, l erreur maximale était de toute façon bornée. Certains algorithmes trop sensibles divergeaient quand même. Il fallait choisir des algorithmes adaptés aux problèmes d arrondi. Depuis CUDA 3.2 et +, sur FERMI : Les double sont disponibles et bien supportés La norme IEEE754 est respectée par les GPUs. Mais selon l ordre des opérations les résultats peuvent varier (classique) Donc les calculs en parallèle du GPU peuvent différer de ceux du CPU Difficile de savoir si on est face à une erreur de développement ou à une simple conséquence du parallélisme! 17

Démarche et contraintes de développement Précision vs Vitesse des calculs En CUDA 3.2 (ou plus) et sur FERMI : Si on veut se limiter à des float (précision suffisante pour le pb) Alors préciser aussi que les constantes sont des float : x = y*4.0f Si on veut aller encore plus vite on peut positionner certains flags de compilation pour profiter de la vitesse de traitement des float avec une précision limitée Ex : -arch=sm_20 -ftz=true -prec-sqrt=false -prec-div=false On peut aussi forcer l utilisation de la bibliothèque «fast math» pour accélérer les calculs si la précision n est pas critique : Compilation avec --use_fast_math Attention : ces «tunings» évoluent vite avec les versions de nvcc et de l architecture GPU. Architecture des GPU et principes de base de CUDA TP CUDA 1 TP CUDA 1 : global memory & registers Blocs 1D de threads (kernel k0) MatrixSide = k.blocksize_x B Implanter des accès coalescent à la mémoire globale A C Pavage 1D de la matrice C Sous-matrice calculée par un bloc 1D de threads 18

TP CUDA 1 : global memory & registers Blocs 1D de threads (kernel k0) MatrixSide k.blocksize_x B Implanter des accès coalescent à la mémoire globale A C Pavage 1D de la matrice C Sous-matrice calculée par un bloc 1D de threads TP CUDA 1 : global memory & registers Blocs 2D de threads (kernel k1) MatrixSide = k1.blocksize_x = k2.blocksize_y B Implanter des accès coalescent à la mémoire globale A C Pavage 2D de la matrice C Sous-matrice calculée par un bloc 2D de threads TP CUDA 1 : global memory & registers Blocs 2D de threads (kernel k1) MatrixSide k1.blocksize_x k2.blocksize_y B Implanter des accès coalescent à la mémoire globale A C Pavage 2D de la matrice C Sous-matrice calculée par un bloc 2D de threads 19

Architecture des GPU et principes de base de CUDA Fin 20