Wie werden CUDA-Blöcke / Warps / Threads auf CUDA-Kerne abgebildet?


142

Ich benutze CUDA seit einigen Wochen, habe aber einige Zweifel an der Zuordnung von Blöcken / Warps / Thread. Ich studiere die Architektur aus didaktischer Sicht (Universitätsprojekt), daher ist es nicht mein Anliegen, Spitzenleistungen zu erzielen.

Zunächst möchte ich verstehen, ob ich diese Fakten richtig verstanden habe:

  1. Der Programmierer schreibt einen Kernel und organisiert seine Ausführung in einem Raster von Thread-Blöcken.

  2. Jeder Block ist einem Streaming Multiprocessor (SM) zugeordnet. Einmal zugewiesen, kann es nicht auf eine andere SM migrieren.

  3. Jeder SM teilt seine eigenen Blöcke in Warps auf (derzeit mit einer maximalen Größe von 32 Threads). Alle Threads in einem Warp werden gleichzeitig auf den Ressourcen des SM ausgeführt.

  4. Die eigentliche Ausführung eines Threads wird von den im SM enthaltenen CUDA-Kernen ausgeführt. Es gibt keine spezifische Zuordnung zwischen Threads und Kernen.

  5. Wenn ein Warp 20 Thread enthält, aber derzeit nur 16 Kerne verfügbar sind, wird der Warp nicht ausgeführt.

  6. Wenn ein Block hingegen 48 Threads enthält, wird er in zwei Warps aufgeteilt und parallel ausgeführt, sofern genügend Speicher verfügbar ist.

  7. Wenn ein Thread auf einem Kern startet, für den Speicherzugriff oder für eine lange Gleitkommaoperation blockiert ist, kann seine Ausführung auf einem anderen Kern fortgesetzt werden.

Sind sie korrekt?

Jetzt habe ich eine GeForce 560 Ti, die gemäß den Spezifikationen mit 8 SM ausgestattet ist, die jeweils 48 CUDA-Kerne enthalten (insgesamt 384 Kerne).

Mein Ziel ist es sicherzustellen, dass jeder Kern der Architektur die gleichen Anweisungen ausführt. Unter der Annahme, dass mein Code nicht mehr Register erfordert als die in jedem SM verfügbaren, stellte ich mir verschiedene Ansätze vor:

  1. Ich erstelle 8 Blöcke mit jeweils 48 Threads, so dass jeder SM 1 Block zum Ausführen hat. In diesem Fall werden die 48 Threads im SM parallel ausgeführt (wobei alle für sie verfügbaren 48 Kerne ausgenutzt werden)?

  2. Gibt es einen Unterschied, wenn ich 64 Blöcke mit 6 Threads starte? (Vorausgesetzt, sie werden gleichmäßig auf die SMs verteilt)

  3. Wenn ich die GPU in geplante Arbeiten "eintauche" (z. B. 1024 Blöcke mit jeweils 1024 Threads erstellen), kann davon ausgegangen werden, dass alle Kerne zu einem bestimmten Zeitpunkt verwendet werden und dieselben Berechnungen durchführen (vorausgesetzt, die Threads) nie stehen bleiben)?

  4. Gibt es eine Möglichkeit, diese Situationen mit dem Profiler zu überprüfen?

  5. Gibt es eine Referenz für dieses Zeug? Ich habe den CUDA-Programmierleitfaden und die Kapitel zur Hardwarearchitektur in "Programmieren massiv paralleler Prozessoren" und "Design und Entwicklung von CUDA-Anwendungen" gelesen. aber ich konnte keine genaue Antwort bekommen.


Ich möchte als Kommentar anhängen, was "CUDA-Kern" ist. "CUDA-Kern" oder "Ausführungseinheit" ist eine vollständig Pipeline-Ganzzahl ALU und FPU, die einen arithmetischen Befehlsbefehl pro Taktzyklus in einem Cuda-Thread ausführt.
Bruziuz

Antworten:


123

Zwei der besten Referenzen sind

  1. Whitepaper zur NVIDIA Fermi Compute Architecture
  2. GF104 Bewertungen

Ich werde versuchen, jede Ihrer Fragen zu beantworten.

Der Programmierer unterteilt die Arbeit in Threads, Threads in Threadblöcke und Threadblöcke in Gitter. Der Compute Work Distributor weist Streaming Multiprocessors (SMs) Threadblöcke zu. Sobald ein Thread-Block an einen SM verteilt wurde, werden die Ressourcen für den Thread-Block zugewiesen (Warps und gemeinsamer Speicher) und Threads werden in Gruppen von 32 Threads unterteilt, die als Warps bezeichnet werden. Sobald ein Warp zugewiesen wurde, wird er als aktiver Warp bezeichnet. Die beiden Warp-Scheduler wählen zwei aktive Warps pro Zyklus aus und senden Warps an Ausführungseinheiten. Weitere Einzelheiten zu Ausführungseinheiten und zum Versenden von Anweisungen finden Sie unter 1 S. 7-10 und 2 .

4 ' . Es gibt eine Zuordnung zwischen Laneid (Thread-Index in einem Warp) und einem Kern.

5 ' . Wenn ein Warp weniger als 32 Threads enthält, wird er in den meisten Fällen genauso ausgeführt, als hätte er 32 Threads. Warps können aus mehreren Gründen weniger als 32 aktive Threads haben: Die Anzahl der Threads pro Block ist nicht durch 32 teilbar. Das Programm führt einen divergierenden Block aus, sodass Threads, die nicht den aktuellen Pfad angenommen haben, als inaktiv markiert werden oder ein Thread im Warp beendet wird.

6 ' . Ein Thread-Block wird in WarpsPerBlock = (ThreadsPerBlock + WarpSize - 1) / WarpSize unterteilt. Die Warp-Scheduler müssen nicht zwei Warps aus demselben Thread-Block auswählen.

7 ' . Eine Ausführungseinheit wird bei einer Speicheroperation nicht blockiert. Wenn eine Ressource nicht verfügbar ist, wenn eine Anweisung zum Versand bereit ist, wird die Anweisung in Zukunft erneut gesendet, wenn die Ressource verfügbar ist. Warps können an Barrieren, bei Speicheroperationen, Texturoperationen, Datenabhängigkeiten usw. zum Stillstand kommen. Ein blockierter Warp kann vom Warp-Scheduler nicht ausgewählt werden. Auf Fermi ist es nützlich, mindestens 2 zulässige Warps pro Zyklus zu haben, damit der Warp-Scheduler eine Anweisung ausgeben kann.

Siehe Referenz 2 für Unterschiede zwischen einer GTX480 und einer GTX560.

Wenn Sie das Referenzmaterial lesen (einige Minuten), werden Sie feststellen, dass Ihr Ziel keinen Sinn ergibt. Ich werde versuchen, auf Ihre Punkte zu antworten.

1 ' . Wenn Sie den Kernel <<< 8, 48 >>> starten, erhalten Sie 8 Blöcke mit jeweils 2 Warps von 32 und 16 Threads. Es gibt keine Garantie dafür, dass diese 8 Blöcke verschiedenen SMs zugewiesen werden. Wenn einem SM 2 Blöcke zugewiesen sind, kann jeder Warp-Scheduler einen Warp auswählen und den Warp ausführen. Sie werden nur 32 der 48 Kerne verwenden.

2 ' . Es gibt einen großen Unterschied zwischen 8 Blöcken mit 48 Threads und 64 Blöcken mit 6 Threads. Nehmen wir an, dass Ihr Kernel keine Divergenz aufweist und jeder Thread 10 Anweisungen ausführt.

  • 8 Blöcke mit 48 Threads = 16 Warps * 10 Anweisungen = 160 Anweisungen
  • 64 Blöcke mit 6 Threads = 64 Warps * 10 Anweisungen = 640 Anweisungen

Um eine optimale Effizienz zu erzielen, sollte die Arbeitsteilung in Vielfachen von 32 Threads erfolgen. Die Hardware verschmilzt keine Threads aus verschiedenen Warps.

3 ' . Eine GTX560 kann 8 SM * 8-Blöcke = 64 Blöcke gleichzeitig oder 8 SM * 48-Warps = 512 Warps haben, wenn der Kernel die Register oder den gemeinsam genutzten Speicher nicht maximal nutzt. Zu jedem Zeitpunkt wird ein Teil der Arbeit auf SMs aktiv sein. Jeder SM verfügt über mehrere Ausführungseinheiten (mehr als CUDA-Kerne). Welche Ressourcen zu einem bestimmten Zeitpunkt verwendet werden, hängt von den Warp-Schedulern und dem Anweisungsmix der Anwendung ab. Wenn Sie keine TEX-Operationen ausführen, sind die TEX-Einheiten inaktiv. Wenn Sie keine spezielle Gleitkommaoperation ausführen, werden die SUFU-Einheiten im Leerlauf ausgeführt.

4 ' . Parallel Nsight und der Visual Profiler werden angezeigt

ein. IPC ausgeführt

b. IPC ausgestellt

c. aktive Warps pro aktivem Zyklus

d. zulässige Warps pro aktivem Zyklus (nur Nsight)

e. Warp Stall Gründe (nur Nsight)

f. aktive Threads pro ausgeführtem Befehl

Der Profiler zeigt nicht den Auslastungsprozentsatz einer der Ausführungseinheiten an. Für die GTX560 wäre eine grobe Schätzung IssuedIPC / MaxIPC. Für MaxIPC wird angenommen, dass GF100 (GTX480) 2 ist. GF10x (GTX560) ist 4, aber Ziel 3 ist ein besseres Ziel.


1
Vielen Dank für Ihre Antwort. Ich habe die Referenzen gelesen, aber es gibt einige Dinge, die ich in Ihrer Antwort nicht verstehe. In den folgenden Fragen gehe ich davon aus, dass wir eine Fermi-Architektur mit 48 Kernen (16 Kerne * 3 "Kerngruppen") verwenden: 1. Sie haben eine Zuordnung zwischen Kernen und Laneid erwähnt. Um welche Art von Mapping handelt es sich? 2. Aus den Referenzen habe ich erhalten, dass jede "Kerngruppe" höchstens einen halben Warp (16 Threads) pro Taktzyklus ausführt. Wenn wir also theoretisch 48 Threads im selben Block haben, werden sie in 3 Half-Warps organisiert und parallel auf den 48 Kernen ausgeführt. Habe ich recht?
Daedalus

1
CUDA-Kerne sind die Anzahl der FP-Einheiten mit einfacher Genauigkeit. Es ist nicht korrekt, an die Ausführung in Bezug auf CUDA-Kerne zu denken. Jeder Warp hat 32 Threads. Diese Threads werden an eine Gruppe von Ausführungseinheiten (z. B. 16 Cuda-Kerne) ausgegeben. Um an alle 48 Kerne in einem einzigen Takt ausgegeben zu werden, muss einer der beiden Warp-Scheduler einen Warp auswählen, der die Anforderungen eines superskalaren Paares erfüllt, und beide Befehle müssen von einem Typ sein, der von CUDA-Kernen ausgeführt wird. Außerdem muss der andere Warp-Scheduler einen Warp auswählen, dessen nächste Anweisung von CUDA-Kernen ausgeführt wird.
Greg Smith

1
Es ist nicht erforderlich, dass sich Warps im selben Block befinden oder dass Warps in einem Block denselben Programmzähler haben.
Greg Smith

2
In Ihrem Beispiel wählt jeder Scheduler einen Warp aus und gibt 1 Anweisung aus. In diesem Fall werden nur 2 Gruppen von Ausführungseinheiten verwendet. Um mehr Ausführungseinheiten zu verwenden, muss 1 der Scheduler zwei Ausgaben ausführen. Wie in den Referenzen angegeben, gibt es mehrere Arten von Ausführungseinheiten (nicht nur die geprägten Cuda-Kerne) und es gibt Befehlspaarungsregeln (nicht gut dokumentiert), die erfüllt sein müssen, damit die Scheduler zwei Ausgaben ausführen können.
Greg Smith

1
@ GregSmith Ich suche im ganzen Web, um herauszufinden, woher diese 8 aktiven Blöcke pro SM in der Fermi-Architektur stammen. Es wird nicht einmal im Fermi-Whitepaper erwähnt. Haben Sie weitere Hinweise dazu?
Greg K.

8

"E. Wenn ein Warp 20 Threads enthält, aber derzeit nur 16 Kerne verfügbar sind, wird der Warp nicht ausgeführt."

ist falsch. Sie verwirren Kerne im üblichen Sinne (auch in CPUs verwendet) - die Anzahl der "Multiprozessoren" in einer GPU, wobei Kerne im nVIDIA-Marketing sprechen ("unsere Karte hat Tausende von CUDA-Kernen").

Ein Warp selbst kann nur auf einem einzelnen Kern (= Multiprozessor) geplant werden und kann bis zu 32 Threads gleichzeitig ausführen. Es kann nicht mehr als einen einzelnen Kern verwenden.

Die Anzahl "48 Warps" ist die maximale Anzahl aktiver Warps (Warps, die für die Arbeit im nächsten Zyklus in einem bestimmten Zyklus geplant werden können) pro Multiprozessor auf nVIDIA-GPUs mit Compute Capability 2.x; und diese Zahl entspricht 1536 = 48 x 32 Threads.

Antwort basierend auf diesem Webinar


@ GregSmith: Die Antwort wurde bearbeitet, um dies zu beheben. Es ist in Ordnung, dass Sie damit geduldig waren, aber - es ist fünf Jahre her ...
einpoklum

Single Core (= Multiprozessor)? Ich denke, die Frage setzt die Terminologie Single Core = Prozessor und nicht Multiprozessor voraus. Mit Ihrer Terminologie ist Ihre Antwort richtig.
Adarsh
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.