2017-03-28 3 views
4

J'apprends le Metal d'Apple en essayant de faire un peu de calcul GPU.Pourquoi l'exemple de multiplication de la matrice métallique d'Apple nécessite un remplissage C

J'ai vérifié le matrix multiplication example donné par Apple. Il y a un point que je ne peux pas comprendre.

Dans le fichier MetalMatrixMult.h

// Number of rows in matrices A and C. 
@property (nonatomic) uint16_t m; 

// Number of columns in matrix A; number of rows in matrix B. 
@property (nonatomic) uint16_t n; 

// Number of columns in matrices B and C. 
@property (nonatomic) uint16_t k; 

// Output matrix (padded) C row count 
@property (nonatomic, readonly) uint16_t M; 

// Output matrix (padded) C column count 
@property (nonatomic, readonly) uint16_t K; 

// Output matrix C = A x B 
@property (nonatomic, readonly) float* output; 

Il dit la matrice C est rembourré. Je ne suis pas clair ce que pad signifie ici. Est-ce une sorte d'alignement? Parce que je sais qu'il existe des types d'alignement dans la spécification du langage de shader de Metal, mais je ne sais pas pourquoi nous avons besoin de tamponner un herer tampon.

Merci.

Répondre

2

Il s'agit d'optimiser l'accès à la mémoire. Votre GPU a un certain nombre de groupes de threads, chacun contenant une quantité relativement faible de mémoire dédiée (quelques Ko) qui peut être accédée très rapidement. Ceci est distinct de la mémoire principale de votre GPU, qui peut être quelques Go de mémoire relativement lente.

Comme il est peu probable que les 3 matrices (A, B et C) rentre dans une seule mémoire de ThreadGroup, et retombant à la mémoire principale à l'intérieur des boucles serait extrêmement lent, on divise le calcul en « blocs » ou secteurs. Imaginez diviser la matrice de résultats C en une grille, où chaque secteur est une collection de 8 x 8 éléments: nous pouvons ensuite demander à Threadgroup 1 de calculer le résultat pour le secteur supérieur gauche tandis que les autres groupes de threads calculent simultanément les autres secteurs. Dans ce cas, Threadgroup 1 n'a besoin que des 8 premières lignes de A et des 8 premières colonnes de B pour calculer sa partie C. Cela signifie que nous pouvons envoyer une quantité beaucoup plus petite de données à Threadgroup 1, en le maintenant bien dans la limite du cache.

La raison pour laquelle le métal nous oblige à garnir les matrices est de pouvoir diviser C en une grille parfaite. Si votre vraie matrice de résultat est 12 x 18 et que la taille du secteur est 8 x 8, cela signifie que C est de 1,5 x 2,25 secteurs. Le GPU ne peut pas fonctionner efficacement sur des secteurs partiels, donc vous devez remplir vos matrices avec des zéros pour atteindre des nombres entiers - dans ce cas 2 x 3 secteurs ou 16 x 24 éléments. Vous sacrifiez un peu de stockage et quelques cycles d'horloge supplémentaires pour un traitement parallèle hautement optimisé.

+1

Très clair. Merci –