web-dev-qa-db-fra.com

Dans CUDA, qu'est-ce que la mémoire coalescente et comment est-elle réalisée?

Qu'est-ce qui est "fusionné" dans la transaction de mémoire globale CUDA? Je ne pouvais pas comprendre même après avoir parcouru mon guide CUDA. Comment faire? Dans l'exemple de matrice du guide de programmation CUDA, l'accès à la matrice ligne par ligne est appelé "coalesced" ou col .. by col .. est appelé coalesced? Qu'est-ce qui est correct et pourquoi?

67
kar

Il est probable que ces informations s'appliquent uniquement aux capacités de calcul 1.x ou cuda 2.0. Les architectures plus récentes et cuda 3.0 ont un accès à la mémoire globale plus sophistiqué et en fait, les "charges globales fusionnées" ne sont même pas profilées pour ces puces.

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


Une transaction de mémoire fusionnée est une transaction dans laquelle tous les threads d'une demi-chaîne accèdent à la mémoire globale en même temps. C'est trop simple, mais la façon correcte de le faire est simplement que les threads consécutifs accèdent aux adresses mémoire consécutives.

Ainsi, si les threads 0, 1, 2 et 3 lisent la mémoire globale 0x0, 0x4, 0x8 et 0xc, il doit s'agir d'une lecture fusionnée.

Dans un exemple de matrice, gardez à l'esprit que vous souhaitez que votre matrice réside linéairement en mémoire. Vous pouvez le faire comme vous le souhaitez et votre accès à la mémoire doit refléter la disposition de votre matrice. Ainsi, la matrice 3x4 ci-dessous

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

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

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

Supposons que vous ayez besoin d'accéder à l'élément une fois et que vous ayez quatre threads. Quels threads 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? Qu'est-ce qui se traduira par des lectures fusionnées, et lequel ne le fera pas?

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

La meilleure façon est probablement d'écrire votre noyau, puis de le profiler pour voir si vous avez des chargements et des magasins globaux non fusionnés.

135
jmilloy

La coalescence de la mémoire est une technique qui permet une utilisation optimale de la bande passante mémoire globale. C'est-à-dire que 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'accès le plus favorable est atteint.

enter image description here

L'exemple de la figure ci-dessus permet d'expliquer l'arrangement fusionné:

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 jje. Chaque thread du noyau GPU est affecté à un vecteur de longueur m -. Les threads dans CUDA sont regroupés dans un tableau de blocs et chaque thread dans le GPU a un identifiant unique qui peut être défini comme indx=bd*bx+tx, Où bd représente la dimension du bloc, bx désigne la index de bloc et tx est l'index de thread dans chaque bloc.

Les flèches verticales montrent que les threads parallèles accèdent aux premiers composants de chaque vecteur, c'est-à-dire aux adresses 0, m, 2m ... de la mémoire. Comme le montre la figure (a), dans ce cas, l'accès à la mémoire n'est pas consécutif. En mettant à zéro l'écart entre ces adresses (flèches rouges illustrées dans la figure ci-dessus), l'accès à la mémoire devient fusionné.

Cependant, le problème devient légèrement délicat ici, car la taille autorisée des threads résidants par bloc GPU est limitée à bd. Par conséquent, l'agencement des données fusionnées peut être effectué en stockant les premiers éléments des premiers vecteurs bd dans un ordre consécutif, suivis des premiers éléments des seconds vecteurs bd et ainsi de suite. Le reste des éléments de vecteurs sont stockés d'une 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 de remplir les données restantes dans le dernier bloc avec une valeur triviale, par ex. 0.

Dans le stockage de données linéaire de la figure (a), le composant i (0 ≤ i <m) du vecteur indx (0 ≤ indx <n) est adressé par m × indx +i; le même composant dans le modèle de stockage coalescent sur 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 de stockage d'un certain nombre de vecteurs de taille m, l'indexation linéaire est mappée à l'indexation coalescée selon:

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

Ce réarrangement des données peut conduire à une bande passante mémoire considérablement plus élevée de la mémoire globale du GPU.


source: "Accélération basée sur GPU des calculs dans l'analyse de déformation par éléments finis non linéaire." Revue internationale des méthodes numériques en génie biomédical (2013).

10
ramino

Si les threads d'un bloc accèdent à des emplacements de mémoire globale consécutifs, tous les accès sont combinés en une seule demande (ou fusionnés) par le matériel. Dans l'exemple de matrice, les éléments de matrice en ligne sont disposés de manière linéaire, suivis de la ligne suivante, etc. Pour, par exemple, une matrice 2x2 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'accès en ligne, thread1 accède à (0,0) et (1,0) qui ne peuvent pas être fusionnés. Dans l'accès aux colonnes, thread1 accède à (0,0) et (0,1) qui peuvent être fusionnés car ils sont adjacents.

7
penmatsa

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 de la chaîne doivent accéder à la mémoire en séquence, et les mots consultés doivent> = 32 bits. De plus, l'adresse de base à laquelle le Warp accède doit être alignée sur 64, 128 ou 256 octets pour les accès 32, 64 et 128 bits, respectivement.

Le matériel Tesla2 et Fermi fait un travail correct de fusion des accès 8 et 16 bits, mais il vaut mieux les éviter si vous voulez une bande passante de pointe.

Notez que malgré les améliorations apportées au matériel Tesla2 et Fermi, la fusion n'est en aucun cas obsolète. Même sur du matériel de classe Tesla2 ou Fermi, l'échec de la fusion des transactions de mémoire globale peut entraîner une baisse des performances de 2x. (Sur le matériel de la classe Fermi, cela semble être vrai uniquement lorsque ECC est activé. Les transactions de mémoire contiguës mais non fusionnées prennent environ 20% sur Fermi.)

2
ArchaeaSoftware