GPA667: Conception et simulation de circuits électroniques
1
GPA667: Conception et simulation de circuits électroniques
Calcul parallèle avec GPU
GPA667: Conception et simulation de circuits électroniques
2
SommaireDéfinition du GPU = ‘Graphical Processor Unit’
− un engin de traitement graphique très puissant
− ‘General Purpose Processing on a GPU’ (GPGPU):− depuis 2004, le GPU est perçu comme un processeur
programmable qui est massivement parallèle − grande capacité de traitement dans chaque PC!
− GPU vs CPU: − architecture et modèle du programmeur très différentes− performances nettement plus élevées pour certaines
classes d’applications
GPA667: Conception et simulation de circuits électroniques
3
Sommaire – Section EDocuments de référence:
1. Hwu & Kirk, Programming Massively Parallel Processors, Morgan Kaufmann, 2010 Chapitres du livre sur le site GPA667
2. NVIDIA, NVidia CUDA Programming Guide, NVIDIA, version 2.3.1, 145 pages, 2009
GPA667: Conception et simulation de circuits électroniques
4
SommaireCalcul parallèle avec GPU:
1. Notions préliminaires 2. Intro. à la reconnaissance de formes3. Modèle de programmation CUDA4. Mémoires et transfert de données5. Exécution et ordonnancement de tâches6. Évaluation des performances
5
1 Notions préliminaires Évolution des microprocesseurs
CPU basé sur un seul ALU:− 1980 – 2003: à l’origine d’une croissance rapide de
performances et fonctionnalités pour plusieurs applications− depuis 2003: le limitations de fclk et de consommation de
puissance du CPU occasionnent un ralentissement
Tendance actuelle – CPU avec plusieurs ALU:− circuits multi-cœurs pour augmenter les performances− défis: assigner des séquences d’instructions aux cœurs− le microprocesseur est maintenant une machine parallèle!
GPA667 Section E: Calcul parallèle avec GPU
6
1 Notions préliminaires Évolution des microprocesseurs
Programmes séquentielles:− les applications sont traditionnellement réalisées
avec des programmes de nature séquentiels− ces programmes exécutent une séquence
d’instructions sur un seul ALU
Tendance actuelle – réalisations avec programmes parallèles:− ces programmes exécutent plusieurs sous-séquences
d’instructions qui coopèrent pour augmenter les performances
GPA667 Section E: Calcul parallèle avec GPU
7
1 Notions préliminaires Émergence du GPU
GPU ≡ un classe de processeurs multi-coeursPerformances GPU vs CPU:− débit des calculs: 367 GFLOPS vs. 32 GFLOPS− bande passante mémoire: 86.4 GB/s vs. 8.4 GB/s
GPA667 Section E: Calcul parallèle avec GPU
G80 = GeForce 8800 GTXG71 = GeForce 7900 GTXG70 = GeForce 7800 GTXNV40 = GeForce 6800 UltraNV35 = GeForce FX 5950 UltraNV30 = GeForce FX 5800
8
1 Notions préliminaires Émergence du GPU
Architecture CPU: - optimisé pour exécuter le code séquentiel (1 séquence)- grande cache avec la bande passante mémoire limitéeArchitecture GPU: - optimisé pour exécuter un grand nombre de séquences- petites caches avec grande bande passante mémoire
GPA667 Section E: Calcul parallèle avec GPU
9
1 Notions préliminaires Architecture d’un GPU
GPA667 Section E: Calcul parallèle avec GPU
10
1 Notions préliminaires Architecture d’un GPU
Terminologie (technologie G80):• 16 multiprocesseurs, SM (‘Streaming Multiprocessors’), soit 2
par bloc• 8 cœurs SP (‘Scalar Processor’) par SM• chaque SP comprend des unités fonctionnels:
– MAC et MUL roulant à 1.35 GHz– SQRT et RCP SQRT point flottant
Total: 16 SM x 8 SP/SM = 128 cœurs (ou ALU)500+ GFLOPS possible
GPA667 Section E: Calcul parallèle avec GPU
11
1 Notions préliminaires Architecture d’un GPU
Terminologie (technologie G80):• mémoire:
– 1.5 MB DRAM cache avec une grande bande passante mémoire de 86.4 GB/s
– bande passante de 4 GB/s par direction sur le bus PCI-express (communications avec le CPU hôte et mémoire)
• performance:– maximum: 10+ fois plus de FLOPS qu’un CPU de haute
gamme– séquences simultanées: supporte 768 séquences/cœur,
environ 12,000 séquences/C.I.GPA667 Section E: Calcul parallèle avec GPU
12
1 Notions préliminaires Applications GPU
Pour une classe particulière d’applications, une réalisation GPU permet une grande accélération
Propriétés:1. le besoin de calculs est énorme: on traite beaucoup de
données2. le parallélisme est considérable: le calcul peut être
parallélisé pour différentes parties, et à différents niveaux de granularité
3. le débit de traitement est élevé
GPA667 Section E: Calcul parallèle avec GPU
13
1 Notions préliminaires Applications GPU
L’accélération dépend:1. de la proportion d’une application qui peut être parallélisée
…si une grande proportion du temps d’exécution est passée dans la partie parallélisable, l’accélération sera grande
2. des limitations de la bande passante mémoire3. de la capacité de la mémoire cache
Complémentarité:– assurer que le GPU vient supporter l’exécution du CPU– les applications ont des parties qui s’exécutent mieux avec
CPU, et d’autres avec GPU
GPA667 Section E: Calcul parallèle avec GPU
GPA667: Conception et simulation de circuits électroniques
14
SommaireCalcul parallèle avec GPU:
1. Notions préliminaires 2. Intro. à la reconnaissance de formes3. Modèle de programmation CUDA4. Mémoires et transfert de données5. Exécution et ordonnancement de tâches6. Évaluation des performances
GPA667: Conception et simulation de circuits électroniques 15
SommaireCalcul parallèle avec GPU:
1. Notions préliminaires 2. Intro. à la reconnaissance de formes3. Modèle de programmation CUDA4. Mémoires et transfert de données5. Exécution et ordonnancement de tâches6. Évaluation des performances
16
CUDA (“Compute Unified Device Architecture”) est un modèle de programmation générique– comme extension de C– comme instance d’un modèle de
programmation parallèle SPMD (‘Single-Program, Multiple-Data’)
Dans CUDA, un système ordiné comprend– 1 hôte: CPU traditionnel (processeur séquentiel)– 1 ou + composants: GPU (1 ou + processeurs massivement
parallèles équipées avec plusieurs unités arithmétiques)
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
17
Interaction entre hôte (CPU) et composants (GPU): – le code principal qui exécute sur le CPU peut entamer des
séquences d’instructions sur une GPU– le GPU agit comme coprocesseur dédié, pour accélérer
l’exécution du code parallélisable– i.e., sections du code qui incorporent un bonne proportion
d’opérations parallélisables selon des structures de données au programme
– parallélisme de données: quand plusieurs opérations arithmétiques peuvent s’exécuter simultanément et indépendamment l’un de l’autre sur les données
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
18
Exemple de système ordiné selon CUDA:
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
CPU(hôte)
GPU avec DRAM locale(composant)
19
Mode CUDA G80:– exécute plusieurs séquence– mode d’opération avec interface de programmation
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
Load/store
Global Memory
Thread Execution Manager
Input Assembler
Host
Texture Texture Texture Texture Texture Texture Texture TextureTexture
Parallel DataCache
Parallel DataCache
Parallel DataCache
Parallel DataCache
Parallel DataCache
Parallel DataCache
Parallel DataCache
Parallel DataCache
Load/store Load/store Load/store Load/store Load/store
20
Exemple de parallélisme de données:
multiplication matriciellePIxJ = MIxK x NKxJ
…chaque élément pij de la matrice P est obtenu en calculant le produit scalaire entre la rangée i de M et la colonne j de N.
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
21
Exemple de parallélisme de données:// Multiplication matricielle: code C sur CPU en précision doublevoid MatrixMulOnHost(float* M, float* N, float* P, int Width){ for (int i = 0; i < Width; ++i) for (int j = 0; j < Width; ++j) { double sum = 0; for (int k = 0; k < Width; ++k) { double a = M[i * width + k]; double b = N[k * width + j]; sum += a * b; } P[i * Width + j] = sum; }}
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
22
Réalisations CUDA
Processus de conception avec CUDA:1. identification des parties du programme à paralléliser2. isoler les données requises par les parties parallélisées, et
allocation de la mémoire sur GPU (fonction API) 3. transfert des données au GPU (fonction API)4. développer un fonction noyau (‘kernel’) qui sera exécuté
par les séquences individuelles dans les parties parallèles
API ≡ ‘Application Programming Interface’fonctions donnant accès au GPU
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
23
Réalisations CUDA
Programme CUDA: un code source dont les parties sont soit exécutées sur CPU ou sur GPU− le compilateur NVIDIA C (NVCC) sépare les parties
− code CPU: correspond aux parties avec aucun ou peu de parallélisme réalisé avec un code ANSI Ccompilé avec un compilateur standardexécuté comme un processus normal sur le CPU hôte
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
24
Réalisations CUDA
Programme CUDA: un code source dont les parties sont soit exécutées sur CPU ou sur GPU− code GPU:
correspond aux parties avec beaucoup de parallélismeréalisé avec un code GPU (code ANSI C étendu)la syntaxe associée aux fonctions noyaux indique les parties parallélisées et ainsi que leurs structures de donnéescompilé avec le compilateur NVCC lorsqu’un noyau un appelé, il est exécuté sur le GPU
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
25
Réalisations CUDAFonction noyau (‘kernel’) :– génère plusieurs séquences afin d’exécuter une partie
parallèle → pour tirer profit du parallélisme de données
– grille ≡ ensemble de séquences généré lors de l’appel d’une fonction noyau
– les noyaux CUDA sont très légers:• CPU (multi-cœur): prennent des milliers de cycles
pour générer et céduler quelques séquences• CUDA: un support matériel efficace permet générer et
céduler avec peu de cycles des 1000s de séquences
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
26
Réalisations CUDAExécution d’un programme CUDA typique:
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
27
Réalisations CUDA
Exemple: fonction main pour la multiplication matricielle
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
28
Mémoires GPU et transfert de donnéesCPU et GPU ont des espaces mémoires propres– les GPU sont réalisés sur cartes électroniques avec leur
propre DRAM
Avant d’exécuter un noyau sur GPU…− allocation de mémoire en GPU− transfert de données du CPU à cette mémoire en GPU
Après l’exécution du noyau…− transfert des résultats du GPU en mémoire CPU− libérer la mémoire GPU
… les fonctions API CUDA: extensions du langage ANSI C appelés au code pour gérer les mémoires et transferts
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
29
Mémoires GPU et transfert de données
Modèle de mémoire du GPU (composant CUDA)
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
30
Mémoires GPU et transfert de données
Mémoire globale: (notre focus pour l’instant)− moyen principal pour
communiquer (R/W) des données entre le CPU et le GPU
− le contenu est visible par toutes les séquences
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
Grid
Global Memory
Block (0, 0)
Shared Memory
Thread (0, 0)
Registers
Thread (1, 0)
Registers
Block (1, 0)
Shared Memory
Thread (0, 0)
Registers
Thread (1, 0)
Registers
Host
31
Mémoires GPU et transfert de donnéesFonctions API CUDA pour la gestion de la mémoire globale GPU
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
32
Mémoires GPU et transfert de donnéesExemple:− allocation d’une matrice 64x64 à précision simple (float de
32 bits)− attacher la structure à Md
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
WIDTH = 64;Float* Md;int size = WIDTH * WIDTH * sizeof(float);
cudaMalloc((void**)&Md, size);cudaFree(Md);
33
Mémoires GPU et transfert de donnéesFonctions API CUDA pour le transfert de données entre mémoires
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
34
Mémoires GPU et transfert de donnéesExemple:− transfert au GPU d’une matrice 64x64 à précision simple− la mémoire CPU est M et mémoire GPU est Md− cudaMemcpyHostToDevice et cudaMemcpyDeviceToHost
sont des constantes symboliques
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
cudaMemcpy(Md, M, size, cudaMemcpyHostToDevice);
cudaMemcpy(P, Pd, size, cudaMemcpyDeviceToHost);
35
Mémoires GPU et transfert de donnéesExemple: fonction MatrixMulOnDevice()void MatrixMulOnDevice(float* M, float* N, float* P, int Width){ int size = Width * Width * sizeof(float); float* Md, Nd, Pd; …1. // Allocate and Load M, N to device memory cudaMalloc(&Md, size); cudaMemcpy(Md, M, size, cudaMemcpyHostToDevice);
cudaMalloc(&Nd, size); cudaMemcpy(Nd, N, size, cudaMemcpyHostToDevice);
// Allocate P on the device cudaMalloc(&Pd, size);
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
36
Mémoires GPU et transfert de donnéesExemple: fonction MatrixMulOnDevice()
2. // Kernel invocation code – to be shown later …
3. // Read P from the device cudaMemcpy(P, Pd, size,cudaMemcpyDeviceToHost);
// Free device matrices cudaFree(Md); cudaFree(Nd); cudaFree (Pd); }
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
37
Fonctions noyaux
Appel d’une fonction noyau CUDA:− indique au compilateur NVCC le code qui doit être exécuté
en parallèle− appelé par une fonction CPU pour générer des grilles de
plusieurs séquences d’instructions− tous les séquences exécutent le même code noyau
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
38
Fonctions noyaux
Extensions de la syntaxe ANSI C:− __global__ nom_fonction : indique au compilateur
NVCC que la fonction est de type noyau
− blocIdx.x et blocIdx.y :− indices uniques permettant de distinguer des blocs de
séquences dans une grille− threadIdx.x, threadIdx.y et threadIdx.z :
− indices uniques permettant de distinguer les séquences dans un bloc
− permet à une séquence d’accéder ses registres en temps-réel
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
39
Fonctions noyaux
Extensions de la syntaxe ANSI C:− __global__: définit un fonction noyau− doit retourner void
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
CPUCPU__host__ float HostFunc()
CPUGPU__global__ void KernelFunc()
GPUGPU__device__ float DeviceFunc()
Appelé sur:Exécuté sur:
40
Fonctions noyauxAppel d’une fonction noyau CUDA:− chaque séquence exécute le même code (SPMD)− chaque séquence a une identité qu’il utilise pour calculé
des adresses mémoire et pour le contrôle
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
…float x = input[threadID];
float y = func(x);output[threadID] = y;
…
threadID 76543210
41
Fonctions noyaux• Exemple: fonction MatrixMulOnDevice()
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
42
Fonctions noyaux• Exemple:
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
42
Nd
Md Pd
WID
TH
WID
TH
WIDTH WIDTH
for (int k = 0; k < Width; ++k) { float Melement = Md[threadIdx.y*Width+k]; float Nelement = Nd[k*Width+threadIdx.x]; Pvalue += Melement * Nelement; } Pd[threadIdx.y*Width+threadIdx.x] = Pvalue; }
ty
tx
ty
tx
k
k
43
Fonctions noyauxUne fonction noyau est exécutée avec une grille de séquences parallèles:− grille: typiquement environ 103 séquences par appel pour
pleinement exploiter le GPU
− les séquences d’une grille sont organisées selon un hiérarchie à deux niveaux− chaque grille comprend 1+ blocs avec le même nombre
de séquences (blockIdx.x, y)− chaque bloc est organisé de la même façon, comme un
matrice 3D de séquences (threadIdx.x, y, z) − Max: 512 séquences / bloc
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
44
Fonctions noyauxOrganisation des grilles en blocs multiples:− les séquences dans un bloc peuvent coopérer: mémoire
partagée, opérations atomiques et synchronisation− les séquences de différents blocs ne peuvent coopérer
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
…float x =
input[threadID];float y = func(x);
output[threadID] = y;…
threadID
Thread Block 0
……
float x = input[threadID];
float y = func(x);output[threadID] = y;
…
Thread Block 1
…float x =
input[threadID];float y = func(x);
output[threadID] = y;…
Thread Block N - 176543210 76543210 76543210
45
Fonctions noyauxUn noyau est exécuté comme une grille de séquences parallèles:
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
46
Fonctions noyauxIdentification des blocs et séquences:Chaque bloc et séquence sont identifiées
− bloc: 1D ou 2D− séquence: 1D, 2D ou 3D
afin de simplifier l’adressage mémoire
Traitement de données multi-dimensionnels (e.g., images)
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
Host
Kernel 1
Kernel 2
Device
Grid 1
Block(0, 0)
Block(1, 0)
Block(0, 1)
Block(1, 1)
Grid 2
Courtesy: NDVIA
Figure 3.2. An Example of CUDA Thread Organization.
Block (1, 1)
Thread(0,1,0)
Thread(1,1,0)
Thread(2,1,0)
Thread(3,1,0)
Thread(0,0,0)
Thread(1,0,0)
Thread(2,0,0)
Thread(3,0,0)
(0,0,1) (1,0,1) (2,0,1) (3,0,1)
47
Fonctions noyaux
Exemple d’appel d’un noyau par CPU:
__global__ void KernelFunct(...);dim3 DimGrid(100, 50); // 5000 thread blocks
dim3 DimBlock(4, 8, 8); // 256 threads per block
size_t SharedMemBytes = 64; // 64 bytes of shared memory
KernelFunct<<< DimGrid, DimBlock, SharedMemBytes >>>(...);
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
48
Fonctions noyaux
Appel d’un noyau par CPU:− Fixer les dimensions du bloc et de la grille en passant de
paramètres
fonction_noyau‹‹‹dimGrid, dimBlock, SharedMemBytes >>>(paramètres…)
3 Modèle de programmation CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
GPA667: Conception et simulation de circuits électroniques
49
SommaireCalcul parallèle avec GPU:
1. Notions préliminaires 2. Intro. à la reconnaissance de formes3. Modèle de programmation CUDA4. Mémoires et transfert de données5. Séquences d’instructions CUDA6. Évaluation des performances
50
Réalisation pour la famille G80
Chaque séquence peut:− très rapidement…
− R/W en registres, par séquence− R/W en mém. locale, par séquence− R/W en mém partagée, par bloc
− lentement...− R/W en mém globale, par grille− R only en mém constante,
par grille (efficace que globale à cause de cache)
4 Mémoires CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
Grid
Global Memory
Block (0, 0)
Shared Memory
Thread (0, 0)
Registers
Thread (1, 0)
Registers
Block (1, 0)
Shared Memory
Thread (0, 0)
Registers
Thread (1, 0)
Registers
Host
Constant Memory
51
Déclaration de variablesles variables automatiques sans qualificatifs résident dans les registres__device__ est optionnel avec __local__, __shared__ ou __constant__
4 Mémoires CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
déclaration Memoire Visibilité Durée de vie__device__ __local__ int LocalVar; locale séquence séquence__device__ __shared__ int SharedVar; partagée bloc noyau__device__ int GlobalVar; globale grille application__device__ __constant__ int ConstantVar; constante grille application
52
Déclaration de variables
4 Mémoires CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
oui nonregistrepartagée
localeglobal
constant
53
Restrictions sur les types de variables
Pointeurs: peuvent seulement pointer vers des adresses déclarées/allouées en mémoire globale…
Deux occurrences des pointeurs en fonctions GPU− allocation par CPU, et transmis au noyau comme paramètre
__global__ void KernelFunc(float* ptr)
− assigne l’adresse d’un variable globalefloat* ptr = &GlobalVar;
4 Mémoires CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
54
Réduction des accès à la mémoire globale
Problème: la mémoire globale (DRAM) a un long temps d’accès, et la bande passante est limitée
− congestion pour l’accès aux données− les séquences progressent plus lentement− réduit l’efficacité d’exécution de noyau
Solution – utiliser la mémoire partagée pour réduire les accès à la mémoire globale
− le temps d’accès est beaucoup plus court, mais plus petite capacité
4 Mémoires CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
55
Exemple – Performance d’un G80 avec noyau pour multiplication matricielle avec blocs multiples: (voir la section 5)
toutes les séquences accèdent la mémoire globale pour leurs éléments des matrices Md et Nd
2 accès mémoire de 8B par MAC en point flottant4 B/s de bande passante mémoire par FLOPS
4 Mémoires CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
Grid
Global Memory
Block (0, 0)
Shared Memory
Thread (0, 0)
Registers
Thread (1, 0)
Registers
Block (1, 0)
Shared Memory
Thread (0, 0)
Registers
Thread (1, 0)
Registers
Host
Constant Memory
56
Exemple – Performance d’un G80 avec noyau pour multiplication matricielle avec blocs multiples:
− pour un atteindre les performances maximum en FLOPS, il faut une bande passante de 4B/s x 346.5GFLOPS =1386 GB/s
− la bande passante pour la mémoire globale (86.4 GB/s) limite le code à 21.6 GFLOPS
− on doit réduire les accès à la mémoire globale
4 Mémoires CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
Grid
Global Memory
Block (0, 0)
Shared Memory
Thread (0, 0)
Registers
Thread (1, 0)
Registers
Block (1, 0)
Shared Memory
Thread (0, 0)
Registers
Thread (1, 0)
Registers
Host
Constant Memory
57
Réduction des accès à la mémoire globaleUne stratégie – pour tirer avantage du traitement parallèle sur GPU, on partitionne les données afin d’exploiter la mémoire partagée rapide
− on partitionne les données en sous ensembles qu’on peut stocker en mémoire partagée
− on traite chaque sous-ensemble de données indépendamment, selon un bloc de séquences:1. copier un sous-ensemble de données de mémoire globale partagée,
en utilisant des séquences multiples (exploite le parallélisme niveau mémoire)
2. effectuer le traitement à partir de la mémoire partagée3. copier les résultats de mémoire partagée globale
4 Mémoires CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
58
Exemple – Utilise la mémoire partagée pour recycler les données en mémoire
globale− chaque élément en entrée est lu en séquence de longueur WIDTH
− copier chaque élément en mémoire partagée, et exécuter plusieurs séquencessur ces données locales pour réduire la bande passante
4 Mémoires CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
58
M
N
P
WID
TH
WID
TH
WIDTH WIDTH
ty
tx
58
Exemple – programme avec blocs multiples
− on peut travailler au niveau de sous-matrice
− partitionne l’exécution du noyau de telle sorte que l’accès aux données pour chaque phase focalisesur une sous-matrice de Md et Nd
4 Mémoires CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
Md
Nd
Pd
Pdsub
TILE_WIDTH
WIDTHWIDTH
TILE_WIDTHTILE_WIDTH
bx
tx01 TILE_WIDTH-12
0 1 2
by ty 210
TILE_WIDTH-1
2
1
0
TIL
E_W
IDT
HT
ILE
_WID
TH
TIL
E_W
IDTH
E
WID
TH
WID
TH
59
Exemple – à petite échelle
4 Mémoires CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
60
Pd1,0Md2,0
Md1,1
Md1,0Md0,0
Md0,1
Md3,0
Md2,1
Pd0,0
Md3,1 Pd0,1
Pd2,0Pd3,0
Nd0,3Nd1,3
Nd1,2
Nd1,1
Nd1,0Nd0,0
Nd0,1
Nd0,2
Pd1,1
Pd0,2 Pd2,2Pd3,2Pd1,2
Pd3,1Pd2,1
Pd0,3 Pd2,3Pd3,3Pd1,3
Exemple – partitionnement enen sous-matrices
4 Mémoires CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
61
Pd1,0Md2,0
Md1,1
Md1,0Md0,0
Md0,1
Md3,0
Md2,1
Pd0,0
Md3,1 Pd0,1
Pd2,0 Pd3,0
Nd0,3Nd1,3
Nd1,2
Nd1,1
Nd1,0Nd0,0
Nd0,1
Nd0,2
Pd1,1
Pd0,2 Pd2,2 Pd3,2Pd1,2
Pd3,1Pd2,1
Pd0,3 Pd2,3 Pd3,3Pd1,3
Exemple – Performances− chaque bloc devrait avoir plusieurs séquences
un TILE_WIDTH =16 donne 16 x 16 = 256 séquences
− chaque grille devrait avoir plusieurs blocs de séquences si Pd est de 1024 x 1024 (WIDTH = 1024), ceci donne
64 x 64 = 4096 blocs
− si chaque bloc occasionne 2 x 256 = 512 lectures de la mémoire globale pour effectuer 256 x (2 x 16) = 8,192 opérations MAC en point flottant
alors la bande passante à la mémoire globale n’est plus un facteur qui limite les performances
4 Mémoires CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
62
Exemple – configuration pour exécuter la fonction noyau MatrixMulKernel
4 Mémoires CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
63
// Setup the execution configuration
dim3 dimBlock(TILE_WIDTH, TILE_WIDTH);dim3 dimGrid(Width / TILE_WIDTH, Width / TILE_WIDTH);
Exemple – code CUDA
4 Mémoires CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
64
__global__ void MatrixMulKernel(float* Md, float* Nd, float* Pd, int Width){1. __shared__float Mds[TILE_WIDTH][TILE_WIDTH];2. __shared__float Nds[TILE_WIDTH][TILE_WIDTH];3. int bx = blockIdx.x; int by = blockIdx.y;4. int tx = threadIdx.x; int ty = threadIdx.y;// Identify the row and column of the Pd element to work on5. int Row = by * TILE_WIDTH + ty;6. int Col = bx * TILE_WIDTH + tx;
7. float Pvalue = 0;// Loop over the Md and Nd tiles required to compute the Pd element8. for (int m = 0; m < Width/TILE_WIDTH; ++m) {// Loading of Md and Nd tiles into shared memory9. Mds[ty][tx] = Md[Row*Width + (m*TILE_WIDTH + tx)];10. Nds[ty][tx] = Nd[Col + (m*TILE_WIDTH + ty)*Width];11. __syncthreads();12. for (int k = 0; k < TILE_WIDTH; ++k)12. Pvalue += Mds[ty][k] * Nds[k][tx];13. __synchthreads();14. }15. Pd[Row*Width+Col] = Pvalue;16. }
Exemple – programme avec blocs multiples
− chaque bloc calcul un sous-matrice carrée Pdsub de dimensions TILE_WIDTH
− chaque séquence calcul un élément de Pdsub
4 Mémoires CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
65
Md
Nd
Pd
Pdsub
TILE_WIDTH
WIDTHWIDTH
TILE_WIDTHTILE_WIDTH
bx
tx01 TILE_WIDTH-12
0 1 2
by ty 210
TILE_WIDTH-1
2
1
0
TIL
E_W
IDT
HT
ILE
_WID
TH
TIL
E_W
IDT
HE
WID
TH
WID
TH
Chaque SM du G80 a un mémoire partagée de 16KB − la dimension de SM varie selon la réalisation−pour TILE_WIDTH = 16, chaque bloc utilise 2 x 16 x 16 x
4B = 2KB de mémoire partagée− avec 8 blocs qui peuvent s’exécutés activement, on a 8 x
512 = 4,096 accès mémoire (2 par séquences, 256 séquences/block)
Avec 16 x 16 sous-matrices, on réduit les accès à la mémoire globale par un facteur de 16− nous pouvons maintenant supporter (86.4 GB/s / 4) x 16 =
347.6 GFLOPS
4 Mémoires CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
66
GPA667: Conception et simulation de circuits électroniques
67
SommaireE. Calcul parallèle avec GPU:
1. Notions préliminaires 2. Intro. à la reconnaissance de formes3. Modèle de programmation CUDA4. Mémoires et transfert de données5. Séquences d’instructions CUDA6. Évaluation des performances
68
Organisation des séquences
Lors de l’exécution d’un noyau CUDA, il y a: – la création d’une grille de séquences – toutes les séquences d’une grill exécutent la même
fonction noyau
Coordonnées de séquences:− requises afin de les distinguer – on doit leur assigner des
coordonnées uniques lors de l’exécution du code− permettent d’identifier la portion des données qui sont
traitées
5 Séquences d’instructions CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
69
Organisation des séquences
Chaque séquence est organisée selon une hiérarchie à 2 niveaux:
blockIdx threadIdx− des coordonnées uniques sont assignées aux séquences lors
de l’exécution du code noyau
− elles paraissent comme variables intégrées qu’on peut accédées dans une fonction noyau
− quand une séquence exécute la fonction noyau, les références aux variables blockIdx et threadIdx retournent les valeurs qui forment les coordonnées de la séquence
5 Séquences d’instructions CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
70
Organisation des séquencesIdentification des blocs et des séquences:Chaque séquence est identifiée par− no. blocs: 1D ou 2D− no. séquences: 1D, 2D ou 3Dafin de: 1. décider des données qui seront
traitées2. simplifier l’adressage
mémoire lors du traitement de données multi-dimensionnels (e.g., images)
5 Séquences d’instructions CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
Host
Kernel 1
Kernel 2
Device
Grid 1
Block(0, 0)
Block(1, 0)
Block(0, 1)
Block(1, 1)
Grid 2
Courtesy: NDVIA
Figure 3.2. An Example of CUDA Thread Organization.
Block (1, 1)
Thread(0,1,0)
Thread(1,1,0)
Thread(2,1,0)
Thread(3,1,0)
Thread(0,0,0)
Thread(1,0,0)
Thread(2,0,0)
Thread(3,0,0)
(0,0,1) (1,0,1) (2,0,1) (3,0,1)
71
Organisation des séquences
Niveau 1 de hiérarchie: la grille est organisée comme une matrice 2D de blocs− chaque bloc est étiqueté avec 2 composantes:
(blockIdx.x, blockIdx.y)
− toutes les séquences dans un bloc partagent la même valeur blockIdx
− variable gridDIM de type ‘struct’: paramètre spécial qui spécifie le nombre de blocs dans chaque dimension de grille
− une grille est généré à l’exécution du noyau avec ce paramètre: (gridDim.x, GridDim.y)
5 Séquences d’instructions CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
72
Organisation des séquencesNiveau 2 de hiérarchie: le bloc est organisé comme une matrice 3D de séquences− chaque séquence est étiquetée avec 3 composantes:
(threadIdx.x, threadIdx.y, threadIdx.z)
− tous les blocs ont le même dimensions et le même nombre de séquences
− variable blockDIM de type ‘struct’: paramètre spécial qui spécifie le nombre de séquences par dimension de bloc
− les blocs sont générés à l’exécution du noyau avec ce paramètre: (blockDim.x, blockDim.y, blockDim.z)
5 Séquences d’instructions CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
73
Organisation des séquences
Paramètres spéciaux : variables connus sous CUDA
Permettent de caractériser l’organisation d’une grille lors de l’exécution d’un noyau− (gridDim.x, gridDim.y): le nombre de blocs dans chaque
dimension de grille− (blockDim.x, blockDim.y, blockDim.z): le nombre de
séquences par dimension de bloc
− dim3: ces paramètres sont de type ‘struct’ à 3 champs (on fixe un champ à 1 pour négliger une dimension)
5 Séquences d’instructions CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
74
Organisation des séquences
Indices pour accéder aux séquences d’une grille:
− blocIdx.x et blocIdx.y :− indices uniques permettant de distinguer des blocs de
séquences dans une grille− threadIdx.x, threadIdx.y et threadIdx.z :
− indices uniques permettant de distinguer les séquences dans un bloc
− permet à une séquence d’accéder ses registres en temps-réel
5 Séquences d’instructions CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
75
• Exemple: fonction MatrixMulOnDevice()
5 Séquences d’instructions CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
76
Exemple: fonction MatrixMulOnDevice()(code sur CPU)
Dans l’exemple, seulement un bloc de séquences est utilisé pour le calcul des Pd:Chaque séquence:- charge un rangée i de Md
- charge un colonne j de Nd
- calcul un MAC pour chaque paire d’éléments
- la dimension de matrices est limité par le nombre maximum (512) de séquences par bloc
5 Séquences d’instructions CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
Grid 1Block 1
3 2 5 4
2
4
2
6
48
Thread(2, 2)
WIDTH
Md
Nd
Pd
77
Exemple – multiplication matricielle avec blocs multiples:
− subdivise Pd en sous-matrices carrées,
chacune calculée par un bloc de séquences− │bloc│ = │sous-matrice│< 512− séquence → pij
5 Séquences d’instructions CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
Md
Nd
Pd
Pdsub
TILE_WIDTH
WIDTHWIDTH
bx
012
0 1 2
ty210
TILE_WIDTH-1
by
2
1
0
TIL
E_W
IDT
HE
WID
TH
WID
THtx
TILE_WIDTH-1
78
Organisation des séquencesExemple – multiplication matricielle avec blocs multiples: (à petite échelle)
5 Séquences d’instructions CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
P1,0P0,0
P0,1
P2,0 P3,0
P1,1
P0,2 P2,2 P3,2P1,2
P3,1P2,1
P0,3 P2,3 P3,3P1,3
Block(0,0) Block(1,0)
Block(1,1)Block(0,1)
TILE_WIDTH = 2
79
Organisation des séquencesExemple – multiplication matricielle avec blocs multiples: (à petite échelle)
5 Séquences d’instructions CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
Pd1,0Md2,0
Md1,1
Md1,0Md0,0
Md0,1
Md3,0
Md2,1
Pd0,0
Md3,1 Pd0,1
Pd2,0 Pd3,0
Nd0,3 Nd1,3
Nd1,2
Nd1,1
Nd1,0Nd0,0
Nd0,1
Nd0,2
Pd1,1
Pd0,2 Pd2,2 Pd3,2Pd1,2
Pd3,1Pd2,1
Pd0,3 Pd2,3 Pd3,3Pd1,3
80
Organisation des séquencesExemple – noyau pour multiplication matricielle avec blocs multiples:
__global__ void MatrixMulKernel(float* Md, float* Nd, float* Pd, int Width)
{// Calculate the row index of the Pd element and Mint Row = blockIdx.y*TILE_WIDTH + threadIdx.y;// Calculate the column index of Pd and Nint Col = blockIdx.x*TILE_WIDTH + threadIdx.x;
float Pvalue = 0;// each thread computes one element of the block sub-matrixfor (int k = 0; k < Width; ++k) Pvalue += Md[Row*Width+k] * Nd[k*Width+Col];
Pd[Row*Width+Col] = Pvalue;}
5 Séquences d’instructions CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
81
Modularité des réalisations
Bloc de séquences CUDA:− le programmeur déclare un bloc:
• dimensions: de 1 à 512 séquences concurrent• forme: 1D, 2D ou 3D
− les séquences ont des threadIdx dans le bloc
− seulement les séquences d’un bloc peuvent coopérer lors de l’exécution: partager des données et synchroniser
__syncthreads()__shared__
5 Séquences d’instructions CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
Thread Id #:0 1 2 3 … m
Thread program
82
Modularité des réalisations
Fonction de synchronisation de CUDA:void __syncthreads()
− permet la coordination de toutes les séquences dans un bloc− si on l’appelle dans un noyau:
− toutes les séquences doivent atteindre l’endroit d’appel avant de poursuivre avec l’exécution
− permet d’éviter les problème liés aux accès à la mémoire globale
− les séquences doivent s’exécuter dans un temps compabable pour éviter des temps d’attentes trop longs
5 Séquences d’instructions CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
83
Modularité des réalisationsNe permet pas de synchroniser pour différents blocs− un noyau peut s’exécuter sur un nombre arbitraire de
processeurs, et rouler un même code à différentes vitesses− compromis performance-coût pour différentes applications
5 Séquences d’instructions CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
GPU
Bloc 0 Bloc 1
Bloc 2 Bloc 3
Bloc 4 Bloc 5
Bloc 6 Bloc 7
Grille noyau
Bloc 0 Bloc 1
Bloc 2 Bloc 3
Bloc 4 Bloc 5
Bloc 6 Bloc 7
GPU
Bloc 0 Bloc 1 Bloc 2 Bloc 3
Bloc 4 Bloc 5 Bloc 6 Bloc 7
Un bloc peut s’exécuter dans un ordre arbitraire par rapport aux autres blocs, sans attente
timetime
84
Assignation de séquences Les séquences d’une grille sont assignées aux ressources d’exécution (les SP de SM) bloc par blocExemple: exécution de blocs avec le G80 (GeForce 8800GTX)
5 Séquences d’instructions CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
t0 t1 t2 … tm
Blocs
SP
SharedMemory
MT IU
SP
SharedMemory
MT IUt0 t1 t2 … tm
Blocs
SM 1SM 0
85
Assignation de séquences
Les séquences sont assignées aux SM (‘Streaming Multiprocessors’) au niveau du bloc:− permet jusqu’à 8 blocs par SM (i.e., 8 SP/SM)− 128 blocs peuvent être assignés à l’ensemble des 16 SM− un SM peut gérer jusqu’à 768 séquences, soit
− 256 séquences/bloc x 3 bloc− 128 séquences/bloc x 6 bloc, etc.
Les séquences s’exécutent de façon concurrentes:− le SM maintien l’identité des blocs et des séquences− le SM gère, et voit à l’ordonnancement d’exécution des
séquences
5 Séquences d’instructions CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
86
Ordonnancement des séquencesExemple: exécution de blocs avec le G80 (GeForce 8800GTX)
5 Séquences d’instructions CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
…t0 t1 t2 …
t31
……t0 t1 t2 …
t31
…Block 1 Warps Block 2 Warps
SP
SP
SP
SP
SFU
SP
SP
SP
SP
SFU
Instruction Fetch/Dispatch
Instruction L1Streaming Multiprocessor
Shared Memory
…t0 t1 t2 …
t31
…Block 1 Warps
87
Ordonnancement des séquencesChaque bloc est exécuté en termes de ‘warps’ de 32 séquences: − Ceci est un décision liée à l’implémentation, et non un élément
du modèle de programmation CUDA− Warp ≡ unité d’ordonnancement au SM
Q: Combien de warps résident dans un SM si on lui assigne 3 blocs, et chaque bloc comporte 256 séquences?– Bloc: (256 séq./bloc) / (32 séq./warp) = 8 warps par bloc– SM: (8 warps/bloc) x (3 blocs/SM) = 24 warps par SM– correspond au maximum: (768 séq./SM) / (32 séq./warp) = 24
5 Séquences d’instructions CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
88
Ordonnancement des séquencesL’ordonnancement des warps dans les SM:– à un certain temps, 24 warps max peuvent résider sur un SM
– maximise le débit: afin d’exécuter plusieurs opérations sans latence (accès à la mémoire globale), les SM sont conçus pour exécuter un seul warp à la fois
– exécution des warps:– si les opérandes liées à la prochaine instruction sont prêtes, un warp
devient éligible pour l’exécution– les warps éligibles sont sélectionnés pour l’exécution selon une
politique de priorités– toutes les séquences d’un warp exécutent la même instruction
lorsqu’elles sont sélectionnées
5 Séquences d’instructions CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
89
Ordonnancement des séquencesExemple d’ordonnancement des warps résident sur un SM dans le temps– case: exécution d’une instruction– 3 blocs de séquences, jaune, bleu et blanc, avec de warps
mutiples– ‘zero-overhead thread scheduling’
5 Séquences d’instructions CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
TB1W1
TB = Thread Block, W = Warp
TB2W1
TB3W1
TB2W1
TB1W1
TB3W2
TB1W2
TB1W3
TB3W2
Time
TB1, W1 stallTB3, W2 stallTB2, W1 stall
Instruction: 1 2 3 4 5 6 1 2 1 2 3 41 2 7 8 1 2 1 2 3 4
90
Structure typique d’un programme CUDA
5 Séquences d’instructions CUDA
GPA667 Section E: Calcul parallèle avec GPUÉric Granger
Global variables declaration__host____device__... __global__, __constant__, __texture__
Function prototypes__global__ void kernelOne(…)float handyFunction(…)
Main ()allocate memory space on the device – cudaMalloc(&d_GlblVarPtr, bytes )transfer data from host to device – cudaMemCpy(d_GlblVarPtr, h_Gl…)execution configuration setupkernel call – kernelOne<<<execution configuration>>>( args… );transfer results from device to host – cudaMemCpy(h_GlblVarPtr,…)optional: compare against golden (host computed) solution
Kernel – void kernelOne(type args,…)variables declaration - __local__, __shared__
automatic variables transparently assigned to registers or local memorysyncthreads()…
Other functionsfloat handyFunction(int inVar…);
Top Related