Objectifs du BE :
Ce BE a pour objectif de pratiquer la programmation d'un GPU au sein d'un noeud de calcul CPU+GPU : il consiste à
implanter un premier produit de matrices denses sur un GPU. On développera différents "kernels", et pour
chacun on mesurera les performances d'un produit de matrices denses sur
un GPU, en fonction de la granularité de la grille de blocs de
threads. On étudiera la qualité de la "coalescence" de chaque version
du kernel, pour identifier les solutions les plus intéressantes. Enfin,
on
comparera les performances obtenues sur GPU avec celle obtenues sur le
CPU.
Plate-forme de développement :
Les machines
utilisées seront celles des clusters Cameron ou Tx de CentraleSupélec :
- Tx : chaque machine contient un CPU
Intel XEON quad-core hyperthreadés, et un GPU NVIDIA RTX2080 (architecture Turing)
- Cameron : chaque machine contient un CPU
Intel XEON hexa-core hyperthreadés, et un GPU NVIDIA GTX1080 (architecture Pascal)
Vous
utiliserez les comptes de TP "
gpucs1_1" à "
gpucs1_20". Depuis votre poste de travail vous vous
connecterez par ssh sur la machine
ghome.metz.supelec.fr, puis vous vous
connecterez à la machine
term2.grid.metz.supelec.fr où vous réserverez
UN noeud sur l'un des clusters à l'aide des commandes OAR.
- pour réserver UN noeuds sur Tx pour 4h depuis Term2 : oarsub -q day -p "cluster='Tx'" -l nodes=1,walltime=4:00:00 -I
- pour réserver UN noeuds sur Cameron pour 4h depuis Term2 : oarsub -q day -p "cluster='Cameron'" -l nodes=1,walltime=4:00:00 -I
Documents à rendre :
Vous rendrez un SEUL document par binôme : un fichier Word (.doc) ou PDF :
- il sera nommé avec vos noms précédés
de "BE3-CUDA". Ex : "BE3-CUDA-Pignon.pdf"
- il comportera sur la première page :
- vos nom et prénoms, et le nom de votre option de 3A,
- la date de remise du rapport,
- le titre ("BE de programmation CUDA sur GPU"),
- ... et le début de votre rapport.
- il contiendra en général :
- pour chaque étape : une description de la parallélisation réalisée (pourquoi ? comment ?),
- les
codes sources des parties importantes du code
à chaque étape (ex : les routines de calcul et de communication).vos
mesures de performances (Texec, GigaFlops, SpeedUp) sur un cluster de
PC, pour les plus performants de vos codes,
- des graphiques de synthèse représentant les
performances fonction du nombre de PC utilisés et
éventuellement de la taille du problème,
- votre analyse/synthèse des performances obtenues,
- Taille maximale de vos rapports : <voir taille maximale annoncée en cours>
- Date limite de remise de vos rapports : <voir date annoncée en cours>
- Remise par e-mail à Stephane Vialle (Stephane.Vialle@centralesupelec.fr)
Travail à effectuer :
Remarques préliminaires :
- Le squelette de programme que vous utiliserez contient un code de produit de matrices denses en OpenMP et CUDA.
- La
partie OpenMP est complète, et servira de calcul de référence. Elle est destiner à permettre de vérifier les résultats obtenus en CUDA.
- La partie CUDA est en partie développée, mais il vous reste à compléter le fichier gpu.cu
- Le squelette est compilable et contient une aide intégrée : exécutez 'make' puis './MatrixProduct -h'.
- Pour valider votre premier code vous compilerez en Double Précision (le type "T_real" devient le type "double") avec "-DDP" dans le Makefile:
- les résultats seront identiques sur CPU et sur GPU,
- mais les performances des GPU
seront faibles (car il s'agit de cartes GPU grand public non adaptées à la Double Précision).
- Pour
faire vos mesures de performances vous compilerez en Simple Précision
(le type "T_real" devient le type "float") avec "#-DDP" dans le
Makefile.
- La simple précision est adaptée
aux capacités des GeForce GTX1080 et TRX2080,
- mais il se peut que vous observiez des
différences entre les calculs sur CPU et sur GPU!!
1 - Implantation d'un kernel K0 déployé par une grille "2D" de blocs "1D" :- Récupérez le code source OpenMP+CUDA à compléter :
- ou bien recopiez le sur votre compte de TP par la commande : cp ~vialle/tmp/MatrixProduct-CUDA-enonce.zip .
Récupérez aussi le
fichier Excel de saisi des résultats, et complétez-le au fur et à mesure du TP.
Compilez ce code et
testez son fonctionnement sur CPU (commencez par exécuter la commande
./MatrixProduct -h).
- Complétez les routines 'gpuSetDataOnGPU' et 'gpuGetResultOnCPU'qui réalisent des transferts CPU/GPU. Vous devez compléter ces routines avec des appels à 'cudaMemcpyFromSymbol' et 'cudaMemcpyToSymbol'.
Toutes les routines à compléter se trouvent dans le fichier 'gpu.cu', et la taille des matrices et des blocs sont définies dans le fichier 'main.h'.
- Implantez le kernel K0 et sa grille "2D" de blocs "1D" de threads, pour que :
- chaque thread calcule un élément complet de la matrice C (matrice résultat),
- un bloc de threads soit un segment 1D de threads selon la dimension X des blocs,
- les colonnes successives d'une ligne de C soient calculées par des threads consécutifs en X au sein d'un bloc,
- les lignes successives de C seront traitées par des blocs 1D différents.
Testez
votre implantation sur une matrice de
1024x1024 DOUBLE (option -DDP active dans le Makefile), et vérifiez que vous obtenez les mêmes valeurs que sur
CPU (MatrixProduct -t CPU -cpu-k 0 -cpu-nt 8 sur Tx et -cpu-nt 12 sur Cameron)
- Puis completez le kernel K0 (et sa grille de blocs de threads) pour que votre implantation fonctionne sur une matrice de taille non multiple de la taille des blocs.
Testez votre kernel K0 sur une matrice de 1025x1025 DOUBLE (vérifiez que vous obtenez les mêmes valeurs que sur
CPUs).
- Une fois votre kernel K0 au point, mesurez les performances obtenues sur une matrice de 4096x4096 FLOAT éléments (#-DDP dans le Makefile).
- Faites
varier la taille de vos blocs 1D de threads, et mesurer les
performances obtenues pour des blocs de 32 à 1024 threads.
- Puis faites varier la taille des blocs de 32 à 4 threads.
- Est-ce que la courbe de performance obtenue semble conforme à la théorie ? pourquoi ?
- Comparez aux meilleurs performances obtenues sur CPU multi-coeurs en OpenMP avec un kernel de même niveau (kernel 0),
- MatrixProduct -t CPU -cpu-k 0 -cpu-nt 8 sur Tx
- MatrixProduct -t CPU -cpu-k 0 -cpu-nt 12 sur Cameron
Calculez le speedup GPU vs CPU.
2 - Etude de la coalescence du kernel K0 :
- En ayant utilisé K0 avec les matrices A et B, quelle était :
- la coalescence des lectures de A,
- la coalescence des lectures de B,
- la coalescence des écritures dans C
- En adaptant K0 pour utiliser des matrices transposées :
- quelle serait la coalescence en utilisant la transposée de A ?
- quelle serait la colaescence en utilisant la transposée de B ?
- En adaptant K0 pour que chaque bloc traite des lignes successives d'une même colonne de C.
- en utilisant A et B ?
- en utilisant la transposée de A ?
- en utilisant la transposée de B ?
- Faites les tests en modifiant rapidement K0 :
- intervertisez simplement les calculs des numéros de ligne et de
colonne du thread dans K0,
- continuez à utiliser A et B,
- la performance évolue-t-elle dans le sens prévu ?
3- Implantation d'un kernel K1 utilisant une grille "2D" de blocs et kernels "2D" :
- Créez le kernel K1 en généralisant votre kernel K0 (et sa grille de blocs) pour qu'il supporte des blocs "2D" de threads.
Testez
votre implantation sur une matrice de
1024x1024 éléments, puis sur une matrice de 1025x1025 éléments : vérifiez que vous obtenez les mêmes valeurs qu'avec le kernel K0 (attention : la taille maximale d'un bloc est de 1024 threads).
- Une fois que votre programme est au point, mesurez les performances obtenues sur une matrice de 4096x4096 FLOAT.
Mesurez
les performances obtenues avec des blocs "XxY" pour des tailles
correspondant en priorité aux cases jaunes du fichier Excel.
- Comparez ces performances
à celles obtenues avec des blocs 1D du même nombre total de threads, comparez
aux meilleurs performances obtenues sur CPU multi-coeurs en OpenMP,
et calculez le speedup GPU vs CPU.