Es ist wahrscheinlich, dass diese Informationen nur für die Berechnung von Capabality 1.x oder Cuda 2.0 gelten. Neuere Architekturen und cuda 3.0 verfügen über einen ausgefeilteren globalen Speicherzugriff, und tatsächlich werden "verschmolzene globale Lasten" für diese Chips nicht einmal profiliert.
Diese Logik kann auch auf den gemeinsam genutzten Speicher angewendet werden, um Bankkonflikte zu vermeiden.
Eine Koaleszenzspeichertransaktion ist eine Transaktion, bei der alle Threads in einem Half-Warp gleichzeitig auf den globalen Speicher zugreifen. Dies ist zu einfach, aber der richtige Weg, dies zu tun, besteht darin, aufeinanderfolgende Threads auf aufeinanderfolgende Speicheradressen zugreifen zu lassen.
Wenn die Threads 0, 1, 2 und 3 den globalen Speicher 0x0, 0x4, 0x8 und 0xc lesen, sollte dies ein zusammengeführtes Lesen sein.
Denken Sie in einem Matrixbeispiel daran, dass sich Ihre Matrix linear im Speicher befinden soll. Sie können dies tun, wie Sie möchten, und Ihr Speicherzugriff sollte die Anordnung Ihrer Matrix widerspiegeln. Also die 3x4 Matrix unten
0 1 2 3
4 5 6 7
8 9 a b
könnte Zeile für Zeile so gemacht werden, so dass (r, c) dem Speicher zugeordnet wird (r * 4 + c)
0 1 2 3 4 5 6 7 8 9 a b
Angenommen, Sie müssen einmal auf das Element zugreifen und sagen, Sie haben vier Threads. Welche Threads werden für welches Element verwendet? Wahrscheinlich auch nicht
thread 0: 0, 1, 2
thread 1: 3, 4, 5
thread 2: 6, 7, 8
thread 3: 9, a, b
oder
thread 0: 0, 4, 8
thread 1: 1, 5, 9
thread 2: 2, 6, a
thread 3: 3, 7, b
Welches ist besser? Was führt zu verschmolzenen Lesevorgängen und was nicht?
In beiden Fällen führt jeder Thread drei Zugriffe durch. Schauen wir uns den ersten Zugriff an und sehen, ob die Threads nacheinander auf den Speicher zugreifen. Bei der ersten Option ist der erste Zugriff 0, 3, 6, 9. Nicht aufeinanderfolgend, nicht zusammengeführt. Die zweite Option ist 0, 1, 2, 3. Aufeinander! Verschmolzen! Yay!
Der beste Weg ist wahrscheinlich, Ihren Kernel zu schreiben und ihn dann zu profilieren, um festzustellen, ob Sie nicht zusammengeführte globale Lasten und Speicher haben.
thread 0: 0, 1, 2 etc...
), daher suche ich jetzt nach einer besseren Option :-)Memory Coalescing ist eine Technik, die eine optimale Nutzung der globalen Speicherbandbreite ermöglicht. Das heißt, wenn parallele Threads, die denselben Befehlszugriff auf aufeinanderfolgende Stellen im globalen Speicher ausführen, das günstigste Zugriffsmuster erreicht werden.
Das Beispiel in der obigen Abbildung hilft bei der Erläuterung der zusammengeführten Anordnung:
In Fig. (A) sind n Vektoren der Länge m linear gespeichert. Das Element i des Vektors j wird mit v j i bezeichnet . Jeder Thread im GPU-Kernel ist einem Vektor mit einer Länge von m zugeordnet. Threads in CUDA sind in einem Array von Blöcken gruppiert, und jeder Thread in der GPU hat eine eindeutige ID, die definiert werden kann als
indx=bd*bx+tx
, wobeibd
diebx
Blockdimension die Blockindex und bezeichnettx
der Threadindex in jedem Block ist.Vertikale Pfeile zeigen den Fall, dass parallele Threads auf die ersten Komponenten jedes Vektors zugreifen, dh auf die Adressen 0, m , 2 m ... des Speichers. Wie in Fig. (A) gezeigt, ist in diesem Fall der Speicherzugriff nicht aufeinanderfolgend. Durch Nullstellen der Lücke zwischen diesen Adressen (rote Pfeile in der obigen Abbildung) wird der Speicherzugriff zusammengeführt.
Das Problem wird hier jedoch etwas knifflig, da die zulässige Größe der residenten Threads pro GPU-Block auf begrenzt ist
bd
. Daher kann eine koaleszierte Datenanordnung durchgeführt werden, indem die ersten Elemente der erstenbd
Vektoren in aufeinanderfolgender Reihenfolge gespeichert werden, gefolgt von den ersten Elementen der zweiten bd-Vektoren und so weiter. Die übrigen Vektorelemente werden auf ähnliche Weise gespeichert, wie in Fig. (B) gezeigt. Wenn n (Anzahl der Vektoren) kein Faktor vonbd
ist, müssen die verbleibenden Daten im letzten Block mit einem trivialen Wert aufgefüllt werden, z. B. 0.In der linearen Datenspeicherung in Fig. (A) wird die Komponente i (0 ≤ i < m ) des Vektors indx (0 ≤ indx < n ) angesprochen durch
m × indx +i
; Die gleiche Komponente in dem zusammengewachsenen Speichermuster in Fig. (b) wird angesprochen als(m × bd) ixC + bd × ixB + ixA
,wo
ixC = floor[(m.indx + j )/(m.bd)]= bx
,ixB = j
undixA = mod(indx,bd) = tx
.Zusammenfassend wird im Beispiel des Speicherns einer Anzahl von Vektoren mit der Größe m die lineare Indizierung auf die koaleszierte Indizierung abgebildet, und zwar gemäß:
m.indx +i −→ m.bd.bx +i .bd +tx
Diese Datenumordnung kann zu einer signifikant höheren Speicherbandbreite des globalen GPU-Speichers führen.
Quelle: "GPU-basierte Beschleunigung von Berechnungen in der nichtlinearen Finite-Elemente-Deformationsanalyse." Internationale Zeitschrift für numerische Methoden in der biomedizinischen Technik (2013).
quelle
Wenn die Threads in einem Block auf aufeinanderfolgende globale Speicherorte zugreifen, werden alle Zugriffe von der Hardware zu einer einzigen Anforderung zusammengefasst (oder zusammengeführt). Im Matrixbeispiel sind die Matrixelemente in der Zeile linear angeordnet, gefolgt von der nächsten Zeile usw. Für z. B. 2x2-Matrix und 2 Threads in einem Block sind die Speicherorte wie folgt angeordnet:
(0,0) (0,1) (1,0) (1,1)
Beim Zeilenzugriff greift Thread1 auf (0,0) und (1,0) zu, die nicht zusammengeführt werden können. Beim Spaltenzugriff greift Thread1 auf (0,0) und (0,1) zu, die zusammengeführt werden können, weil sie benachbart sind.
quelle
Die Kriterien für das Zusammenführen sind im CUDA 3.2-Programmierhandbuch ausführlich dokumentiert , Abschnitt G.3.2, gut dokumentiert. Die Kurzversion lautet wie folgt: Threads im Warp müssen nacheinander auf den Speicher zugreifen, und die Wörter, auf die zugegriffen wird, sollten> = 32 Bit sein. Zusätzlich sollte die Basisadresse, auf die der Warp zugreift, 64-, 128- oder 256-Byte sein, ausgerichtet für 32-, 64- bzw. 128-Bit-Zugriffe.
Tesla2- und Fermi-Hardware können 8- und 16-Bit-Zugriffe problemlos zusammenführen. Sie werden jedoch am besten vermieden, wenn Sie eine maximale Bandbreite wünschen.
Beachten Sie, dass das Koaleszieren trotz Verbesserungen der Tesla2- und Fermi-Hardware keinesfalls überholt ist. Selbst auf Hardware der Tesla2- oder Fermi-Klasse kann das Nichtzusammenführen globaler Speichertransaktionen zu einem doppelten Leistungseinbruch führen. (Auf Hardware der Fermi-Klasse scheint dies nur dann der Fall zu sein, wenn ECC aktiviert ist. Zusammenhängende, aber nicht zusammengeführte Speichertransaktionen verursachen bei Fermi einen Treffer von etwa 20%.)
quelle