Un GPU lance des milliers de fils
Un GPU (processeur graphique) est une puce qui exécute des milliers de petits calculs en même temps. Il est construit pour appliquer la même instruction à beaucoup de données, ce qui convient aux images et aux tableaux de nombres.
Une image de 1920 par 1080 pixels compte 2 073 600 pixels, et chacun se calcule seul. Le labo n’a pas de GPU : le script kernel.py simule un lancement en Python, pour montrer comment les fils s’organisent.
À la fin de cette leçon
Remarque
Je peux dire comment un GPU organise ses fils en blocs et en grille.
Mots clés
| Mot | Ce que ça veut dire |
|---|---|
| GPU | une puce faite pour lancer des milliers de petits calculs |
| Fil | le plus petit travailleur : il traite un seul élément |
| Bloc | un groupe de fils qui partent ensemble |
| Grille | l’ensemble des blocs d’un lancement |
| Noyau | la fonction que chaque fil exécute |
| Indice global | le numéro de l’élément dont un fil s’occupe |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Une grille est faite de blocs de fils
Quand on lance un calcul sur un GPU, on choisit deux nombres : combien de blocs, et combien de fils par bloc. L’ensemble forme la grille.
Chaque fil exécute le même noyau, mais sur un élément différent. Dans le labo, la grille compte 3 blocs de 4 fils, soit 12 fils.
Chaque fil calcule son indice global
Chaque fil connaît son numéro de bloc et son numéro dans le bloc. Il en déduit l’élément qui lui revient : indice = bloc × taille du bloc + fil.
Avec des blocs de 4 fils, le fil 1 du bloc 2 traite l’élément 2 × 4 + 1, soit 9.
Calculer les indices d’une grille
# Un lancement de 3 blocs de 4 fils : l'indice global de chaque fil.
taille_bloc = 4
nombre_blocs = 3
for bloc in range(nombre_blocs):
for fil in range(taille_bloc):
indice = bloc * taille_bloc + fil
print("bloc", bloc, "fil", fil, "-> indice", indice)
# Changez taille_bloc en 8 : les indices vont jusqu'à 23.
Le labo simule un lancement de douze fils
Le script kernel.py simule un lancement de 3 blocs de 4 fils pour additionner deux listes de 10 nombres. Il affiche une ligne par fil, avec son indice global, de 0 à 11.
Le script affiche des mots en anglais : « block » veut dire bloc, « thread » veut dire fil, et « global index » veut dire indice global.
Lancer la simulation du labo
docker exec jupyter python3 steps/kernel.pyAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Exemple : ce que le lancement fait
| Bloc | Fils | Indices globaux | Ce qui se passe |
|---|---|---|---|
| 0 | 0 à 3 | 0 à 3 | Chaque fil additionne sa case |
| 1 | 0 à 3 | 4 à 7 | Chaque fil additionne sa case |
| 2 | 0 et 1 | 8 et 9 | Chaque fil additionne sa case |
| 2 | 2 et 3 | 10 et 11 | Au-delà de la fin : rien à faire |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Assez de blocs pour couvrir les données
Pour 10 éléments et des blocs de 4 fils, deux blocs ne couvrent que 8 éléments. Il en faut trois, soit 12 fils : deux fils n’ont rien à faire.
Le nombre de blocs est la division de n par la taille du bloc, arrondie vers le haut. Sans fonction d’arrondi, on écrit (n + taille - 1) // taille.
Calculer le nombre de blocs
docker exec jupyter python3 -c "print((10 + 4 - 1) // 4)"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Une erreur fréquente
Attention
On croit souvent qu’un fil de GPU est un petit cœur de processeur, puissant et indépendant. Un fil de GPU est très simple et traite un seul élément. La force d’un GPU vient de leur nombre, et du fait que beaucoup de fils font la même chose.