CORSO HPC: PROGRAMMAZIONE GPU
Alessandro Dal Palù1
1. Dipartimento di Scienze Matematiche, Fisiche e Informatiche,Università di Parma
14 Giu 2018
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 1 / 32
OBIETTIVI
Architettura GPUModello di programmazioneIl prodotto tra matrici (CPU)Esempio implementazione GPUUtilizzo Shared Memory su GPUConfronti
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 2 / 32
ARCHITETTURA GPUDOCUMENTAZIONE
Approfondimenti:http://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html
http://www.es.ele.tue.nl/~mwijtvliet/5KK73/?page=mmcuda
Ci riferiamo al modello CUDA C (definito da Nvidia)
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 3 / 32
ARCHITETTURA GPU
THREAD
Unità elementare di calcoloEsegue un flusso di esecuzionePuò essere lanciato in parallelo ad altri threadIl suo comportamento è programmato da un kernel
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 4 / 32
ARCHITETTURA GPU
BLOCCO
I thread (max 1024) sono raggruppati in blocchiI thread di un blocco sono eseguiti in paralleloI thread del blocco eseguono le stesse istruzioni (idea SIMD)A seconda dei processori presenti, più blocchi eseguiti in parallelo
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 5 / 32
ARCHITETTURA GPUORGANIZZAZIONE
I thread sono organizzati 1D, 2D o 3D nel bloccoI blocchi sono organizzati su una griglia 1D, 2D o 3DBlocchi diversi possono essere eseguiti da processori diversiNon c’è garanzia sull’ordine di esecuzione dei blocchi
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 6 / 32
ARCHITETTURA GPUORGANIZZAZIONE
threadIdx: identifica il thread all’interno del blocco (.x, .y, .z)blockIdx: identifica il blocco all’interno della griglia (.x, .y, .z)blockDim: descrive quanti thread ci sono in un blocco (.x, .y, .z)gridDim: descrive quanti blocchi ci sono nella griglia (.x, .y, .z)
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 7 / 32
ARCHITETTURA GPUMEMORIE
Le memorie presenti su una GPU sono di diverso tipoIl loro utilizzo ha un impatto notevole sui trasferimenti datiNecessaria una strutturazione dell’algoritmo parallelo permassimizzare prestazioni
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 8 / 32
ARCHITETTURA GPUMEMORIE P100
255 registri per thread64KB memoria condivisa perblocco (veloce se non ci sonoconflitti di accesso)12GB memoria globale(tempo accesso alto,trasferimenti a blocchi agrande larghezza di banda)
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 9 / 32
ARCHITETTURA GPUESEMPIO DI CALCOLO
Il programma gira su CPU(host)Il programma chiede alla GPU(device) di eseguire un kernelLa CPU può fare altro duranteil lavoro della GPUE’ possibile lanciare piùkernel in parallelo (se non cisono dipendenze)La CPU colleziona risultatidalla GPU e prosegue
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 10 / 32
CALCOLO PRODOTTO MATRICI
Prodotto C = A × B (A dimensione M x K, B dimensione K x N)Ogni cella di C richiede il prodotto scalare di due vettoriOgni cella di A o B viene letta K volte
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 11 / 32
CALCOLO PRODOTTO MATRICIIMPLEMENTAZIONE CPU
void main(){for i = 0 to M do
for j = 0 to N do/* compute element C(i,j) */for k = 0 to K do
C(i,j) = C(i,j) + A(i,k) * B(k,j)end
endend
}
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 12 / 32
CALCOLO PRODOTTO MATRICIPARALLELIZZAZIONE SU GPU: IDEA NAIVE
Ogni thread calcola una cella di C(i , j)Ogni thread carica una riga di A e una colonna di B dalla memoriaglobaleIl thread calcola il prodotto e scrive il risultato nella cella inmemoria globale
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 13 / 32
CALCOLO PRODOTTO MATRICIPARALLELIZZAZIONE SU GPU: IDEA NAIVE
Bisogna dividere il lavoro in blocchi da 1024 threadViene comodo lavorare in 2DOgni blocco può lavorare in modo indipendente dagli altri
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 14 / 32
CALCOLO PRODOTTO MATRICIPARALLELIZZAZIONE SU GPU: IDEA NAIVE - CPU
void main(){define A_cpu, B_cpu, C_cpu in the CPU memorydefine A_gpu, B_gpu, C_gpu in the GPU memorymemcopy A_cpu to A_gpumemcopy B_cpu to B_gpudim3 dimBlock(32, 32)dim3 dimGrid(N/dimBlock.x, M/dimBlock.y)matrixMul<<<dimGrid, dimBlock>>>(A_gpu,B_gpu,C_gpu,K)memcopy C_gpu to C_cpu
}
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 15 / 32
CALCOLO PRODOTTO MATRICIPARALLELIZZAZIONE SU GPU: IDEA NAIVE - GPU
__global__ void matrixMul(A_gpu,B_gpu,C_gpu,K){temp = 0// Row i of matrix Ci = blockIdx.y * blockDim.y + threadIdx.y// Column j of matrix Cj = blockIdx.x * blockDim.x + threadIdx.xfor k = 0 to K-1 do
accu = accu + A_gpu(i,k) * B_gpu(k,j)C_gpu(i,j) = accu
}
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 16 / 32
CALCOLO PRODOTTO MATRICIPARALLELIZZAZIONE SU GPU: IDEA NAIVE
Critiche: troppe letture da memoria globaleSe i dati sono contigui e letti da thread contigui, buone prestazioniTuttavia la matrice B non viene letta con celle contigue(inefficiente)Proposta: memorizzo B trasposta (memory coalescing)
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 17 / 32
CALCOLO PRODOTTO MATRICIPARALLELIZZAZIONE SU GPU: IDEA NAIVE
Critiche: troppe letture da memoria globaleSoluzione: sfrutto la memoria condivisa del bloccoEvito di leggere tante volte gli stessi datiOrganizzo il calcolo parallelo in modo diverso
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 18 / 32
CALCOLO PRODOTTO MATRICIPARALLELIZZAZIONE SU GPU: MEMORIA CONDIVISA
Divido le matrici in blocchi (tiles)Calcolo ogni blocco di C in piu’ fasi
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 19 / 32
CALCOLO PRODOTTO MATRICIPARALLELIZZAZIONE SU GPU: MEMORIA CONDIVISA
Ad ogni iterazione, ogni thread copia un blocco di A e uno di Bdalla memoria globale in sharedOgni thread calcola il prodotto e aggiorna il risultato in un registroAl termine delle iterazioni, tutti i thread memorizzano il lororisultato (blocco di C) in memoria globale
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 20 / 32
CALCOLO PRODOTTO MATRICIPARALLELIZZAZIONE SU GPU: MEMORIA CONDIVISA
Prima coppia di blocchi
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 21 / 32
CALCOLO PRODOTTO MATRICIPARALLELIZZAZIONE SU GPU: MEMORIA CONDIVISA
Ultima coppia di blocchi
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 22 / 32
CALCOLO PRODOTTO MATRICIPARALLELIZZAZIONE SU GPU: MEMORIA CONDIVISA - GPU
__global__ void matrixMul(A_gpu,B_gpu,C_gpu,K){__shared__ float A_tile(blockDim.y, blockDim.x)__shared__ float B_tile(blockDim.x, blockDim.y)accu = 0for tileIdx = 0 to (K/blockDim.x - 1) do
//carica blocchi di A e B in shared memi = blockIdx.y * blockDim.y + threadIdx.yj = tileIdx * blockDim.x + threadIdx.xA_tile(threadIdx.y, threadIdx.x) = A_gpu(i,j)B_tile(threadIdx.x, threadIdx.y) = B_gpu(j,i)__sync()
...
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 23 / 32
CALCOLO PRODOTTO MATRICIPARALLELIZZAZIONE SU GPU: MEMORIA CONDIVISA - GPU
__global__ void matrixMul(A_gpu,B_gpu,C_gpu,K){...for tileIdx = 0 to (K/blockDim.x - 1) do//carica blocchi di A e B in shared mem
// Prodotto scalare (accumulato)for k = 0 to blockDim.x doaccu = accu + A_tile(thrIdx.y,k) * B_tile(k,thrIdx.x)
end__sync()
end
//scrive il blocco in globali = blockIdx.y * blockDim.y + threadIdx.yj = blockIdx.x * blockDim.x + threadIdx.xC_gpu(i,j) = accu
}
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 24 / 32
CALCOLO PRODOTTO MATRICIPARALLELIZZAZIONE SU GPU: MEMORIA CONDIVISA
Le operazioni di calcolo (somme, moltiplicazioni) sono invariateGli accessi alla memoria globale sono stati divisi per ladimensione del bloccoInfatti un blocco di una matrice è letto una volta dalla globale eusato dalla shared più volte per calcolare il blocco C
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 25 / 32
SETUPPREPARARE L’AMBIENTE
Per poter generare gli eseguibili per una GPU, bisogna prepararel’ambienteLanciare > module load cuda
I files della lezione sono in/hpc/home/alessandro.dalpalu/lezione6/cuda.tar.gz
Lanciarecdmkdir lezione6cd lezione6cp /hpc/home/alessandro.dalpalu/lezione6/cuda.tar.gz .tar xzf cuda.tar.gz
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 26 / 32
SETUPPREPARARE L’AMBIENTE
Usiamo un gestore della compilazione (Cmake)Lanciarecd bincmake ..makecd ..
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 27 / 32
PROVARE L’ESEMPIO
> cat slurm_launch_single
#!/bin/sh#SBATCH --partition=gpu # Richiedi un nodo con una gpu#SBATCH --nodes=1#SBATCH --gres=gpu:tesla:1#SBATCH --mem=4G# Dichiara che il job durera’ al massimo 1 minuto#SBATCH --time=0-00:01:00#stampa il nome del nodo assegnato e argomentiecho "#SLURM_JOB_NODELIST : $SLURM_JOB_NODELIST"echo "#CUDA_VISIBLE_DEVICES : $CUDA_VISIBLE_DEVICES"echo "size A= $WA X $HA,B= $WB X $HB"module load cuda #esegui il programma./bin/matrixMul -wA=$WA -hA=$HA -wB=$WB -hB=$HB
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 28 / 32
PROVARE L’ESEMPIO
Lo script prevede dei parametri da passare al programmaE’ possibile provare diverse dimensioni delle matricisbatch --export=WA=100,HA=100,WB=100,HB=1000--reservation=corso_hpc18a slurm_launch_single
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 29 / 32
PROVARE L’ESEMPIO
Lo script go lancia diverse dimensioni da confrontare
> cat go
#!/bin/bashfor i in $(seq 100 100 1000); dosbatch --export=WA=$i,HA=$i,WB=$i,HB=$i \--reservation=corso_hpc18a slurm\_launch\_single
done
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 30 / 32
PROVARE L’ESEMPIO
Lanciare lo script goI risultati saranno scritti su vari file.Ogni esecuzione prova diverse idee (alcune non presentate oggi)Per estrarre le performances di un esperimento (es. Tiling):
cat *.o* | grep -A1 "Tiling GPU" | grep time > tiling.txt
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 31 / 32
CONSEGNA
Estrarre le prestazioni per CPU, Naive GPU, coalescing GPU eTiling GPU.Caricare i 4 files su ellyOpzionale: adattare lo script della prima lezione per produrre ungrafico con le 4 serie di dati
A. DAL PALÙ CORSO HPC: PROGRAMMAZIONE GPU 14/6/18 32 / 32