Dans CUDA, quelle est la coalescence de la mémoire et comment est-elle réalisée?

Qu’est-ce que “coalesced” dans la transaction de mémoire globale CUDA? Je ne pouvais même pas comprendre après avoir parcouru mon guide CUDA. Comment faire? Dans l’exemple de la masortingce de guide de programmation CUDA, l’access à la masortingce ligne par ligne est appelé “coalesced” ou col .. by col .. est appelé coalesced? Lequel est correct et pourquoi?

Il est probable que cette information ne s’applique que pour calculer la capacité 1.x ou cuda 2.0. Les architectures plus récentes et cuda 3.0 ont un access à la mémoire globale plus sophistiqué et, en fait, les “charges globales coalescées” ne sont même pas profilées pour ces puces.

En outre, cette logique peut être appliquée à la mémoire partagée pour éviter les conflits de banque.


Une transaction de mémoire coalescée est une transaction dans laquelle tous les threads d’une demi-chaîne accèdent à la mémoire globale en même temps. Ceci est trop simple, mais la manière correcte de le faire est de faire en sorte que les threads consécutifs accèdent à des adresses de mémoire consécutives.

Donc, si les threads 0, 1, 2 et 3 lisent la mémoire globale 0x0, 0x4, 0x8 et 0xc, la lecture doit être coalescée.

Dans un exemple de masortingce, gardez à l’esprit que vous souhaitez que votre masortingce réside linéairement en mémoire. Vous pouvez le faire comme vous le souhaitez et votre access à la mémoire doit refléter la disposition de votre masortingce. Donc, la masortingce 3×4 ci-dessous

0 1 2 3 4 5 6 7 8 9 ab 

pourrait être fait ligne après ligne, comme ceci, de sorte que (r, c) mappe à la mémoire (r * 4 + c)

 0 1 2 3 4 5 6 7 8 9 ab 

Supposons que vous ayez besoin d’accéder à element une fois et de dire que vous avez quatre threads. Quels fils seront utilisés pour quel élément? Probablement soit

 thread 0: 0, 1, 2 thread 1: 3, 4, 5 thread 2: 6, 7, 8 thread 3: 9, a, b 

ou

 thread 0: 0, 4, 8 thread 1: 1, 5, 9 thread 2: 2, 6, a thread 3: 3, 7, b 

Ce qui est mieux? Qui se traduira par des lectures fusionnées, et qui ne le fera pas?

De toute façon, chaque thread effectue trois access. Regardons le premier access et voyons si les threads accèdent à la mémoire consécutivement. Dans la première option, le premier access est 0, 3, 6, 9. Non consécutif, pas fusionné. La deuxième option, c’est 0, 1, 2, 3. consécutive! Coalescé! Yay!

Le meilleur moyen est probablement d’écrire votre kernel, puis de le profiler pour voir si vous avez des charges et des magasins globaux non fusionnés.

La coalescence de la mémoire est une technique qui permet une utilisation optimale de la bande passante mémoire globale. En d’autres termes, lorsque des threads parallèles exécutant la même instruction accèdent à des emplacements consécutifs dans la mémoire globale, le modèle d’access le plus favorable est atteint.

entrer la description de l'image ici

L’exemple de la figure ci-dessus aide à expliquer l’arrangement coalescent:

Sur la figure (a), n vecteurs de longueur m sont stockés de manière linéaire. L’élément i du vecteur j est désigné par v j i . Chaque thread du kernel GPU est assigné à un vecteur de longueur m . Les threads dans CUDA sont regroupés dans un tableau de blocs et chaque thread dans GPU a un identifiant unique qui peut être défini comme indx=bd*bx+tx , où bd représente la dimension du bloc, bx l’indice du bloc et tx l’index du thread chaque bloc.

Les flèches verticales démontrent que des threads parallèles accèdent aux premières composantes de chaque vecteur, à savoir les adresses 0, m , 2m … de la mémoire. Comme indiqué sur la figure (a), dans ce cas, l’access à la mémoire n’est pas consécutif. En mettant à zéro l’écart entre ces adresses (flèches rouges illustrées sur la figure ci-dessus), l’access à la mémoire devient fusionné.

Cependant, le problème devient un peu délicat car la taille autorisée des threads résidants par bloc de GPU est limitée à bd . Par conséquent, la disposition des données fusionnées peut être effectuée en stockant les premiers éléments des premiers vecteurs bd dans un ordre consécutif, suivi des premiers éléments des seconds vecteurs bd et ainsi de suite. Le rest des éléments vecteurs est stocké de manière similaire, comme le montre la figure (b). Si n (nombre de vecteurs) n’est pas un facteur de bd , il est nécessaire d’insérer une valeur sortingviale dans les données restantes du dernier bloc, par exemple 0.

Dans la mémoire de données linéaire de la figure (a), la composante i (0 ≤ i < m ) du vecteur indx (0 ≤ indx < n ) est adressée par m × indx +i ; le même composant dans le motif de stockage coalescent de la figure (b) est adressé comme

(m × bd) ixC + bd × ixB + ixA ,

ixC = floor[(m.indx + j )/(m.bd)]= bx , ixB = j et ixA = mod(indx,bd) = tx .

En résumé, dans l’exemple du stockage d’un nombre de vecteurs de taille m , l’indexation linéaire est mappée sur l’indexation coalescée en fonction de:

m.indx +i −→ m.bd.bx +i .bd +tx

Ce réarrangement des données peut entraîner une bande passante mémoire importante de la mémoire globale du GPU.


source: “Accélération des calculs basée sur le GPU dans l’parsing des déformations par éléments finis non linéaires”. Revue internationale des méthodes numériques en génie biomédical (2013).

Si les threads d’un bloc accèdent à des emplacements mémoire consécutifs, tous les access sont combinés en une seule requête (ou coalescée) par le matériel. Dans l’exemple de masortingce, les éléments de la masortingce en ligne sont disposés linéairement, suivis de la ligne suivante, etc. Par exemple, une masortingce 2×2 et 2 threads dans un bloc, les emplacements de mémoire sont organisés comme suit:

(0,0) (0,1) (1,0) (1,1)

Dans l’access en ligne, le thread1 accède (0,0) et (1,0) qui ne peuvent pas être fusionnés. Dans l’access à la colonne, thread1 accède (0,0) et (0,1) qui peuvent être fusionnées car elles sont adjacentes.

Les critères de coalescence sont bien documentés dans le Guide de programmation CUDA 3.2 , Section G.3.2. La version courte est la suivante: les threads du warp doivent accéder à la mémoire en séquence, et les mots auxquels on accède doivent être> = 32 bits. En outre, l’adresse de base utilisée par le Warp doit être alignée sur 64, 128 ou 256 octets pour les access 32, 64 et 128 bits, respectivement.

Le matériel Tesla2 et Fermi fait un bon travail de fusion des access 8 et 16 bits, mais il vaut mieux les éviter si vous voulez une bande passante maximale.

Notez que malgré les améliorations apscopes au matériel Tesla2 et Fermi, la coalescence est SANS AUCUN CAS obsolète. Même sur les matériels de classe Tesla2 ou Fermi, le non-rapprochement des transactions de mémoire globale peut entraîner un impact sur les performances 2x. (Sur le matériel de classe Fermi, cela semble être vrai uniquement lorsque le mode ECC est activé. Les transactions de mémoire contiguës mais non mises à niveau prennent environ 20% sur Fermi.)