J'utilise CUDA depuis quelques semaines, mais j'ai quelques doutes sur l'allocation des blocs / warps / thread. J'étudie l'architecture d'un point de vue didactique (projet universitaire), donc atteindre des performances optimales n'est pas ma préoccupation.
Tout d'abord, j'aimerais comprendre si j'ai bien compris ces faits:
Le programmeur écrit un noyau et organise son exécution dans une grille de blocs de threads.
Chaque bloc est affecté à un multiprocesseur de streaming (SM). Une fois attribué, il ne peut pas migrer vers un autre SM.
Chaque SM divise ses propres blocs en Warps (actuellement avec une taille maximale de 32 threads). Tous les threads d'un warp s'exécutent simultanément sur les ressources du SM.
L'exécution réelle d'un thread est effectuée par les cœurs CUDA contenus dans le SM. Il n'y a pas de mappage spécifique entre les threads et les cœurs.
Si une chaîne contient 20 threads, mais qu'il n'y a actuellement que 16 cœurs disponibles, la chaîne ne fonctionnera pas.
Par contre si un bloc contient 48 threads, il sera divisé en 2 warps et ils s'exécuteront en parallèle à condition que suffisamment de mémoire soit disponible.
Si un thread démarre sur un cœur, alors il est bloqué pour l'accès à la mémoire ou pour une longue opération en virgule flottante, son exécution peut reprendre sur un cœur différent.
Ont-ils raison?
Maintenant, j'ai une GeForce 560 Ti donc selon les spécifications, elle est équipée de 8 SM, contenant chacun 48 cœurs CUDA (384 cœurs au total).
Mon objectif est de m'assurer que chaque cœur de l'architecture exécute les MÊMES instructions. En supposant que mon code ne nécessitera pas plus de registres que ceux disponibles dans chaque SM, j'ai imaginé différentes approches:
Je crée 8 blocs de 48 threads chacun, de sorte que chaque SM ait 1 bloc à exécuter. Dans ce cas, les 48 threads s'exécuteront-ils en parallèle dans le SM (en exploitant tous les 48 cœurs disponibles pour eux)?
Y a-t-il une différence si je lance 64 blocs de 6 threads? (En supposant qu'ils seront mappés uniformément parmi les SM)
Si je "submerge" le GPU dans un travail planifié (en créant 1024 blocs de 1024 threads chacun, par exemple), est-il raisonnable de supposer que tous les cœurs seront utilisés à un certain moment et effectueront les mêmes calculs (en supposant que les threads ne jamais caler)?
Existe-t-il un moyen de vérifier ces situations à l'aide du profileur?
Y a-t-il une référence pour ce truc? J'ai lu le guide de programmation CUDA et les chapitres consacrés à l'architecture matérielle dans "Programming Massively Parallel Processors" et "CUDA Application design and development"; mais je n'ai pas pu obtenir de réponse précise.
la source
Réponses:
Deux des meilleures références sont
Je vais essayer de répondre à chacune de vos questions.
Le programmeur divise le travail en threads, les threads en blocs de threads et les blocs de thread en grilles. Le distributeur de travail de calcul alloue des blocs de thread aux multiprocesseurs de streaming (SM). Une fois qu'un bloc de thread est distribué à un SM, les ressources pour le bloc de thread sont allouées (warps et mémoire partagée) et les threads sont divisés en groupes de 32 threads appelés warps. Une fois qu'une chaîne est allouée, elle est appelée une chaîne active. Les deux planificateurs de distorsion choisissent deux déformations actives par cycle et envoient des déformations aux unités d'exécution. Pour plus de détails sur les unités d'exécution et la répartition des instructions, voir 1 p.7-10 et 2 .
4 ' . Il existe un mappage entre laneid (index des threads dans une chaîne) et un noyau.
5 ' . Si une chaîne contient moins de 32 threads, elle sera dans la plupart des cas exécutée de la même manière que si elle avait 32 threads. Les warps peuvent avoir moins de 32 threads actifs pour plusieurs raisons: le nombre de threads par bloc n'est pas divisible par 32, le programme exécute un bloc divergent donc les threads qui n'ont pas emprunté le chemin actuel sont marqués comme inactifs, ou un thread du warp est sorti.
6 ' . Un bloc de thread sera divisé en WarpsPerBlock = (ThreadsPerBlock + WarpSize - 1) / WarpSize Il n'est pas nécessaire que les planificateurs de chaîne sélectionnent deux warps dans le même bloc de thread.
7 ' . Une unité d'exécution ne se bloquera pas sur une opération de mémoire. Si une ressource n'est pas disponible lorsqu'une instruction est prête à être envoyée, l'instruction sera à nouveau distribuée dans le futur lorsque la ressource sera disponible. Les warps peuvent caler au niveau des barrières, sur les opérations de mémoire, les opérations de texture, les dépendances de données, ... Un warp bloqué ne peut pas être sélectionné par le planificateur de warp. Sur Fermi, il est utile d'avoir au moins 2 warps éligibles par cycle afin que le planificateur de warp puisse émettre une instruction.
Voir la référence 2 pour les différences entre une GTX480 et une GTX560.
Si vous lisez le matériel de référence (quelques minutes), je pense que vous constaterez que votre objectif n'a pas de sens. Je vais essayer de répondre à vos points.
1 ' . Si vous lancez le noyau <<< 8, 48 >>> vous obtiendrez 8 blocs chacun avec 2 warps de 32 et 16 threads. Il n'y a aucune garantie que ces 8 blocs seront attribués à différents SM. Si 2 blocs sont alloués à un SM, alors il est possible que chaque programmateur de chaîne puisse sélectionner une chaîne et exécuter la chaîne. Vous n'utiliserez que 32 des 48 cœurs.
2 ' . Il y a une grande différence entre 8 blocs de 48 threads et 64 blocs de 6 threads. Supposons que votre noyau n'a pas de divergence et que chaque thread exécute 10 instructions.
Afin d'obtenir une efficacité optimale, la division du travail doit être en multiples de 32 threads. Le matériel ne fusionnera pas les threads de différentes déformations.
3 ' . Un GTX560 peut avoir 8 blocs SM * 8 = 64 blocs à la fois ou 8 SM * 48 warps = 512 warps si le noyau n'atteint pas le maximum de registres ou de mémoire partagée. A tout moment, une partie du travail sera active sur les SM. Chaque SM a plusieurs unités d'exécution (plus de cœurs CUDA). Les ressources utilisées à un moment donné dépendent des ordonnanceurs de distorsion et du mélange d'instructions de l'application. Si vous n'effectuez pas d'opérations TEX, les unités TEX seront inactives. Si vous n'effectuez pas d'opération spéciale en virgule flottante, les unités SUFU seront inactives.
4 ' . Parallel Nsight et le spectacle Visual Profiler
une. IPC exécuté
b. IPC émis
c. déformations actives par cycle actif
ré. Warps éligibles par cycle actif (Nsight uniquement)
e. raisons de décrochage de la chaîne (Nsight uniquement)
F. threads actifs par instruction exécutée
Le profileur n'affiche le pourcentage d'utilisation d'aucune des unités d'exécution. Pour GTX560 une estimation approximative serait IssuedIPC / MaxIPC. Pour MaxIPC, supposons que GF100 (GTX480) est 2 GF10x (GTX560) est 4 mais que la cible est 3 est une meilleure cible.
la source
"E. Si une chaîne contient 20 threads, mais qu'il n'y a actuellement que 16 cœurs disponibles, la chaîne ne fonctionnera pas."
est incorrect. Vous confondez les cœurs dans leur sens habituel (également utilisé dans les processeurs) - le nombre de "multiprocesseurs" dans un GPU, avec les cœurs dans le marketing nVIDIA ("notre carte a des milliers de cœurs CUDA").
Un warp lui-même ne peut être planifié que sur un seul cœur (= multiprocesseur) et peut exécuter jusqu'à 32 threads en même temps; il ne peut pas utiliser plus d'un seul cœur.
Le nombre «48 warps» est le nombre maximum de warps actifs (warps qui peuvent être choisis pour être programmés pour le travail dans le prochain cycle, à n'importe quel cycle donné) par multiprocesseur, sur les GPU nVIDIA avec Compute Capability 2.x; et ce nombre correspond à 1536 = 48 x 32 fils.
Réponse basée sur ce webinaire
la source