Überblick über die tiny-gpu-Architektur
GPU
- Entwickelt, um jeweils nur einen Kernel gleichzeitig auszuführen
- Zum Ausführen eines Kernels sind folgende Schritte erforderlich:
- Den globalen Programmspeicher mit dem Kernel-Code laden
- Den Datenspeicher mit den benötigten Daten laden
- In den Device-Control-Registern die Anzahl der auszuführenden Threads festlegen
- Das Startsignal auf HIGH setzen, um den Kernel auszuführen
- Die GPU besteht aus folgenden Einheiten:
- Device-Control-Register
- Dispatcher
- Eine variable Anzahl von Computing-Cores
- Speichercontroller für Daten- und Programmspeicher
- Cache
Speicher
- Die GPU ist für die Anbindung an externen globalen Speicher ausgelegt
- Daten- und Programmspeicher sind zur Vereinfachung getrennt
- Der globale Speicher besitzt eine feste Lese-/Schreibbandbreite
- Der Speichercontroller verfolgt alle ausgehenden Anfragen von den Computing-Cores an den Speicher, taktet die Anfragen entsprechend der tatsächlichen externen Speicherbandbreite und leitet Antworten aus dem externen Speicher an die jeweils passenden Ressourcen weiter
- Der Cache speichert wiederholt angeforderte Daten, um die Nutzung der Speicherbandbreite zu verringern
Cores
- Jeder Core verfügt über Computing-Ressourcen
- In dieser vereinfachten GPU verarbeitet jeder Core jeweils einen Block und besitzt für jeden Thread im Block eine dedizierte ALU, LSU, PC und ein Register-File
- Der Scheduler verwaltet die Thread-Ausführung und wählt keinen neuen Block aus, bevor der aktuelle Block abgeschlossen ist
- Der Fetcher holt asynchron Instruktionen vom aktuellen Programmzähler
- Der Decoder dekodiert die geholten Instruktionen in Steuersignale für die Thread-Ausführung
- Jeder Thread besitzt einen eigenen Satz von Register-Files
- Die ALU ist eine dedizierte arithmetisch-logische Einheit pro Thread
- Die LSU ist eine dedizierte Load-Store-Unit pro Thread für den Zugriff auf den globalen Datenspeicher
- Der PC ist ein dedizierter Programmzähler pro Thread, der die nächste auszuführende Instruktion bestimmt
ISA
- Implementiert eine einfache ISA mit 11 Instruktionen
- Ausgelegt für einfache Kernel wie Matrixaddition und Matrixmultiplikation
- Unterstützt grundlegende arithmetische Operationen, Speicher-Load/Store, Verzweigungen, Laden von Konstanten usw.
Ausführung
- Jeder Core durchläuft zum Ausführen von Instruktionen die Phasen Fetch, Decode, Request, Wait, Execute und Update
- Jeder Thread folgt einem Ausführungspfad, um Berechnungen auf Daten im Register-File durchzuführen
- Für SIMD-Funktionalität gibt es schreibgeschützte Register mit Werten für Blockindex, Dimensionen und Threadindex
Kernel
- Kernel für Matrixaddition und -multiplikation wurden in der ISA geschrieben, um SIMD-Programmierung und GPU-Ausführung zu demonstrieren
- Mit Testdateien lässt sich die Kernel-Ausführung auf der GPU vollständig simulieren; dabei werden der Zustand des Datenspeichers und eine Ausführungsspur erzeugt
Simulation
- Nach der Installation von
iverilog und cocotb kann die Kernel-Simulation mit dem Befehl make ausgeführt werden
- In der Logdatei werden der anfängliche und der finale Zustand des Datenspeichers sowie die vollständige Ausführungsspur des Kernels ausgegeben
Erweiterte Funktionen
- Viele zusätzliche Funktionen moderner GPUs, die Leistung und Funktionsumfang deutlich verbessern, wurden zur Vereinfachung weggelassen
- Diskutiert werden Funktionen wie mehrstufige Caches und Shared Memory, Memory Coalescing, Pipelining, Warp-Scheduling, Branch Divergence, Synchronisation und Barriers
Meinung von GN⁺
- Die Kernelemente der GPU-Architektur und des SIMD-Programmiermodells werden einfach und gut verständlich erklärt. Besonders die Beispiel-Kernel für Matrixoperationen zeigen anschaulich, wie Parallelverarbeitung auf einer realen GPU abläuft.
- Auch fortgeschrittene Funktionen moderner GPUs sind gut zusammengefasst, sodass tiny-gpu nach dem Verständnis als hilfreicher Einstieg für das Studium komplexerer GPU-Architekturen dienen kann.
- Allerdings fehlen die eigentlichen Funktionen einer Grafik-Pipeline, daher wird nicht behandelt, wie grafikspezifische Hardware arbeitet. Für Menschen mit Interesse an Grafik kann das etwas unbefriedigend sein.
- Im Vergleich mit anderen als Open Source veröffentlichten GPU-Architekturen wie MIAOW oder GPGPU-Sim kann das helfen, realistischere GPUs besser zu verstehen.
- Wenn künftig Funktionen wie Branch Divergence, Memory Coalescing und Pipelining ergänzt werden, dürfte das Material noch praxisnäher werden. Auch dass es sich um ein Open-Source-Projekt handelt, zu dem man beitragen kann, ist attraktiv.
1 Kommentare
Hacker-News-Kommentare
Intel veröffentlicht viele technische Dokumente zu GPUs. Auch die 810/815-Handbücher sind online zu finden. Mit Ausnahme von 855/910/915/945 ist die Dokumentation recht konsistent.
Die Arbeit an Open-Core-GPUs wird begrüßt.
Es gibt noch ein weiteres Open-Source-GPU-Projekt namens NyuziProcessor. (https://github.com/jbush001/NyuziProcessor)
Ich würde gern in FPGA einsteigen, aber es ist schwer herauszufinden, wo man anfangen soll, und das Feld wirkt an sich einschüchternd.
Das Endziel ist, eine Beschleunigerkarte für LLMs (Large Language Models) zu bauen. Abgesehen vom Memory-Offloading dürfte es viele Ähnlichkeiten zu diesem Projekt geben.
Es wird gefragt, warum in einem sequenziellen
always-Block non-blocking und blocking assignment operators gemischt werden.Ich habe früher etwas Ähnliches in VHDL gemacht. Es gab eine Website namens opencores mit verschiedenen Open-Source-HDL-Projekten. Ich frage mich, ob es heute großskalige verteilte HDL-Simulatoren auf HPC-Niveau gibt. Es wäre vernünftig, moderne GPUs für Simulationen auf RTL-Ebene zu nutzen.
Ich mag es, wenn Hardwareprojekte offen gelegt werden. Aber man könnte argumentieren, dass dies eher ein SIMD-Coprozessor ist. Um eine GPU zu sein, müsste es zumindest irgendeine Form von Display-Ausgabe geben. In letzter Zeit ist der Begriff etwas aufgeweicht worden, weil Nvidia und andere serverexklusive Varianten von Grafikarchitekturen als GPU verkaufen, aber der „Grafik“-Teil des GPU-Designs macht immer noch einen erheblichen Teil der Komplexität aus.
Es wird hinterfragt, ob es üblich ist, dass eine ALU auf Hardwareebene eine
DIV-Instruktion direkt implementiert. Ich frage mich, ob das in modernen CUDA-Kernen und Ähnlichem tatsächlich als echte Instruktion existiert oder ob es üblicherweise per Software emuliert wird. Echte Hardware für Division braucht viel Fläche, daher hätte ich das in einer GPU-ALU nicht erwartet. In Verilog ist es leicht,DIV: begin alu_out_reg <= rs / rt; endzu schreiben, aber in Silizium nimmt das viel Platz ein. Wer nur Verilog simuliert, merkt das womöglich nicht.Noch eine weitere „GPU“, die keine Grafikfunktionen bietet. Solche Dinge sollten anders genannt werden.
Es wird vereinfacht angenommen, dass Threads parallel verarbeitet werden und nach jeder Instruktion alle Threads wieder auf denselben Program Counter „konvergieren“. In echten GPUs können einzelne Threads zu unterschiedlichen PCs verzweigen, wodurch branch divergence entsteht. Bevor man eine GPU in Silizium baut, wäre es gut, erst einmal GPU-Programmierung auszuprobieren. Nicht einmal SIMD. (Dieselbe Person wie derjenige, der behauptete, er habe eine CPU gebaut, nachdem er eine Schaltung zum Blinken von LEDs zusammengesetzt hatte.)