Leçon 26 : Multiplication de matrices — le kernel GPU naïf
La multiplication de matrices C = A * B calcule chaque élément C[row][col] comme une somme de produits le long de la dimension partagée K : C[row][col] = somme sur k de A[row][k] * B[k][col]. La façon directe (naïve) de paralléliser ça sur un GPU est d'attribuer un thread à chaque élément de C : le
Imagine une énorme grille de multiplication à remplir. Dans la version naïve, chaque ouvrier est responsable d'une seule case, mais pour la remplir il marche jusqu'à un entrepôt éloigné et ramène toute une ligne et toute une colonne — et l'ouvrier d'à côté marche jusqu'au même entrepôt et ramène exactement la même ligne à nouveau. De nombreux trajets gaspillés.
- mappage thread-vers-élément
- Chaque thread possède un élément de C. Il calcule row et col à partir des indices 2D et produit C[row][col].
- la boucle k (produit scalaire)
- La boucle sur k qui accumule sum += A[row*K + k] * B[k*N + col]. C'est un produit scalaire d'une ligne de A avec une colonne de B.
- indice plat row-major
- Une matrice de rows x cols est stockée ligne après ligne, donc l'élément [r][c] est à l'adresse r*cols + c.
- lectures globales redondantes
- Dans la version naïve, chaque élément de A et de B est lu depuis la mémoire globale encore et encore par différents threads, consommant de la bande passante.