Calcul Québec - Université Laval. Atelier CUDA/GPU



Documents pareils
Introduction à CUDA.

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

Introduction à la programmation des GPUs

Calcul multi GPU et optimisation combinatoire

Une bibliothèque de templates pour CUDA

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

Introduction au calcul parallèle avec OpenCL

Conventions d écriture et outils de mise au point

DE L ALGORITHME AU PROGRAMME INTRO AU LANGAGE C 51

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

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

Initiation au HPC - Généralités

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

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

Cours d Algorithmique-Programmation 2 e partie (IAP2): programmation 24 octobre 2007impérative 1 / 44 et. structures de données simples

EPREUVE OPTIONNELLE d INFORMATIQUE CORRIGE

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

Plan du cours Cours théoriques. 29 septembre 2014

INITIATION AU LANGAGE C SUR PIC DE MICROSHIP

Introduction à la Programmation Parallèle: MPI

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

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

I. Introduction aux fonctions : les fonctions standards

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

Programmation système I Les entrées/sorties

Cours Programmation Système

Optimisations des SGBDR. Étude de cas : MySQL

Architecture des ordinateurs

Introduction à MATLAB R

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

1/24. I passer d un problème exprimé en français à la réalisation d un. I expressions arithmétiques. I structures de contrôle (tests, boucles)

Le prototype de la fonction main()

Introduction à la programmation orientée objet, illustrée par le langage C++ Patrick Cégielski

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

Exécution des instructions machine

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

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

Initiation. àl algorithmique et à la programmation. en C

1.6- Génération de nombres aléatoires

Rappels Entrées -Sorties

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

INF6500 : Structures des ordinateurs. Sylvain Martel - INF6500 1

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

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

Cours Informatique Master STEP

Langage Java. Classe de première SI

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

Journée Utiliateurs Nouvelles du Pôle ID (Informatique) Pierre Neyron, LIG/CNRS

Éléments d informatique Cours 3 La programmation structurée en langage C L instruction de contrôle if

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

Cours d Algorithmique et de Langage C v 3.0

École Polytechnique de Montréal. Département de Génie Informatique et Génie Logiciel. Cours INF2610. Contrôle périodique.

1 Mesure de la performance d un système temps réel : la gigue

Introduction au langage C

Le Projet BINSEC. Automatiser l analyse de sécurité au niveau binaire. Airbus group, CEA, IRISA, LORIA, Uni. Joseph Fourier. p.

UE C avancé cours 1: introduction et révisions

IFT Systèmes d exploitation - TP n 1-20%

Dans le chapitre 1, nous associions aux fichiers ouverts des descripteurs de fichiers par lesquels nous accédions aux fichiers.

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

Outils pour la pratique

Cours 7 : Utilisation de modules sous python

OS Réseaux et Programmation Système - C5

Algorithmique et Programmation, IMA

Les structures de données. Rajae El Ouazzani

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

INTRODUCTION A JAVA. Fichier en langage machine Exécutable

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

IRL : Simulation distribuée pour les systèmes embarqués

TP : Gestion d une image au format PGM

Concept de machine virtuelle

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

Windows Server Chapitre 1: Découvrir Windows Server 2008

SYSTÈME DE GESTION DE FICHIERS

Machines Virtuelles. et bazard autour. Rémi Forax

Guide d'installation et de configuration de Pervasive.SQL 7 dans un environnement réseau Microsoft Windows NT

Parallélisme et Répartition

Introduction à Java. Matthieu Herrb CNRS-LAAS. Mars

Tests de performance du matériel

1. Systèmes d entrée/sortie 2. Systèmes de fichiers 3. Structure de mémoire de masse (disques)

3IS - Système d'exploitation linux - Programmation système

Remote Method Invocation Les classes implémentant Serializable

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

Python - introduction à la programmation et calcul scientifique

SYSTÈME DE GESTION DE FICHIERS SGF - DISQUE

INF111. Initiation à la programmation impérative en C amini/cours/l1/inf111/ Massih-Reza Amini

Cours de Programmation Impérative: Zones de mémoires et pointeurs

as Architecture des Systèmes d Information

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

Cours de C. Petits secrets du C & programmation avancée. Sébastien Paumier

Prénom : Matricule : Sigle et titre du cours Groupe Trimestre INF1101 Algorithmes et structures de données Tous H2004. Loc Jeudi 29/4/2004

OpenPaaS Le réseau social d'entreprise

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

Architecture des calculateurs

1. Structure d'un programme FORTRAN 95

SRS DAY: Problématique liée à la virtualisation

Informatique pour scientifiques hiver Plan général Systèmes d exploitation

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

Gestion mémoire et Représentation intermédiaire

Grandes lignes ASTRÉE. Logiciels critiques. Outils de certification classiques. Inspection manuelle. Definition. Test

Transcription:

Atelier CUDA/GPU Maxime Boissonneault <maxime.boissonneault@calculquebec.ca> Université Laval - Octobre 2014 Adaptation du "CUDA/GPU Workshop", par Dan Mazur <dan.mazur@calculquebec.ca>, Université McGill 1

2 Prérequis Connaissance de base d'un système Linux Connexion à distance avec SSH Éditeur texte (VIM, emacs, nano) Bonne connaissance du langage C Syntaxe Compilateur GCC Familiarité avec les fils d'exécutions (threads)

3 Postes fixes Windows/Linux Pour se connecter sur les postes Nom d utilisateur : formation Mot de passe : «Automne14»

4 Plan Vue d'ensemble du GPGPU Terminologie et architecture Connexion et préparation de l'environnement Introduction à CUDA-C Exemples Mémoire globale Blocs et fils d'exécutions Syntaxe courante Gestion des erreurs Mémoire partagée et synchronisation Mémoire unifiée (Cuda 6) Multi-GPU

V1.0 Vue d'ensemble du GPGPU 5

6 Qu'est-ce qu'un GPU Dispositif servant à résoudre des calculs coûteux en temps (accélérateur) Grand nombre de coeurs peu coûteux Énergie $$ Vitesse de calcul théorique impressionnante : plusieurs Teraflops Modèle hybride de programmation : CPU et GPU en même temps

7 Historique 1980s Premiers contrôleurs graphiques (Intel, TI, IBM) 1990s Premiers GPUs 2D et 3D Gaming 2001-2005 2006 2007+ Introduction de CUDA Révisions des compute capabilities... OpenCL OpenACC 2012-2013 K20, Titan compute capability 3.5 http://en.wikipedia.org/wiki/cuda#version_features_and_specifications

8 Applications connues ABAQUS, Amber, GROMACS, LAMMPS, MATLAB, PETSc, OpenFOAM (certains solvers) http://www.nvidia.ca/object/gpu-applications.html

9 Librairies CuBLAS Magma CuFFT Thrust

10 Programmation CUDA-{C,Fortran Compute Unified Device Architecture OpenCL OpenACC

11 Rappel : C et les pointeurs int x = 1; int *p; p = &x; *p = 0; printf( x=%d\n,x); Quel sera l'affichage à l'écran? A)x=1 B)x=0 C)x=0x7ffff957 D)Erreur ou SEGFAULT

11 Rappel : C et les pointeurs int x = 1; int *p; p = &x; *p = 0; printf( x=%d\n,x); Quel sera l'affichage à l'écran? A)x=1 B)x=0 C)x=0x7ffff957 D)Erreur ou SEGFAULT On assigne 0 à la mémoire vers laquelle pointe p

V1.0 Terminologie et architecture 12

13 GPU

14 192 coeurs fp32 Calcul flotant 32 bits Calcul entier (simple) 32 bits 64 coeurs fp64 32 SFU Multiplications d'entier, etc. sin, cos,... 32 LD/ST Chargement / rapatriement de la mémoire 16 TEX Textures SM (GK110)

15 Connexion et préparation de l'environnement $ ssh userx@helios.calculquebec.ca password: [~]$ prepare_formation cuda [~]$ prepare_formation job [user41@gpu-k20-02 ~]$ cd ~/ formation/cuda/formation Noeuds de formation fournis par :

16 Structure des exercices [mboisson@colosse3 formation]$ ls -lh total 36K drwxr-xr-x 2 mboisson clumeq 4,0K 7 oct 10:58 1-compilation drwxr-xr-x 3 mboisson clumeq 4,0K 9 oct 14:59 3-remplir-vecteur drwxr-xr-x 3 mboisson clumeq 4,0K 9 oct 15:10 4-multiplications-matrices drwxr-xr-x 3 mboisson clumeq 4,0K 7 oct 10:58 5-erreurs drwxr-xr-x 3 mboisson clumeq 4,0K 8 oct 16:05 7-produit-scalaire drwxr-xr-x 3 mboisson clumeq 4,0K 8 oct 16:04 8-multiplications-matricecuda6 drwxr-xr-x 3 mboisson clumeq 4,0K 20 oct 10:08 9-multiples-gpus -rw-r--r-- 1 mboisson clumeq 504 6 oct 15:30 makefile-levels.mk -rw-r--r-- 1 mboisson clumeq 293 9 oct 10:59 makefile.mk

17 Code minimal global void foo() { int main() { foo<<<1,1>>>(); printf("cuda error: %s\n", cudageterrorstring(cudagetlasterror())); return 0; trivial.cu

18 Compilation $ nvcc <fichier.cu> -o <exe> $./<exe>

19 Exercice 1 : compilation Fichier trivial.cu Description Compiler et exécuter un programme CUDA-C

19 Exercice 1 : compilation Fichier Description trivial.cu Compiler et exécuter un programme CUDA-C Indice : Utilisez "nvcc" comme compilateur

20 Exercice 1 : solution $ nvcc trivial.cu -o trivial $./trivial CUDA error: no error $

20 Exercice 1 : solution $ nvcc trivial.cu -o trivial $./trivial CUDA error: no error $ «Compilateur» à utiliser

21 Exercice 1 : solution (alternative) $ nvcc -arch=sm_12 trivial.cu -o trivial $./trivial CUDA error: no error $

21 Exercice 1 : solution (alternative) $ nvcc -arch=sm_12 trivial.cu -o trivial $./trivial CUDA error: no error $ Important pour avoir accès à des opérations atomiques de base.

22 Exercice 1 : restant des exercices $ make nvcc -arch=sm_12 -c trivial.cu -o trivial.o nvcc trivial.o -o trivial $./trivial CUDA error: no error $

23 Code minimal global void foo() { int main() { foo<<<1,1>>>(); printf("cuda error: %s\n", cudageterrorstring(cudagetlasterror())); return 0;

23 Code minimal Kernel global void foo() { int main() { foo<<<1,1>>>(); printf("cuda error: %s\n", cudageterrorstring(cudagetlasterror())); return 0;

23 Portée de la fonction(kernel). Code minimal Kernel global void foo() { int main() { foo<<<1,1>>>(); printf("cuda error: %s\n", cudageterrorstring(cudagetlasterror())); return 0;

23 Portée de la fonction(kernel). Code minimal Kernel global void foo() { int main() { foo<<<1,1>>>(); printf("cuda error: %s\n", cudageterrorstring(cudagetlasterror())); return 0; Paramètres d exécution du kernel.

24 Exemples CUDA $CUDA_HOME/samples Contient : Exemples de programmes (utilitaires, imagerie, finance, simulations, librairies) Documentation Outils de gestion (nvidia-smi)

25 Exercice 2 : exemples CUDA Répertoire $CUDA_HOME/samples/1_Utilities/deviceQuery/ Description Copier, compiler et exécuter le programme devicequery

26 Sortie de devicequery Detected 1 CUDA Capable device(s) Device 0 : "GeForce GT 330M" CUDA Driver Version / Runtime Version 5.5 / 5.5 CUDA Capability Major/Minor version number: 1.2 Total amount of global memory: 256 MBytes (268107776 bytes) ( 6) Multiprocessors x ( 8) CUDA Cores/MP: 48 CUDA Cores Total amount of shared memory per block: 16384 bytes Maximum number of threads per block: 512 Maximum sizes of each dimension of a block: 512 x 512 x 64 Maximum sizes of each dimension of a grid: 65535 x 65535 x 1

26 Sortie de devicequery Detected 1 CUDA Capable device(s) Device 0 : "GeForce GT 330M" Nombre de carte(s) CUDA Driver Version / Runtime Version 5.5 / 5.5 CUDA Capability Major/Minor version number: 1.2 Total amount of global memory: 256 MBytes (268107776 bytes) ( 6) Multiprocessors x ( 8) CUDA Cores/MP: 48 CUDA Cores Total amount of shared memory per block: 16384 bytes Maximum number of threads per block: 512 Maximum sizes of each dimension of a block: 512 x 512 x 64 Maximum sizes of each dimension of a grid: 65535 x 65535 x 1

26 Sortie de devicequery Detected 1 CUDA Capable device(s) Device 0 : "GeForce GT 330M" CUDA Driver Version / Runtime Version 5.5 / 5.5 CUDA Capability Major/Minor version number: 1.2 Numéro de la carte Total amount of global memory: 256 MBytes (268107776 bytes) ( 6) Multiprocessors x ( 8) CUDA Cores/MP: 48 CUDA Cores Total amount of shared memory per block: 16384 bytes Maximum number of threads per block: 512 Maximum sizes of each dimension of a block: 512 x 512 x 64 Maximum sizes of each dimension of a grid: 65535 x 65535 x 1

26 Sortie de devicequery Detected 1 CUDA Capable device(s) Device 0 : "GeForce GT 330M" CUDA Driver Version / Runtime Version 5.5 / 5.5 CUDA Capability Major/Minor Version version de number: 1.2 Total amount of global memory: CUDA (268107776 bytes) 256 MBytes ( 6) Multiprocessors x ( 8) CUDA Cores/MP: 48 CUDA Cores Total amount of shared memory per block: 16384 bytes Maximum number of threads per block: 512 Maximum sizes of each dimension of a block: 512 x 512 x 64 Maximum sizes of each dimension of a grid: 65535 x 65535 x 1

26 Sortie de devicequery Detected 1 CUDA Capable device(s) Device 0 : "GeForce GT 330M" CUDA Driver Version /"Capability" Runtime Version 5.5 / 5.5 CUDA Capability Major/Minor version number: 1.2 Total amount of global memory: 256 MBytes (268107776 bytes) ( 6) Multiprocessors x ( 8) CUDA Cores/MP: 48 CUDA Cores Total amount of shared memory per block: 16384 bytes Maximum number of threads per block: 512 Maximum sizes of each dimension of a block: 512 x 512 x 64 Maximum sizes of each dimension of a grid: 65535 x 65535 x 1

26 Sortie de devicequery Detected 1 CUDA Capable device(s) Device 0 : "GeForce GT 330M" CUDA Driver Version / Runtime Version 5.5 / 5.5 CUDA Capability Major/Minor version number: 1.2 Total amount of global memory: 256 MBytes (268107776 bytes) ( 6) Multiprocessors Mémoire x ( 8) CUDA Cores/MP: 48 CUDA Cores Total amount of shared totale memory per block: 16384 bytes Maximum number of threads per block: 512 Maximum sizes of each dimension of a block: 512 x 512 x 64 Maximum sizes of each dimension of a grid: 65535 x 65535 x 1

26 Sortie de devicequery Detected 1 CUDA Capable device(s) Device 0 : "GeForce GT 330M" CUDA Driver Version / Runtime Version 5.5 / 5.5 CUDA Capability Major/Minor version number: 1.2 Total amount of global memory: 256 MBytes (268107776 bytes) ( 6) Multiprocessors x ( 8) CUDA Cores/MP: 48 CUDA Cores Total amount of shared Nombre memory de per block: 16384 bytes coeurs Maximum number of threads per block: 512 Maximum sizes of each dimension of a block: 512 x 512 x 64 Maximum sizes of each dimension of a grid: 65535 x 65535 x 1

26 Sortie de devicequery Detected 1 CUDA Capable device(s) Device 0 : "GeForce GT 330M" CUDA Driver Version / Runtime Version 5.5 / 5.5 CUDA Capability Major/Minor version number: 1.2 Total amount of global memory: 256 MBytes (268107776 bytes) Mémoire ( 6) Multiprocessors x ( partagée 8) CUDA Cores/MP: 48 CUDA Cores Total amount of shared memory per block: 16384 bytes Maximum number of threads per block: 512 Maximum sizes of each dimension of a block: 512 x 512 x 64 Maximum sizes of each dimension of a grid: 65535 x 65535 x 1

26 Sortie de devicequery Detected 1 CUDA Capable device(s) Device 0 : "GeForce GT 330M" CUDA Driver Version / Runtime Version 5.5 / 5.5 CUDA Capability Major/Minor version number: 1.2 Total amount of global memory: 256 MBytes (268107776 bytes) ( 6) Multiprocessors Nombre x ( 8) de CUDA fils Cores/MP: 48 CUDA Cores Total amount of max. shared par memory bloc per block: 16384 bytes Maximum number of threads per block: 512 Maximum sizes of each dimension of a block: 512 x 512 x 64 Maximum sizes of each dimension of a grid: 65535 x 65535 x 1

26 Sortie de devicequery Detected 1 CUDA Capable device(s) Device 0 : "GeForce GT 330M" CUDA Driver Version / Runtime Version 5.5 / 5.5 CUDA Capability Major/Minor version number: 1.2 Total amount of global memory: 256 MBytes (268107776 bytes) ( 6) Multiprocessors x ( 8) CUDA Cores/MP: 48 CUDA Cores Total amount of Dimensions shared memory per block: 16384 bytes Maximum number of threads per block: 512 maximales Maximum sizes of each dimension of a block: 512 x 512 x 64 Maximum sizes of each dimension of a grid: 65535 x 65535 x 1

27 Flot d'exécution CUDA Code série

27 Flot d'exécution CUDA Code série Allocation de mémoire sur le GPU Copie de données vers le GPU Lancement du "kernel"

27 Flot d'exécution CUDA Code série Allocation de mémoire sur le GPU Copie de données vers le GPU Lancement du "kernel" Code parallèle Exécution du noyau

27 Flot d'exécution CUDA Code série Allocation de mémoire sur le GPU Copie de données vers le GPU Lancement du "kernel" Code parallèle Exécution du noyau Code série Copie des résultats vers l'hôte Libération de la mémoire du GPU

V1.0 Gestion de mémoire 28

29 Gestion de mémoire

29 Gestion de mémoire cudamalloc(void ** devptr, size_t size) Équivalent de malloc en C (new en C++)

29 Gestion de mémoire cudamalloc(void ** devptr, size_t size) Équivalent de malloc en C (new en C++) cudafree(void * devptr); Équivalent de free en C (delete en C++)

29 Gestion de mémoire cudamalloc(void ** devptr, size_t size) Équivalent de malloc en C (new en C++) cudafree(void * devptr); Équivalent de free en C (delete en C++) cudamemcpy(void * dst, const void * src, size_t size, enum cudamemcpykind kind); cudamemcpyhosttohost cudamemcpyhosttodevice cudamemcpydevicetohost

V1.0 Quiz sur les pointeurs et la gestion de mémoire 30

31 Sortie 1 de 3 global void add2(int *a) { *a = *a + 2; int main( void ) { int *data_h, *data_d; cudamalloc((void**)&data_d, sizeof(int)); data_h = (int *)malloc(sizeof(int)); *data_h = 5; cudamemcpy(data_d, data_h, sizeof(int), cudamemcpyhosttodevice); add2<<<1,1>>>(data_d); cudamemcpy(data_h, data_d, sizeof(int), cudamemcpydevicetohost); printf("data: %d\n", *data_h); free(data_h); cudafree(data_d); return 0; A. data: 5 B. data: 7 C. Erreur ou segfault D. Ne compile pas E. Autre

31 Sortie 1 de 3 global void add2(int *a) { *a = *a + 2; int main( void ) { int *data_h, *data_d; cudamalloc((void**)&data_d, sizeof(int)); data_h = (int *)malloc(sizeof(int)); *data_h = 5; cudamemcpy(data_d, data_h, sizeof(int), cudamemcpyhosttodevice); add2<<<1,1>>>(data_d); cudamemcpy(data_h, data_d, sizeof(int), cudamemcpydevicetohost); printf("data: %d\n", *data_h); free(data_h); cudafree(data_d); return 0; A. data: 5 B. data: 7 C. Erreur ou segfault D. Ne compile pas E. Autre

31 Sortie 1 de 3 global void add2(int *a) { *a = *a + 2; int main( void ) { int *data_h, *data_d; cudamalloc((void**)&data_d, sizeof(int)); data_h = (int *)malloc(sizeof(int)); *data_h = 5; cudamemcpy(data_d, data_h, sizeof(int), cudamemcpyhosttodevice); add2<<<1,1>>>(data_d); cudamemcpy(data_h, data_d, sizeof(int), cudamemcpydevicetohost); printf("data: %d\n", *data_h); free(data_h); cudafree(data_d); return 0; A. data: 5 B. data: 7 C. Erreur ou segfault D. Ne compile pas E. Autre

31 Sortie 1 de 3 global void add2(int *a) { *a = *a + 2; int main( void ) { int *data_h, *data_d; cudamalloc((void**)&data_d, sizeof(int)); data_h = (int *)malloc(sizeof(int)); *data_h = 5; cudamemcpy(data_d, data_h, sizeof(int), cudamemcpyhosttodevice); add2<<<1,1>>>(data_d); cudamemcpy(data_h, data_d, sizeof(int), cudamemcpydevicetohost); printf("data: %d\n", *data_h); free(data_h); cudafree(data_d); return 0; A. data: 5 B. data: 7 C. Erreur ou segfault D. Ne compile pas E. Autre

31 Sortie 1 de 3 global void add2(int *a) { *a = *a + 2; int main( void ) { int *data_h, *data_d; cudamalloc((void**)&data_d, sizeof(int)); data_h = (int *)malloc(sizeof(int)); *data_h = 5; cudamemcpy(data_d, data_h, sizeof(int), cudamemcpyhosttodevice); add2<<<1,1>>>(data_d); cudamemcpy(data_h, data_d, sizeof(int), cudamemcpydevicetohost); printf("data: %d\n", *data_h); free(data_h); cudafree(data_d); return 0; A. data: 5 B. data: 7 C. Erreur ou segfault D. Ne compile pas E. Autre

31 Sortie 1 de 3 global void add2(int *a) { *a = *a + 2; int main( void ) { int *data_h, *data_d; cudamalloc((void**)&data_d, sizeof(int)); data_h = (int *)malloc(sizeof(int)); *data_h = 5; cudamemcpy(data_d, data_h, sizeof(int), cudamemcpyhosttodevice); add2<<<1,1>>>(data_d); cudamemcpy(data_h, data_d, sizeof(int), cudamemcpydevicetohost); printf("data: %d\n", *data_h); free(data_h); cudafree(data_d); return 0; A. data: 5 B. data: 7 C. Erreur ou segfault D. Ne compile pas E. Autre

31 Sortie 1 de 3 global void add2(int *a) { *a = *a + 2; int main( void ) { int *data_h, *data_d; cudamalloc((void**)&data_d, sizeof(int)); data_h = (int *)malloc(sizeof(int)); *data_h = 5; cudamemcpy(data_d, data_h, sizeof(int), cudamemcpyhosttodevice); add2<<<1,1>>>(data_d); cudamemcpy(data_h, data_d, sizeof(int), cudamemcpydevicetohost); printf("data: %d\n", *data_h); free(data_h); cudafree(data_d); return 0; A. data: 5 B. data: 7 C. Erreur ou segfault D. Ne compile pas E. Autre

31 Sortie 1 de 3 global void add2(int *a) { *a = *a + 2; int main( void ) { int *data_h, *data_d; cudamalloc((void**)&data_d, sizeof(int)); data_h = (int *)malloc(sizeof(int)); *data_h = 5; cudamemcpy(data_d, data_h, sizeof(int), cudamemcpyhosttodevice); add2<<<1,1>>>(data_d); cudamemcpy(data_h, data_d, sizeof(int), cudamemcpydevicetohost); printf("data: %d\n", *data_h); free(data_h); cudafree(data_d); return 0; A. data: 5 B. B. data: 77 C. Erreur ou segfault D. Ne compile pas E. Autre

32 Sortie 2 de 3 global void add2(int *a) { *a = *a + 2; int main( void ) { int *data_h, *data_d; cudamalloc((void**)&data_d, sizeof(int)); data_h = (int *)malloc(sizeof(int)); *data_h = 5; /* cudamemcpy(data_d, data_h, sizeof(int), cudamemcpyhosttodevice); */ add2<<<1,1>>>(data_d); cudamemcpy(data_h, data_d, sizeof(int), cudamemcpydevicetohost); printf("data: %d\n", *data_h); free(data_h); cudafree(data_d); return 0; A. data: 5 B. data: 7 C. Erreur ou segfault D. Ne compile pas E. Autre

32 Sortie 2 de 3 global void add2(int *a) { *a = *a + 2; int main( void ) { int *data_h, *data_d; cudamalloc((void**)&data_d, sizeof(int)); data_h = (int *)malloc(sizeof(int)); *data_h = 5; /* cudamemcpy(data_d, data_h, sizeof(int), cudamemcpyhosttodevice); */ add2<<<1,1>>>(data_d); cudamemcpy(data_h, data_d, sizeof(int), cudamemcpydevicetohost); printf("data: %d\n", *data_h); free(data_h); cudafree(data_d); return 0; A. data: 5 B. data: 7 C. Erreur ou segfault D. Ne compile pas E. Autre E. Autre

33 Sortie 3 de 3 global void add2(int *a) { *a = *a + 2; int main( void ) { int *data_h, *data_d; cudamalloc((void**)&data_d, sizeof(int)); data_h = (int *)malloc(sizeof(int)); *data_h = 5; cudamemcpy(data_d, data_h, sizeof(int), cudamemcpyhosttodevice); add2<<<1,1>>>(data_d); cudamemcpy(data_h, data_d, sizeof(int), cudamemcpydevicetohost); printf("data: %d\n", *data_d); free(data_h); cudafree(data_d); return 0; A. data: 5 B. data: 7 C. Erreur ou segfault D. Ne compile pas E. Autre

33 Sortie 3 de 3 global void add2(int *a) { *a = *a + 2; int main( void ) { int *data_h, *data_d; cudamalloc((void**)&data_d, sizeof(int)); data_h = (int *)malloc(sizeof(int)); *data_h = 5; cudamemcpy(data_d, data_h, sizeof(int), cudamemcpyhosttodevice); add2<<<1,1>>>(data_d); cudamemcpy(data_h, data_d, sizeof(int), cudamemcpydevicetohost); printf("data: %d\n", *data_d); free(data_h); cudafree(data_d); return 0; A. data: 5 B. data: 7 C. Erreur ou segfault D. Ne compile pas E. Autre

V1.0 Calcul d'index 34

35 Code minimal global void foo() { int main() { foo<<< 1, 1 >>>();

35 Code minimal global void foo() {? int main() { foo<<< 1, 1 >>>();

35 Code minimal global void foo() { int main() { foo<<< 1, 1 >>>(); Taille de la grille (nombre de blocs)

35 Code minimal global void foo() { int main() { foo<<< 1, 1 >>>(); Taille d un bloc (nombre de threads)

36 Grille? Bloc?

36 Grille? Bloc? Kernel = grille de blocs de threads (dimensions fixes)

36 Grille? Bloc? Kernel = grille de blocs de threads (dimensions fixes) Grille = tableau 3D de blocs uniformes

36 Grille? Bloc? Kernel = grille de blocs de threads (dimensions fixes) Grille = tableau 3D de blocs uniformes Bloc = tableau 3D de threads

36 Grille? Bloc? Kernel = grille de blocs de threads (dimensions fixes) Grille = tableau 3D de blocs uniformes Bloc = tableau 3D de threads 1 bloc = max 1024 threads

36 Grille? Bloc? Kernel = grille de blocs de threads (dimensions fixes) Grille = tableau 3D de blocs uniformes Bloc = tableau 3D de threads 1 bloc = max 1024 threads 1 bloc = 1 SM (streaming multiprocessor), plusieurs «warps»

36 Grille? Bloc? Kernel = grille de blocs de threads (dimensions fixes) Grille = tableau 3D de blocs uniformes Bloc = tableau 3D de threads 1 bloc = max 1024 threads 1 bloc = 1 SM (streaming multiprocessor), plusieurs «warps» 1 warp = SIMD (single instruction, multiple data)

37 Calcul d'index Le programmeur défini le nombre de bloc dans la grille, puis le nombre de threads par bloc Le code du kernel connait son identifiant de thread, son identifiant de bloc et la taille de ce dernier thread ID 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 Block 0 Block 1 Block 2 Taille d'un block: 6 kernel<<<3, 6>>> ()

38 Calcul d'index thread ID 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 Taille d'un block: 6 Block 0 Block 1 Block 2 kernel<<<3, 6>>> ()

38 Calcul d'index thread ID 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 Block 0 Block 1 Block 2 Taille d'un block: 6 kernel<<<3, 6>>> () threadidx.x Numéro du fil d'exécution dans le block

38 Calcul d'index thread ID 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 Taille d'un block: 6 Block 0 Block 1 Block 2 blockidx.x Numéro du block kernel<<<3, 6>>> () threadidx.x Numéro du fil d'exécution dans le block

38 Calcul d'index thread ID 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 Taille d'un block: 6 Block 0 Block 1 Block 2 blockdim.x Dimension d'un block (équivalent au nombre de thread par block) blockidx.x Numéro du block kernel<<<3, 6>>> () threadidx.x Numéro du fil d'exécution dans le block

39 Calcul d'index Quelle expression permet de calculer l'index (unique) pour chaque fil d'exécution? A. idx = threadidx.x + blockidx.x B. idx = threadidx.x * blockidx.x C. idx = threadidx.x * blockdim.x + blockidx.x D. idx = threadidx.x + blockdim.x * blockidx.x

39 Calcul d'index Quelle expression permet de calculer l'index (unique) pour chaque fil d'exécution? A. idx = threadidx.x + blockidx.x B. idx = threadidx.x * blockidx.x C. idx = threadidx.x * blockdim.x + blockidx.x D. idx = threadidx.x + blockdim.x * blockidx.x

40 Exercice 3 : remplir un vecteur Fichier Description integers.cu Déplacer le code pour remplir un vecteur du CPU vers le GPU

40 Exercice 3 : remplir un vecteur Fichier Description integers.cu Déplacer le code pour remplir un vecteur du CPU vers le GPU Indice : N'oubliez-pas de copier les résultats vers l'hôte

41 Exercice 3 : remplir un vecteur Solution 1 #define BLOCK_SIZE 1 const int N = 100; fillarray<<<n, BLOCK_SIZE>>>( );

41 Exercice 3 : remplir un vecteur? Solution 1 #define BLOCK_SIZE 1 const int N = 100; const uint32_t N = 4000000000; fillarray<<<n, BLOCK_SIZE>>>( );

41 Exercice 3 : remplir un vecteur Solution 1 #define BLOCK_SIZE 1 const int N = 100; const uint32_t N = 4000000000; fillarray<<<n, BLOCK_SIZE>>>( ); Maximum sizes of each dimension of a grid: 2147483647 x 65535 x 65535./deviceQuery

42 Exercice 3 : remplir un vecteur Solution 2 #define BLOCK_SIZE 128 const int N = 100; fillarray<<<n/block_size, BLOCK_SIZE>>>( );

42 Exercice 3 : remplir un vecteur Solution 2 #define BLOCK_SIZE 128 const int N = 100; fillarray<<<n/block_size, BLOCK_SIZE>>>( ); $./integers integers: integers.cu:39: int main(int, char**): Assertion `data[i] == i' failed. Abandon (core dumped)?

42 Exercice 3 : remplir un vecteur Solution 2 N/BLOCK_SIZE est une division entière #define BLOCK_SIZE 128 const int N = 100; fillarray<<<n/block_size, BLOCK_SIZE>>>( ); $./integers integers: integers.cu:39: int main(int, char**): Assertion `data[i] == i' failed. Abandon (core dumped)

43 Exercice 3 : remplir un vecteur Solution 3 #define BLOCK_SIZE 128 global void fillarray(int *data, int N) { int idx = threadidx.x + blockidx.x*blockdim.x; data[idx] = idx; const int N = 100; fillarray<<<(n+block_size-1)/block_size, BLOCK_SIZE>>>( );

43 Exercice 3 : remplir un vecteur Solution 3 #define BLOCK_SIZE 128 global void fillarray(int *data, int N) { int idx = threadidx.x + blockidx.x*blockdim.x; data[idx] = idx; const int N = 100; fillarray<<<(n+block_size-1)/block_size, BLOCK_SIZE>>>( ); A. $./integers B. Correct! Vraiment?

43 Exercice 3 : remplir un vecteur Solution 3 #define BLOCK_SIZE 128 global void fillarray(int *data, int N) { int idx = threadidx.x + blockidx.x*blockdim.x; data[idx] = idx; idx = {0 128 idx > 99 écrase de la mémoire qui ne nous appartient pas. const int N = 100; fillarray<<<(n+block_size-1)/block_size, BLOCK_SIZE>>>( ); A. $./integers B. Correct!

44 Exercice 3 : remplir un vecteur Solution 4 (valide) #define BLOCK_SIZE 128 global void fillarray(int *data, int N) { int idx = threadidx.x + blockidx.x*blockdim.x; if (idx < N) { data[idx] = idx; const int N = 100; fillarray<<<(n+block_size-1)/block_size, BLOCK_SIZE>>>( );

45 Exercice 4 : multiplication de matrices Fichiers matrixmul.cu matrixmul_med.cu matrixmul_adv.cu Description Compléter, à l'aide des fonctions CUDA, le programme de multiplication de matrices

46 Division de la grille dim3 dimblock(block_size,block_size); dim3 dimgrid(msize/block_size,msize/block_size); MatMulKernel<<<dimGrid,dimBlock>>>(d_A, d_b, d_c);

47 Exercice 4 : algorithme Thread 0, Block 0, idx = 0, idy=0, MSIZE = 2 0 1 2 3 0 2 1 3 for (int i = 0; i < MSIZE; ++i) { Cvalue += A[row * MSIZE + i] * B[i * MSIZE + col]; C[row * MSIZE + col] = Cvalue; Thread 3, Block 1, idx = 1, idy=1, MSIZE = 2 0 1 2 3 0 2 1 3

47 Exercice 4 : algorithme Thread 0, Block 0, idx = 0, idy=0, MSIZE = 2 0 1 2 3 0 2 1 3 for (int i = 0; i < MSIZE; ++i) { Cvalue += A[row * MSIZE + i] * B[i * MSIZE + col]; C[row * MSIZE + col] = Cvalue; Thread 3, Block 1, idx = 1, idy=1, MSIZE = 2 0 1 2 3 0 2 1 3

47 Exercice 4 : algorithme Thread 0, Block 0, idx = 0, idy=0, MSIZE = 2 0 1 2 3 0 2 1 3 for (int i = 0; i < MSIZE; ++i) { Cvalue += A[row * MSIZE + i] * B[i * MSIZE + col]; C[row * MSIZE + col] = Cvalue; Thread 3, Block 1, idx = 1, idy=1, MSIZE = 2 0 1 2 3 0 2 1 3

47 Exercice 4 : algorithme Thread 0, Block 0, idx = 0, idy=0, MSIZE = 2 0 1 0 1 2 2 3 2 3 for (int i = 0; i < MSIZE; ++i) { Cvalue += A[row * MSIZE + i] * B[i * MSIZE + col]; C[row * MSIZE + col] = Cvalue; Thread 3, Block 1, idx = 1, idy=1, MSIZE = 2 0 1 0 1 2 2 3 2 3

47 Exercice 4 : algorithme Thread 0, Block 0, idx = 0, idy=0, MSIZE = 2 0 1 0 1 2 2 3 2 3 for (int i = 0; i < MSIZE; ++i) { Cvalue += A[row * MSIZE + i] * B[i * MSIZE + col]; C[row * MSIZE + col] = Cvalue; Thread 3, Block 1, idx = 1, idy=1, MSIZE = 2 0 1 0 1 2 2 3 2 3

47 Exercice 4 : algorithme Thread 0, Block 0, idx = 0, idy=0, MSIZE = 2 0 1 0 1 2 2 3 2 3 for (int i = 0; i < MSIZE; ++i) { Cvalue += A[row * MSIZE + i] * B[i * MSIZE + col]; C[row * MSIZE + col] = Cvalue; Thread 3, Block 1, idx = 1, idy=1, MSIZE = 2 0 1 0 1 2 2 3 2 3

47 Exercice 4 : algorithme Thread 0, Block 0, idx = 0, idy=0, MSIZE = 2 0 1 0 1 2 2 3 2 3 for (int i = 0; i < MSIZE; ++i) { Cvalue += A[row * MSIZE + i] * B[i * MSIZE + col]; C[row * MSIZE + col] = Cvalue; Thread 3, Block 1, idx = 1, idy=1, MSIZE = 2 0 1 0 1 2 2 3 2 3 11

48 Exercice 4 : multiplication de matrices global void MatMulKernel(float* A, float* B, float* C) { int col = threadidx.x + blockidx.x * blockdim.x; int row = threadidx.y + blockidx.y * blockdim.y; // Compute the row and column for (int i = 0; i < MSIZE; ++i) { Cvalue += A[row * MSIZE + i] * B[i * MSIZE + col]; C[row*MSIZE+col] = Cvalue;

V1.0 Vérification des erreurs 49

50 Vérification des erreurs Toutes les fonctions Cuda retournent un «cudaerror_t» Doit être égal à «cudasuccess» pour être sans erreur cudagetlasterror() retourne la dernière erreur (pour les kernels) Appeler cudadevicesynchronize() pour attendre après un kernel

51 Exercice 5 : vérification des erreurs Fichier Description errorcheck.cu Ajouter des vérifications d'erreurs CUDA dans le code. Corriger les erreurs.

51 Exercice 5 : vérification des erreurs Fichier Description errorcheck.cu Ajouter des vérifications d'erreurs CUDA dans le code. Corriger les erreurs. Indice : À première vue, ce programme compile et s'exécute sans erreur. Vérifiez bien tous les retours d'appels CUDA-C.

52 Exercice 5 : solution int main(void) { int *data_d = 0, *data_h = 0; cudaerror_t err; if ((err = cudamalloc((void**)&data_d, sizeof(int)))!= cudasuccess) { printf("could not allocate that much memory. \n%s",cudageterrorstring(err)); exit(1); setdata<<<1,1>>>(0); cudadevicesynchronize(); err = cudagetlasterror(); if (err!= cudasuccess) { printf("error calling setdata. \n%s",cudageterrorstring(err)); goto cleanup; cleanup: if ((err = cudafree(data_d))!= cudasuccess) { printf("could not free memory (free #1) \n%s",cudageterrorstring(err)); exit(1);

52 Initialisation des pointeurs à 0 Exercice 5 : solution int main(void) { int *data_d = 0, *data_h = 0; cudaerror_t err; if ((err = cudamalloc((void**)&data_d, sizeof(int)))!= cudasuccess) { printf("could not allocate that much memory. \n%s",cudageterrorstring(err)); exit(1); setdata<<<1,1>>>(0); cudadevicesynchronize(); err = cudagetlasterror(); if (err!= cudasuccess) { printf("error calling setdata. \n%s",cudageterrorstring(err)); goto cleanup; cleanup: if ((err = cudafree(data_d))!= cudasuccess) { printf("could not free memory (free #1) \n%s",cudageterrorstring(err)); exit(1);

52 Exercice 5 : solution int main(void) { int *data_d = 0, *data_h = 0; cudaerror_t err; Type de retour des fonctions CUDA if ((err = cudamalloc((void**)&data_d, sizeof(int)))!= cudasuccess) { printf("could not allocate that much memory. \n%s",cudageterrorstring(err)); exit(1); setdata<<<1,1>>>(0); cudadevicesynchronize(); err = cudagetlasterror(); if (err!= cudasuccess) { printf("error calling setdata. \n%s",cudageterrorstring(err)); goto cleanup; cleanup: if ((err = cudafree(data_d))!= cudasuccess) { printf("could not free memory (free #1) \n%s",cudageterrorstring(err)); exit(1);

52 Exercice 5 : solution int main(void) { int *data_d = 0, *data_h = 0; cudaerror_t err; if ((err = cudamalloc((void**)&data_d, sizeof(int)))!= cudasuccess) { printf("could not allocate that much memory. \n%s",cudageterrorstring(err)); exit(1); setdata<<<1,1>>>(0); cudadevicesynchronize(); err = cudagetlasterror(); if (err!= cudasuccess) { printf("error calling setdata. \n%s",cudageterrorstring(err)); goto cleanup; Valeur toujours attendue cleanup: if ((err = cudafree(data_d))!= cudasuccess) { printf("could not free memory (free #1) \n%s",cudageterrorstring(err)); exit(1);

52 Exercice 5 : solution int main(void) { int *data_d = 0, *data_h = 0; cudaerror_t err; if ((err = cudamalloc((void**)&data_d, sizeof(int)))!= cudasuccess) { printf("could not allocate that much memory. \n%s",cudageterrorstring(err)); exit(1); setdata<<<1,1>>>(0); cudadevicesynchronize(); err = cudagetlasterror(); if (err!= cudasuccess) { printf("error calling setdata. \n%s",cudageterrorstring(err)); goto cleanup; Important après l appel d un kernel pour vérifier les erreurs cleanup: if ((err = cudafree(data_d))!= cudasuccess) { printf("could not free memory (free #1) \n%s",cudageterrorstring(err)); exit(1);

52 Exercice 5 : solution int main(void) { int *data_d = 0, *data_h = 0; cudaerror_t err; if ((err = cudamalloc((void**)&data_d, sizeof(int)))!= cudasuccess) { printf("could not allocate that much memory. \n%s",cudageterrorstring(err)); exit(1); setdata<<<1,1>>>(0); cudadevicesynchronize(); err = cudagetlasterror(); if (err!= cudasuccess) { printf("error calling setdata. \n%s",cudageterrorstring(err)); goto cleanup; Erreur précédente cleanup: if ((err = cudafree(data_d))!= cudasuccess) { printf("could not free memory (free #1) \n%s",cudageterrorstring(err)); exit(1);

53 Exercice 6 : produit scalaire Fichier Description Aucun Penser un algorithme parallèle de produit scalaire Lister les problèmes rencontrés

53 Exercice 6 : produit scalaire Fichier Description Aucun Penser un algorithme parallèle de produit scalaire Lister les problèmes rencontrés Pseudocode global void dot( float *a, float *b, float *c) { c = a0b0 + a 1 b1 + a2b2 +... + anbn;

54 Solution possible global void dot( float *a, float *b, float *c) { if(threadidx.x + blockidx.x*blockdim.x == 0) { for(int i = 0; i < N; i++) { *c += a[i]*b[i];

54 Solution possible global void dot( float *a, float *b, float *c) { if(threadidx.x + blockidx.x*blockdim.x == 0) { for(int i = 0; i < N; i++) { *c += a[i]*b[i]; Problème 1 : N'utilise pas le parallélisme

54 Solution possible global void dot( float *a, float *b, float *c) { if(threadidx.x + blockidx.x*blockdim.x == 0) { for(int i = 0; i < N; i++) { *c += a[i]*b[i]; Problème 1 : N'utilise pas le parallélisme Problème 2 : Comment stocker les valeurs intermédiaires

54 Solution possible global void dot( float *a, float *b, float *c) { if(threadidx.x + blockidx.x*blockdim.x == 0) { for(int i = 0; i < N; i++) { *c += a[i]*b[i]; Problème 1 : N'utilise pas le parallélisme Problème 2 : Comment stocker les valeurs intermédiaires Problème 3 : Comment effectuer la somme (réduction)

55 Rappel : réduction La réduction consiste à appliquer une fonction d'aggrégation (exemple: sum) à un ensemble de valeurs pour en retirer une seule valeur de retour.

56 Types de mémoire

57 Types de mémoire Type Bande Visibilité Notes Exemple passante Mémoire Lente, latence Tous les fils cudamalloc() device float data[n]; globale élevée d'exécution Mémoire Lente, avec Tous les fils Lecture seule constant float constante cache d'exécution data[n]; Mémoire 150x plus Fils d'exécution Durée de vie shared data[n]; partagée rapide que la d'un même bloc limitée. 50kb/ mémoire bloc globale Registre Plus rapide, Fil d'exécution Limité float data[n]; pas de latence courant (visibilité, espace, durée)

58 Stratégie de réduction Multiplications parallèles Réductions successives (additions)

59 Types de mémoire Type Bande Visibilité Notes Exemple passante Mémoire Lente, latence Tous les fils cudamalloc() device float data[n]; globale élevée d'exécution Mémoire Lente, avec Tous les fils Lecture seule constant float constante cache d'exécution data[n]; Mémoire 150x plus Fils d'exécution Durée de vie shared data[n]; partagée rapide que la d'un même bloc limitée. 50kb/ mémoire bloc globale Registre Plus rapide, Fil d'exécution Limité float data[n]; pas de latence courant (visibilité, espace, durée)

60 Rappel : race condition Deux ou plusieurs fils sont en concurrence pour mettre à jour une valeur partagée 1. #define ARRAY_SIZE 128 2. global shiftl(int *array) { 3. int idx = threadidx.x; 4. tmp = array[idx]; 5. if (idx == 0) 6. array[array_size-1] = tmp; 7. else 8. array[idx-1] = tmp; 9.

61 Rappel : race condition 1. #define ARRAY_SIZE 128 2. global shiftl(int *array) { 3. int idx = threadidx.x; 4. tmp = array[idx]; 5. if (idx == 0) 6. array[array_size-1] = tmp; 7. else 8. array[idx-1] = tmp; 9. Fil d'exécution 0 Fil d'exécution 1 Fil d'exécution 2 Note 3. 3. 3. Calcul d'index 4. 4. 4. Copie de la valeur dans un registre 6. 8. 8. Écriture de la nouvelle valeur

61 Rappel : race condition 1. #define ARRAY_SIZE 128 2. global shiftl(int *array) { 3. int idx = threadidx.x; 4. tmp = array[idx]; 5. if (idx == 0) 6. array[array_size-1] = tmp; 7. else 8. array[idx-1] = tmp; 9. Fil d'exécution 0 Fil d'exécution 1 Fil d'exécution 2 Note 3. 3. 3. Calcul d'index 4. 4. 4. Copie de la valeur dans un registre 6. 8. 8. Écriture de la nouvelle valeur

61 Rappel : race condition 1. #define ARRAY_SIZE 128 2. global shiftl(int *array) { 3. int idx = threadidx.x; 4. tmp = array[idx]; 5. if (idx == 0) 6. array[array_size-1] = tmp; 7. else 8. array[idx-1] = tmp; 9. Fil d'exécution 0 Fil d'exécution 1 Fil d'exécution 2 Note 3. 3. 3. Calcul d'index 4. 4. 4. Copie de la valeur dans un registre 6. 8. 8. Écriture de la nouvelle valeur

61 Rappel : race condition 1. #define ARRAY_SIZE 128 2. global shiftl(int *array) { 3. int idx = threadidx.x; 4. tmp = array[idx]; 5. if (idx == 0) 6. array[array_size-1] = tmp; 7. else 8. array[idx-1] = tmp; 9. Fil d'exécution 0 Fil d'exécution 1 Fil d'exécution 2 Note 3. 3. 3. Calcul d'index 4. 4. 4. Copie de la valeur dans un registre 6. 8. 8. Écriture de la nouvelle valeur

62 Rappel : race condition 1. #define ARRAY_SIZE 128 2. global shiftl(int *array) { 3. int idx = threadidx.x; 4. tmp = array[idx]; 5. if (idx == 0) 6. array[array_size-1] = tmp; 7. else 8. array[idx-1] = tmp; 9. Fil d'exécution 1 Fil d'exécution 2 Note 3. 3. Calcul d'index 4. Copie de la valeur dans un registre (fil 2) 8. Écriture de la nouvelle valeur (fil 2) 4. Copie de la (mauvaise) valeur dans un registre (fil 1) 8. Écriture de la (mauvaise) nouvelle valeur (fil 1)

62 Rappel : race condition 1. #define ARRAY_SIZE 128 2. global shiftl(int *array) { 3. int idx = threadidx.x; 4. tmp = array[idx]; 5. if (idx == 0) 6. array[array_size-1] = tmp; 7. else 8. array[idx-1] = tmp; 9. Fil d'exécution 1 Fil d'exécution 2 Note 3. 3. Calcul d'index 4. Copie de la valeur dans un registre (fil 2) 8. Écriture de la nouvelle valeur (fil 2) 4. Copie de la (mauvaise) valeur dans un registre (fil 1) 8. Écriture de la (mauvaise) nouvelle valeur (fil 1) 1,2

62 Rappel : race condition 1. #define ARRAY_SIZE 128 2. global shiftl(int *array) { 3. int idx = threadidx.x; 4. tmp = array[idx]; 5. if (idx == 0) 6. array[array_size-1] = tmp; 7. else 8. array[idx-1] = tmp; 9. 1 2 Fil d'exécution 1 Fil d'exécution 2 Note 3. 3. Calcul d'index 4. Copie de la valeur dans un registre (fil 2) 8. Écriture de la nouvelle valeur (fil 2) 4. Copie de la (mauvaise) valeur dans un registre (fil 1) 8. Écriture de la (mauvaise) nouvelle valeur (fil 1)

62 Rappel : race condition 1. #define ARRAY_SIZE 128 2. global shiftl(int *array) { 3. int idx = threadidx.x; 4. tmp = array[idx]; 5. if (idx == 0) 6. array[array_size-1] = tmp; 7. else 8. array[idx-1] = tmp; 9. 1 2 Fil d'exécution 1 Fil d'exécution 2 Note 3. 3. Calcul d'index 4. Copie de la valeur dans un registre (fil 2) 8. Écriture de la nouvelle valeur (fil 2) 4. Copie de la (mauvaise) valeur dans un registre (fil 1) 8. Écriture de la (mauvaise) nouvelle valeur (fil 1)

62 Rappel : race condition 1. #define ARRAY_SIZE 128 2. global shiftl(int *array) { 3. int idx = threadidx.x; 4. tmp = array[idx]; 5. if (idx == 0) 6. array[array_size-1] = tmp; 7. else 8. array[idx-1] = tmp; 9. 1 Fil d'exécution 1 Fil d'exécution 2 Note 3. 3. Calcul d'index 4. Copie de la valeur dans un registre (fil 2) 8. Écriture de la nouvelle valeur (fil 2) 4. Copie de la (mauvaise) valeur dans un registre (fil 1) 8. Écriture de la (mauvaise) nouvelle valeur (fil 1)

62 Rappel : race condition 1. #define ARRAY_SIZE 128 2. global shiftl(int *array) { 3. int idx = threadidx.x; 4. tmp = array[idx]; 5. if (idx == 0) 6. array[array_size-1] = tmp; 7. else 8. array[idx-1] = tmp; 9. 1 Fil d'exécution 1 Fil d'exécution 2 Note 3. 3. Calcul d'index 4. Copie de la valeur dans un registre (fil 2) 8. Écriture de la nouvelle valeur (fil 2) 4. Copie de la (mauvaise) valeur dans un registre (fil 1) 8. Écriture de la (mauvaise) nouvelle valeur (fil 1)

63 Solution?

63 Solution? Utiliser un point de synchronisation syncthreads();

64 Point de synchronisation, mais où? 1. #define ARRAY_SIZE 128 2. global shiftl(int *array) { 3. int idx = threadidx.x; 4. tmp = array[idx]; 5. if (idx == 0) 6. array[array_size-1] = tmp; 7. else 8. array[idx-1] = tmp; 9.

64 Point de synchronisation, mais où? 1. #define ARRAY_SIZE 128 2. global shiftl(int *array) { 3. int idx = threadidx.x; 4. tmp = array[idx]; 5. if (idx == 0) 6. array[array_size-1] = tmp; 7. else 8. array[idx-1] = tmp; 9.

65 Rappel : race condition 1. #define ARRAY_SIZE 128 2. global shiftl(int *array) { 3. int idx = threadidx.x; 4. tmp = array[idx]; 5. syncthreads(); 6. if (idx == 0) 7. array[array_size-1] = tmp; 8. else 9. array[idx-1] = tmp; Fil d'exécution 1 Fil d'exécution 2 Note 3. 3. Calcul d'index 4. Copie de la valeur dans un registre (fil 2) 5. Point de synchronisation 4. Copie de la (bonne) valeur dans un registre (fil 1) 5. Point de synchronisation 7. 9. Écriture de la (bonne) nouvelle valeur (fil 1 et 2)

65 Rappel : race condition 1. #define ARRAY_SIZE 128 2. global shiftl(int *array) { 3. int idx = threadidx.x; 4. tmp = array[idx]; 5. syncthreads(); 6. if (idx == 0) 7. array[array_size-1] = tmp; 8. else 9. array[idx-1] = tmp; Fil d'exécution 1 Fil d'exécution 2 Note 3. 3. Calcul d'index 4. Copie de la valeur dans un registre (fil 2) 5. Point de synchronisation 4. Copie de la (bonne) valeur dans un registre (fil 1) 5. Point de synchronisation 7. 9. Écriture de la (bonne) nouvelle valeur (fil 1 et 2) 1,2

65 Rappel : race condition 1. #define ARRAY_SIZE 128 2. global shiftl(int *array) { 3. int idx = threadidx.x; 4. tmp = array[idx]; 5. syncthreads(); 6. if (idx == 0) 7. array[array_size-1] = tmp; 8. else 9. array[idx-1] = tmp; 1 2 Fil d'exécution 1 Fil d'exécution 2 Note 3. 3. Calcul d'index 4. Copie de la valeur dans un registre (fil 2) 5. Point de synchronisation 4. Copie de la (bonne) valeur dans un registre (fil 1) 5. Point de synchronisation 7. 9. Écriture de la (bonne) nouvelle valeur (fil 1 et 2)

65 Rappel : race condition 1. #define ARRAY_SIZE 128 2. global shiftl(int *array) { 3. int idx = threadidx.x; 4. tmp = array[idx]; 5. syncthreads(); 6. if (idx == 0) 7. array[array_size-1] = tmp; 8. else 9. array[idx-1] = tmp; 1 2 Fil d'exécution 1 Fil d'exécution 2 Note 3. 3. Calcul d'index 4. Copie de la valeur dans un registre (fil 2) 5. Point de synchronisation 4. Copie de la (bonne) valeur dans un registre (fil 1) 5. Point de synchronisation 7. 9. Écriture de la (bonne) nouvelle valeur (fil 1 et 2)

65 Rappel : race condition 1. #define ARRAY_SIZE 128 2. global shiftl(int *array) { 3. int idx = threadidx.x; 4. tmp = array[idx]; 5. syncthreads(); 6. if (idx == 0) 7. array[array_size-1] = tmp; 8. else 9. array[idx-1] = tmp; 1 2 Fil d'exécution 1 Fil d'exécution 2 Note 3. 3. Calcul d'index 4. Copie de la valeur dans un registre (fil 2) 5. Point de synchronisation 4. Copie de la (bonne) valeur dans un registre (fil 1) 5. Point de synchronisation 7. 9. Écriture de la (bonne) nouvelle valeur (fil 1 et 2)

65 Rappel : race condition 1. #define ARRAY_SIZE 128 2. global shiftl(int *array) { 3. int idx = threadidx.x; 4. tmp = array[idx]; 5. syncthreads(); 6. if (idx == 0) 7. array[array_size-1] = tmp; 8. else 9. array[idx-1] = tmp; Fil d'exécution 1 Fil d'exécution 2 Note 3. 3. Calcul d'index 4. Copie de la valeur dans un registre (fil 2) 5. Point de synchronisation 4. Copie de la (bonne) valeur dans un registre (fil 1) 5. Point de synchronisation 7. 9. Écriture de la (bonne) nouvelle valeur (fil 1 et 2) 1,2

65 Rappel : race condition 1. #define ARRAY_SIZE 128 2. global shiftl(int *array) { 3. int idx = threadidx.x; 4. tmp = array[idx]; 5. syncthreads(); 6. if (idx == 0) 7. array[array_size-1] = tmp; 8. else 9. array[idx-1] = tmp; 1 2 Fil d'exécution 1 Fil d'exécution 2 Note 3. 3. Calcul d'index 4. Copie de la valeur dans un registre (fil 2) 5. Point de synchronisation 4. Copie de la (bonne) valeur dans un registre (fil 1) 5. Point de synchronisation 7. 9. Écriture de la (bonne) nouvelle valeur (fil 1 et 2)

66 Stratégie de réduction

66 Stratégie de réduction Points de synchronisations

66 Stratégie de réduction Points de synchronisations

67 Exercice 7 : implémenter le dot product Fichier Description dotproduct.cu Implémenter l'algorithme parallèle de produit scalaire.

67 Exercice 7 : implémenter le dot product Fichier Description dotproduct.cu Implémenter l'algorithme parallèle de produit scalaire. Indice 1 : Utilisez la mémoire partagée (performance)

67 Exercice 7 : implémenter le dot product Fichier Description dotproduct.cu Implémenter l'algorithme parallèle de produit scalaire. Indice 1 : Utilisez la mémoire partagée (performance) Indice 2 : N'oubliez-pas la synchronisation

68 Exercice 7 : implémenter le dot product global void dotproduct(float *a, float *b, float *c, float *result, int N) { // At best, our K20 card can handle 49152 bytes of // shared storage (see devicequery output) // That means 49152/sizeof(float) elements, which is 12288. shared float cache[block_size]; int idx = threadidx.x + blockidx.x*blockdim.x; cache[threadidx.x] = (idx < N)? a[idx]*b[idx] : 0.0f; syncthreads(); for (int i = blockdim.x/2; i > 0; i /= 2) { if (threadidx.x < i) { cache[threadidx.x] += cache[threadidx.x+i]; syncthreads(); Filename if (threadidx.x == 0) { c[blockidx.x] = cache[0];

68 Exercice 7 : implémenter le dot product global void dotproduct(float *a, float *b, float *c, float *result, int N) { // At best, our K20 card can handle 49152 bytes of // shared storage (see devicequery output) // That means 49152/sizeof(float) elements, which is 12288. shared float cache[block_size]; int idx = threadidx.x + blockidx.x*blockdim.x; cache[threadidx.x] = (idx < N)? a[idx]*b[idx] : 0.0f; syncthreads(); Filename for (int i = blockdim.x/2; i > 0; i /= 2) { if (threadidx.x < i) { cache[threadidx.x] += cache[threadidx.x+i]; syncthreads(); if (threadidx.x == 0) { c[blockidx.x] = cache[0]; Est-ce que c est complet? Qu arrive-t-il s il y a plusieurs blocs?

69 Exercice 7 : implémenter le dot product global void dotproduct(float *a, float *b, float *c, float *result, int N) {... if (threadidx.x == 0) { c[blockidx.x] = cache[0]; Filename syncthreads(); for (int i=0; i<blockdim.x; i++) c[0] += c[i];

69 Exercice 7 : implémenter le dot product global void dotproduct(float *a, float *b, float *c, float *result, int N) {... if (threadidx.x == 0) { c[blockidx.x] = cache[0]; Filename syncthreads(); for (int i=0; i<blockdim.x; i++) c[0] += c[i]; Est-ce que ça marcherait?

69 Exercice 7 : implémenter le dot product global void dotproduct(float *a, float *b, float *c, float *result, int N) {... if (threadidx.x == 0) { c[blockidx.x] = cache[0]; Filename syncthreads(); for (int i=0; i<blockdim.x; i++) c[0] += c[i]; Est-ce que ça marcherait? Aucune synchronisation possible entre deux blocs à l intérieur d un kernel. La seule façon de synchroniser deux blocs est de lancer deux kernels. => doit finir le calcul sur le CPU ou lancer un autre kernel.

V1.0 Cuda 6 : Mémoire unifiée 70

71 Cuda 6 : Mémoire unifiée Permet d utiliser le même pointeur sur l hôte et sur le GPU Requiert cartes récentes (Kepler+) On doit compiler avec -arch=sm_35 cudamalloc(void **,size_t); cudamemcpy(h_c,d_c,size_t,cudamemcpydevicet ohost); => cudamallocmanaged(void **,size_t)

72 Exercice 8 : multiplication matricielle + mémoire unifiée Fichier Description matrixmul.cu Allouer la mémoire avec cudamallocmanaged et modifier MatMul pour en tenir compte

73 Solution (partie 1) // Allocate space for the matrices mata = (float *) malloc(size); matb = (float *) malloc(size); matc = (float *) malloc(size);... free(matc); free(matb); free(mata); // Allocate space for the matrices cudamallocmanaged(mata,size); cudamallocmanaged(matb,size); cudamallocmanaged(matc,size);... cudafree(matc); cudafree(matb); cudafree(mata);

74 Solution (partie 2) void MatMul(float* A, float* B, float* C) { float *d_a = 0; size_t size = MSIZE * MSIZE * sizeof(float); // TODO : Remove all memory management calls // Allocate space for matrix A on device cudamalloc(&d_a, size); // Copy matrix A to device cudamemcpy(d_a, A, size, cudamemcpyhosttodevice); float *d_b = 0; // Allocate space for matrix B on device cudamalloc(&d_b, size); // Copy matrix B to device cudamemcpy(d_b, B, size, cudamemcpyhosttodevice); // Allocate C in device memory float *d_c = 0; cudamalloc(&d_c, size); // Invoke kernel dim3 dimblock(block_size,block_size); dim3 dimgrid(msize/block_size,msize/block_size); MatMulKernel<<<dimGrid,dimBlock>>>(d_A, d_b, d_c); // Read C from device memory cudamemcpy(c, d_c, size, cudamemcpydevicetohost); // Free device memory cudafree(d_a); cudafree(d_b); cudafree(d_c);

74 Solution (partie 2) void MatMul(float* A, float* B, float* C) { float *d_a = 0; size_t size = MSIZE * MSIZE * sizeof(float); // TODO : Remove all memory management calls // Allocate space for matrix A on device cudamalloc(&d_a, size); // Copy matrix A to device cudamemcpy(d_a, A, size, cudamemcpyhosttodevice); float *d_b = 0; // Allocate space for matrix B on device cudamalloc(&d_b, size); // Copy matrix B to device cudamemcpy(d_b, B, size, cudamemcpyhosttodevice); // Allocate C in device memory float *d_c = 0; cudamalloc(&d_c, size); // Invoke kernel dim3 dimblock(block_size,block_size); dim3 dimgrid(msize/block_size,msize/block_size); MatMulKernel<<<dimGrid,dimBlock>>>(A,B,C); cudadevicesynchronize(); // Read C from device memory cudamemcpy(c, d_c, size, cudamemcpydevicetohost); // Free device memory cudafree(d_a); cudafree(d_b); cudafree(d_c);

74 Solution (partie 2) void MatMul(float* A, float* B, float* C) { float *d_a = 0; size_t size = MSIZE * MSIZE * sizeof(float); // TODO : Remove all memory management calls // Allocate space for matrix A on device cudamalloc(&d_a, size); // Copy matrix A to device cudamemcpy(d_a, A, size, cudamemcpyhosttodevice); float *d_b = 0; // Allocate space for matrix B on device cudamalloc(&d_b, size); // Copy matrix B to device cudamemcpy(d_b, B, size, cudamemcpyhosttodevice); // Allocate C in device memory float *d_c = 0; cudamalloc(&d_c, size); // Invoke kernel dim3 dimblock(block_size,block_size); dim3 dimgrid(msize/block_size,msize/block_size); MatMulKernel<<<dimGrid,dimBlock>>>(A,B,C); cudadevicesynchronize(); // Read C from device memory cudamemcpy(c, d_c, size, cudamemcpydevicetohost);? // Free device memory cudafree(d_a); cudafree(d_b); cudafree(d_c);

75 cudadevicesynchronize Par défaut, tous les appels Cuda sont lancés dans le même «stream» (0) Stream : Une séquence d opérations qui s exécutent dans l ordre sur le GPU Kernels cudamemcpy, cudamemset,... Tout est synchrone sur le GPU... sauf la mémoire unifiée.

V1.0 Multi-GPU 76

77 Contexte Cuda

77 Contexte Cuda Toutes les opérations Cuda sont exécutées dans un «contexte Cuda» Un contexte Cuda est associé à un GPU. On change de contexte en appelant cudasetdevice(int devnum); On connaît le nombre de devices avec cudagetdevicecount(int * dev_count);

V1.0 Exercice 9 : Multiplications de N matrices 78

79 Problématique #1 Performance limitée par l initialisation On doit intialiser sur le GPU Problème : un générateur de nombre aléatoire typique est séquentiel!

80 Nombres aléatoires device unsigned int hash(unsigned int x) { x = (x+0x7ed55d16) + (x<<12); x = (x^0xc761c23c) ^ (x>>19); x = (x+0x165667b1) + (x<<5); x = (x+0xd3a2646c) ^ (x<<9); x = (x+0xfd7046c5) + (x<<3); x = (x^0xb55a4f09) ^ (x>>16); return x; global void RandomFillKernel(float * A, unsigned int seed ) { unsigned int idx = threadidx.x + blockidx.x * blockdim.x; A[idx] = float(hash(idx+seed) / UINT_MAX);

81 Problématique #2

81 Problématique #2 Mémoire unifiée ne fonctionne pas très bien avec plusieurs GPUs

81 Problématique #2 Mémoire unifiée ne fonctionne pas très bien avec plusieurs GPUs Quel GPU devrait avoir la priorité sur la mémoire unifiée?

81 Problématique #2 Mémoire unifiée ne fonctionne pas très bien avec plusieurs GPUs Quel GPU devrait avoir la priorité sur la mémoire unifiée? Solution : retour aux cudamemcpy, cudamalloc, etc.

82 Exercice 9 : multiplication matricielle multi-gpu Fichier Description matrixmul.cu Modifier le code pour qu il s exécute sur 2 GPUs Soumission de tâche Du noeud interactif, faire : prepare_formation job2

83 Solution (partie 1) struct timespec ts; int dev_count = 0; cudagetdevicecount(&dev_count); float * matas[n], * matas_h[n]; float * matbs[n], * matbs_h[n]; float * matcs[n], * matcs_h[n];

83 Solution (partie 1)

84 Solution (partie 2) #pragma omp parallel for num_threads(5) private(ts) for (int d=0; d<n; d++) { cudasetdevice(d % dev_count); clock_gettime(clock_monotonic,&ts); print_duration("clock started. Duration %fs\n",&ts);... // Allocate space for the matrices print_duration("malloc done. Duration %fs\n",&ts); // Seed the random number generator RandomFillKernel<<<MSIZE*MSIZE/BLOCK_SIZE,BLOCK_SIZE>>>(matAs[d],rand()); RandomFillKernel<<<MSIZE*MSIZE/BLOCK_SIZE,BLOCK_SIZE>>>(matBs[d],rand()); print_duration("initialization done. Duration %fs\n",&ts); //Multiply the matrices MatMul(matAs[d], matbs[d], matcs[d]); print_duration("multiplication done. Duration %fs\n",&ts);......... //Copy the results back to host cudamemcpy(matcs[d],matcs_h[d],size,cudamemcpydevicetohost); print_duration("copied data back. Duration %fs\n",&ts); cudafree(matcs[d]); print_duration("freed device memory. Duration %fs\n",&ts);

84 Solution (partie 2)

85 CUDA_VISIBLE_DEVICES

85 CUDA_VISIBLE_DEVICES Liste les numéros des cartes disponibles ex. : CUDA_VISIBLE_DEVICES=0,1 Changer la valeur : export CUDA_VISIBLE_DEVICES=0

86 Exécution avec une seule carte Clock started. Duration 0.000000s Clock started. Duration 0.000000s Malloc done. Duration 0.468985s Initialization done. Duration 0.009761s Malloc done. Duration 0.479983s Initialization done. Duration 0.000088s Multiplication done. Duration 42.948335s Copied data back. Duration 0.000229s Multiplication done. Duration 85.829810s Copied data back. Duration 0.001962s Freed device memory. Duration 42.887963s Freed device memory. Duration 0.007604s Qu est-ce qui se passe avec la deuxième multiplication?

V1.0 Opérations asynchrones 87

88 Streams Par défaut, tous les appels Cuda sont lancés dans le même «stream» (0) Stream : Une séquence d opérations qui s exécutent dans l ordre sur le GPU

89 Streams (suite) cudastream_t stream; cudastreamcreate(&stream); kernel<<<grid,block,0,stream>>>(...); cudamemcpyasync(...,stream); cudastreamsynchronize(stream);

V1.0 Conclusion 90

91 Ressources supplémentaires Wiki : https://wiki.calculquebec.ca/ support@calculquebec.ca colosse@calculquebec.ca helios@calculquebec.ca