General Purpose Computation on Graphics Processing Unit
Inhalt Motivation Hardware Design aktueller GPUs GPGPU mit Standard APIs Anwendungsbeispiele GPGPU a la Nvidia GPGPU a la AMD/ATI Stefan Auer
Motivation GPUs sind äußerst leistungsfähige Parallelrechner Beispiel: Nvidia GeForce 8800 GTX 128 Stream Prozessoren ~690 millionen Transistoren 1350 MHz Shader clock speed 86 GB/s Speicherbandbreite (Peak) 345 Gflops Floatingpoint-Leistung (Peak, Multiply-ADD) Vergleich: Intel Core 2 Quad Q6600 4 Kerne (zwei Doppelkernprozessoren) ~580 millionen Transistoren 2400 MHz Takt ~5 GB/s Speicherbandbreite (gemessen) ~40 Gflops FPU-Leistung (gemessen, Whetstone) Stefan Auer
Motivation Vergleich der Floatingpointleistung Stefan Auer Gflops Gflops FMUL FADD 50% FMUL / 50% FADD FMADD GeForce 8800 GTX 172,8 345,6 GeForce 8800 GTS 115,2 230,4 Core 2 Extreme QX6700 42,7 85,3 Core 2 Extreme X6800 23,5 46,9 Stefan Auer Quelle: behardware.com
Quelle: tomshardware.com Motivation Leistungszuwachs war bei GPUs in letzter Zeit größer als bei CPUs (GPUs profitieren mehr als CPUs von Verbesserungen der Fertigung) 503% 1355% CPUs sind optimiert für sequentiellen Code -> großer Aufwand für branch prediction, caching, usw. GPUs arbeiten von Natur aus parallel -> Leistungssteigerung durch Erhöhung der ALU-Zahl möglich 100% 100% Stefan Auer Quelle: tomshardware.com
Motivation Einsatzzweck von GPUs ist das Rendering von interaktiven 3D Anwendungen Überleitung zu Pipline -> GPUs sind vergleichsweise preiswert (Videospiele sind Massenmarkt) GeForce 8800 GTX aktuell ~ 450.- € Stefan Auer
Programmierbare Rendering Pipeline Motivation Programmierbare Rendering Pipeline CPU GPU Geometrie Shader (DX10) Vertex Shader Pixel Shader Anwendung Transformation & (Vertex-) Beleuchtung Geometrie Verarbeitung Rasterisierung Schattierung Tests Render Target (Framebuffer,Texturen) Aspekt der Programmierbarkeit hervorheben. Kurze „History der Programmierbarkeit“ / Überleitung: Hardwareabstraktion Vertices Primitive (z.B. Dreiecke) Fragmente („Pre-Pixels“) Pixel (Farbe, Tiefe) Stefan Auer
Inhalt Motivation Hardware Design aktueller GPUs GPGPU mit Standard APIs Anwendungsbeispiele GPGPU a la Nvidia GPGPU a la AMD/ATI Stefan Auer
Hardware Design aktueller GPUs Nvidia G70 Architektur (GeForce 7800) L2 Tex Cull / Clip / Setup Shader Instruction Dispatch Fragment Crossbar Memory Partition Z-Cull DRAM(s) Host / FW / VTF 8 vertex shader pipelines 24 pixel shader pipelines 16 raster operation pipelines 32 ppc Vertex- / Pixel-Shader getrennt bis DX9 Stefan Auer Quelle: Nvidia
Hardware Design aktueller GPUs Unified shader (1) Höhere Flexibilität geometry shader, Spielphysik ?, general purpose Bessere Ausnutzung der Hardware Wenig Geometrie Lastverteilung Viel Geometrie Stefan Auer Quelle: Nvidia
Hardware Design aktueller GPUs Unified shader (2) Vertex Shader Pixel Shader Idle hardware Heavy Geometry Vertex Shader Pixel Shader Idle hardware Heavy Pixel Stefan Auer Quelle: Nvidia
Hardware Design aktueller GPUs Unified shader (3) Unified Shader Heavy Geometry Vertex workload Pixel workload Unified Shader Heavy Pixel Vertex workload Pixel workload Stefan Auer Quelle: Nvidia
Hardware Design aktueller GPUs 128 unified shader pipelines Nvidia G80 Architektur (GeForce 8800) L2 FB SP L1 TF Thread Processor Vtx Thread Issue Setup / Rstr / ZCull Geom Thread Issue Pixel Thread Issue Data Assembler Host 6 ROPs 192 ppc Stefan Auer Quelle: Nvidia
Hardware Design aktueller GPUs CPU vs. GPU: CPU Architektur CPUs implementieren Von Neumann Architektur Von Neumann Flaschenhals Teilung von ALU und Speicher schafft Bandbreitenprobleme heutige ALUs sind wesentlich schneller als die Datenverbindungen cache management notwendig > großer Kontroll-Overhead > für rechenintensive Anwendungen verbleibt weniger Leistung Überleitung: Programmierung; only 6.5% of the Itanium die is devoted to its 12 integer and 2 floating point ALUs and their registers Stefan Auer
Hardware Design aktueller GPUs CPU vs. GPU: GPU Architektur GPUs entsprechen annähernd Stream Processing Architektur Anwendung organisiert in Streams und Kernel fördert Lokalität der Daten und Parallelität ihrer Verarbeitung Stream Processoren vs. GPU: looping and jumping (nicht effizient), turing machine (eingeschränkt), sehr viel mehr Speicher Stefan Auer
Hardware Design aktueller GPUs CPU vs. GPU: Gegenüberstellung CPU: profitiert hauptsächlich von Aufgabenparallelität schnelle (aber kostspielige) Caches Pipelines auf Sprünge optimiert wenige Ausführungseinheiten (1,2,4,...) hohe Leistung bei Bearbeitung einzelner Threads GPU: profitiert zusätzlich von Datenparallelität sehr viele ALUs, spezialisiert auf floating-point Arithmetik sehr hohe Speicherbandbreite (aber hohe Latenz) tausende Threads mit gleichem Kernel Stefan Auer
Inhalt Motivation Hardware Design aktueller GPUs GPGPU mit Standard APIs Anwendungsbeispiele GPGPU a la Nvidia GPGPU a la AMD/ATI Stefan Auer
GPGPU mit Standard APIs „Standard“-Verwendung der Pipeline bei GPGPU Fragment Prozessor 2 „screen“ 1 Frame Buffer(s) Texture Buffer Rasterisierer Vertex Prozessor Vertex Texture Fetch Render to Texture Texture Sampling (Gather) „screensize“ entspricht Textur-Größe, orthographische (parallele) Projektion Stefan Auer
GPGPU mit Standard APIs Texturen als computational grids (ein) typischer GPGPU Programmierstil Texturen repräsentieren Streams (Texel = Datenelement) Fragmentprogramme (pixel shader) repräsentieren Kernel Zeichnen eines Full-Screen-Rechtecks repräsentiert Berechnungschritt Quelle: Mark Harris - Nvida Stefan Auer
GPGPU mit Standard APIs viele Berechnungen lassen sich gut auf Grids abbilden: Matrix Algebra Bild- und Volumenverarbeitung Physiksimulationen Global Illumination ray tracing, photon mapping, radiosity Streams die keinen Grids entsprechen lassen sich meist darauf abbilden Stefan Auer
GPGPU mit Standard APIs GPGPU „Hello World“ (1) Bildverarbeitung: 3x3 Faltung Input: Bild, Gewichtungen Output: „geblurrtes“ Bild CPU Version: image = loadImage(WIDTH, HEIGHT); blurImage = allocZeros(WIDTH, HEIGHT); for (x=0; x < WIDTH; x++) for (y=0; y < HEIGHT; y++) for (i=-1; i <= 1; i++) for (j=-1; j <= 1; j++) float w = computeWeight(i,j); blurImage[x][y] += w * image[x+i, y+j]; Quelle: Mark Harris - Nvida Stefan Auer
GPGPU mit Standard APIs GPGPU „Hello World“ (2) GPU Version: 1) Lade image als Textur 2) erstelle blurImage Textur welche das Ergebnis aufnimmt Quelle: Mark Harris - Nvida Stefan Auer
GPGPU mit Standard APIs GPGPU „Hello World“ (3) GPU Version: 3) Lade Fragment-Programm (Kernel) Beispielcode in CG: float4 blurKernel(uniform samplerRECT image, float2 winPos : WPOS, out float4 blurImage ) { blurImage = float4(0,0,0,0); for (i=-1; i <= 1; i++) { for (j=-1; j <= 1; j++) { float2 texCoord = winPos + float2(i,j); float w = computeWeight(i,j); blurImage += w * texRECT( image, texCoord ); } Quelle: Mark Harris - Nvida Stefan Auer
GPGPU mit Standard APIs GPGPU „Hello World“ (4) GPU Version: 4) wähle Viewport-Größe entsprechend Bildgröße konfiguriere Vertex Pipeline so dass sie „nichts“ tut Beispielcode für OpenGl: glMatrixMode( GL_PROJECTION ); glLoadIdentity(); gluOrtho2D(0, 1, 0, 1); glViewport(0, 0, WIDTH, HEIGHT ); glMatrixMode( GL_MODELVIEW ); Quelle: Mark Harris - Nvida Stefan Auer
GPGPU mit Standard APIs GPGPU „Hello World“ (5) GPU Version: 5) binde image als Textur, blurKernel als Fragment-Programm und blurImage als Render-Target 6) zeichne ein Full-Screen-Rechteck glBegin( GL_TRIANGLES ); glVertex2f(0, 0); glVertex2f(2, 0); glVertex2f(0, 2); glEnd(); „screen“ 1 2 Quelle: Mark Harris - Nvida Stefan Auer
GPGPU mit Standard APIs Was passiert? blurKernel wird für jedes Fragment einmal ausgeführt das Rendering ersetzt die äußeren beiden Schleifen der CPU- Version blurKernel führt für jedes Fragment 9 Gather (p=a[i]) Operationen durch und gewichtet jeden Wert die „geblurrten“ Ergebnis-Pixel werden in den Framebuffer bzw. in die blurImage Textur geschrieben Stefan Auer
GPGPU mit Standard APIs GPGPU Techniken (1) Scatter Nachbildung Problem: a[i]=p indirektes Schreiben Position des Fragments kann im Pixel-Shader nicht mehr geändert werden Lösung 1: Auf Gather abbilden erster Schritt: Kernel 1 ermittelt Werte und speichert in Zwischenergebnis zweiter Schritt: Kernel 2 sammelt Zwischenergebnisse an der passenden Stelle ein 1 2 Stefan Auer
GPGPU mit Standard APIs 1 9 5 8 3 6 GPGPU Techniken (2) Scatter Nachbildung Lösung 2: Adresssortierung erster Schritt: Kernel 1 ermittelt Werte und speichert sie gemeinsam mit Zieladresse in Zwischenergebnis zweiter Schritt: Kernel 2 führt auf dem Zwischenergebnis bitonic-sort nach Adressen durch dritter Schritt: Kernel 3 findet die passenden Werte durch binäre Suche 1 3 5 6 8 9 2 bitonic-sort: ein Standardsortierverfahren auf GPUs 1+1 3+3 5+5 6 8 9 3 Stefan Auer
GPGPU mit Standard APIs GPGPU Techniken (3) Scatter Nachbildung Lösung 3: Vertex Shader führe Rechnung im Vertex Shader durch Zeichne Punkte Vertex Position = Adresse führe Rechnung im Pixel Shader durch schreibe Adresse und Wert in Textur lies Textur im Vertex Shader (Vertex Texture Fetch) und passe Position an Adresse an bitonic-sort: ein Standardsortierverfahren auf GPUs Stefan Auer
GPGPU mit Standard APIs GPGPU Techniken (4) Reduce Problem: Reduktion der Wertanzahl durch Zusammenfassung Lösungen: Zeilen-/Spaltenweise, Blockweise immer mehrere Passes notwendig Filtering Problem: Reduktion der Wertanzahl durch entfernen bestimmter Werte Sortierung Lösung: Sorting Networks bitonic-sort: ein Standardsortierverfahren auf GPUs Stefan Auer
GPGPU mit Standard APIs GPGPU Techniken (5) Suche Problem: Suche nach bestimmten Werten, Werten die bestimmte Eigenschaften erfüllen oder Nachbarn von bestimmten Werten sinnvoll auf GPU bei vielen parallelen Suchen Datenstrukturen Arrays und Matrizen (dicht- und dünnbesetzt) adaptive Datenstrukturen (adp. shadow maps, octrees,…) bitonic-sort: ein Standardsortierverfahren auf GPUs Stefan Auer
GPGPU mit Standard APIs GPGPU Techniken (6) Branching statisch vermeiden von Branches, wenn vorab bekannt ist welche Elemente betroffen sind Nutzung des z-Buffers bedingte writes Nutzung mathematischer Konstrukte als Branch-Ersatz sinnvolle Verwendung der Branching-Unterstützung in shader-model 3 und 4 bitonic-sort: ein Standardsortierverfahren auf GPUs Stefan Auer
Inhalt Motivation Hardware Design aktueller GPUs GPGPU mit Standard APIs Anwendungsbeispiele GPGPU a la Nvidia GPGPU a la AMD/ATI Stefan Auer
Grundlegende (aus der Mathematik): Anwendungsbeispiele Grundlegende (aus der Mathematik): Differentielle Gleichungen & … gewöhnliche (ODE), z.B. für Partikel Systeme partielle (PDE), z.B. für Fluid Simulationen (Navier Stokes) … Lineare Algebra z.B. für numerische Simulation, Ray tracing, realtime Shading & Lighting Stefan Auer
Anwendungsbeispiele Allgemeine (1): Relationale Datenbanken Query-Beschleunigung z.B. durch beschleunigtes Join Simulation physikalischer Phänomene finite Differenzen & finite Elemente z.B. für Fluide (Flüssigkeiten, Gase) Signal- und Bildverarbeitung Segmentierung: Erkennen von „Objekten“ (z.B. Tumor in medizinischen Scans) Computer Vision ... Stefan Auer
Anwendungsbeispiele Allgemeine (2): Global Illumination Ray tracing Photon mapping Radiosity Subsurface scattering Stefan Auer
Anwendungsbeispiele Havok FX entstanden durch Zusammenarbeit von Havok und Nvidia Nutzung der GPU für Effekt Physik in Spielen Stefan Auer
Inhalt Motivation Hardware Design aktueller GPUs GPGPU mit Standard APIs Anwendungsbeispiele GPGPU a la Nvidia GPGPU a la AMD/ATI Stefan Auer
Nachteile von Grafik APIs: GPGPU alla Nvidia Nachteile von Grafik APIs: sehr hohe Lernkurve für Nicht-Grafik-Entwickler Pipeline? Texturen? (sampler, filtering, blending) Framebuffer? unpassende Abstraktionen für Nicht-Grafik-Programme Vertices/Primitive/Fragmente + jeweils spezielle Programme dafür unflexibler Speicherzugriff fehlendes Scatter zuwenig Kontrolle (Grafik-) Treiber + API treffen wichtige Entscheidungen Treiber ändern sich Grafik-Optimierungen teilweise kontrapoduktiv Stefan Auer
GPGPU alla Nvidia Nvidia G80 als GPU Stefan Auer Quelle: Nvidia L2 FB SP L1 TF Thread Processor Vtx Thread Issue Setup / Rstr / ZCull Geom Thread Issue Pixel Thread Issue Data Assembler Host Stefan Auer Quelle: Nvidia
Thread Execution Manager GPGPU alla Nvidia Nvidia G80 als GPGPU Load/store Global Memory Thread Execution Manager Input Assembler Host Texture Parallel Data Cache Stefan Auer Quelle: Nvidia
CUDA GPGPU alla Nvidia Compute Unified Device Architecture Plattform für GPGPU auf Nvidia G80 (und höher) nutzt das C Subset von C++ (mit ein paar Erweiterungen) als Programmiersprache ersetzt (für GP-Anwendungen) Grafik API und Treiber CUDA und Grafik Anwendungen parallel möglich (OS- Multitasking) Stefan Auer
CUDA Software Stack GPGPU alla Nvidia Anwendung CPU (Host) GPU CUDA Libraries CUDA Runtime CUDA Driver GPU Stefan Auer
CUDA aus Compiler-Sicht GPGPU alla Nvidia CUDA aus Compiler-Sicht NVCC ist compiler driver (ähnlich gcc) NVCC verarbeitet CUDA Programme (.cu) Host-Code wird entweder extrahiert (-> .cpp) oder sofort kompiliert (z.B. Visual Studio Kompilier) Device Code wird für PTX kompiliert PTX Code wird zur Installationszeit für das jeweilige Device (z.B. G80) kompiliert Stefan Auer
GPGPU alla Nvidia PTX PTX: parallel thread execution virtuelle Maschine und ISA Programmiermodell Abstraktion von Ressourcen und State ISA: instruction set architecture formale Spezifikation des Verhaltens eines Prozessors aus Sicht des Programmierers PTX-Übersetzter ist optimizing compiler übersetzt PTX in Zielmaschinencode (z.B. für G80) wird zur Installationszeit ausgeführt CUDA Treiber implementiert die virtuelle Maschine stellt Übersetzer zur Verfügung zusammengefasst durch: PTX ist virtuelle Maschine, code wird bei Installation in Maschinencode übersetzt Stefan Auer
CUDA Programmiermodell GPGPU alla Nvidia CUDA Programmiermodell GPU ist Coprozessor der CPU (CPU = Host) datenparallele Programmteile werden für die GPU kompiliert und dort als Kernel ausgeführt implizites Multithreadding: derselbe Kernel wird auf der GPU (hundertfach) parallel von (tausenden) Threads auf unterschiedlichen Daten (SIMD) ausgeführt eigener Speicher für CPU (host memory) und GPU (device memory); Hin-und-Her-Kopieren durch DMA indirekte Ressourcenverteilung (Threads, Register, Shared- Memory) durch Programmierer notwendig Stefan Auer
CUDA Threadmanagement GPGPU alla Nvidia CUDA Threadmanagement Grid alle Threads führen denselben Kernel aus alle Blöcke haben die selben Dimensionen Block zu jeder Zeit nur ein Block aktiv auf jedem Multiprozessor alle Threads teilen sich Register (per Thread) und Shared Memory (per Block) Synchronisation zwischen Threads möglich Grid Block (0,0) (0,1) (0,2) (0,3) (1,0) (1,1) (2,0) (2,1) … 65535 Thread (1,2) (2,2) 512 512 512 64 Stefan Auer Zahlen gelten für GeForce 8800
Streaming Multiprocessor (SM) GPGPU alla Nvidia CUDA Threadausführung Streaming Multiprocessor (SM) SP 0 SP 1 SP 2 SP 3 SP 4 SP 5 SP 6 SP 7 Constant L1 Cache SFU Instruction Fetch Instruction L1 Cache Thread / Instruction Dispatch Shared Memory RF 0 RF 1 RF 2 RF 3 RF 4 RF 5 RF 6 RF 7 ein Multiprozessor führt zu jeder Zeit nur einen aktiven Block aus ein Multiprozessor kann 8 Blöcke ausführen (scheduling) die Threads eines Blocks werden ausgeführt als „Warps“ a 32 Threads ein Multiprozessor kann insgesamt 24 Warps ausführen (scheduling) eine Instruktion benötigt pro Warp je nach Art zwischen 4 (z.B. FMADD) und 32 (z.B. IMUL) clock cycles GPU Texture Parallel Data Cache Stefan Auer Zahlen gelten für GeForce 8800
Streaming Multiprocessor (SM) GPGPU alla Nvidia CUDA Speicherbereiche (1) Streaming Multiprocessor (SM) SP 0 SP 1 SP 2 SP 3 SP 4 SP 5 SP 6 SP 7 Constant L1 Cache SFU Instruction Fetch Instruction L1 Cache Thread / Instruction Dispatch Shared Memory RF 0 RF 1 RF 2 RF 3 RF 4 RF 5 RF 6 RF 7 Jeder Thread kann: pro Thread Register Lesen/Schreiben pro Thread Local Memory L/S pro Block Shared Memory L/S pro Grid Global Memory L/S pro Grid Constant Memory nur L pro Grid Texture Memory nur L Device Memory Constants (cached) Textures Global (uncached) Local GPU Texture Parallel Data Cache Stefan Auer
Streaming Multiprocessor (SM) GPGPU alla Nvidia CUDA Speicherbereiche (2) Streaming Multiprocessor (SM) Alle Threads eines Blocks teilen sich Shared Memory (per Block) und Register (per Thread) -> verfügbare Registerzahl = Register pro SM / Threads pro Block Kernel die mehr Register verwenden können nicht gestartet werden Instruction Fetch Device Memory Instruction L1 Cache Thread / Instruction Dispatch Constants (cached) Shared Memory SFU SP 0 RF 0 RF 4 SP 4 SFU SP 1 RF 1 RF 5 SP 5 Textures (cached) SP 2 RF 2 RF 6 SP 6 SP 3 RF 3 RF 7 SP 7 Global (uncached) GPU Constant L1 Cache Texture Parallel Data Cache Local (uncached) Stefan Auer
Streaming Multiprocessor (SM) GPGPU alla Nvidia CUDA Speicherbereiche (3) Streaming Multiprocessor (SM) SP 0 SP 1 SP 2 SP 3 SP 4 SP 5 SP 6 SP 7 Constant L1 Cache SFU Instruction Fetch Instruction L1 Cache Thread / Instruction Dispatch Shared Memory RF 0 RF 1 RF 2 RF 3 RF 4 RF 5 RF 6 RF 7 Device Memory Der Host kann: Constant Memory L/S Texture Memory L/S Global Memory L/S Constants (cached) Textures (cached) Global (uncached) GPU Texture Parallel Data Cache Local (uncached) Stefan Auer
Streaming Multiprocessor (SM) GPGPU alla Nvidia CUDA Speicherbereichgrößen Streaming Multiprocessor (SM) SP 0 SP 1 SP 2 SP 3 SP 4 SP 5 SP 6 SP 7 Constant L1 Cache SFU Instruction Fetch Instruction L1 Cache Thread / Instruction Dispatch Shared Memory RF 0 RF 1 RF 2 RF 3 RF 4 RF 5 RF 6 RF 7 Register pro SM 8192 (-> 16-256 pro Thread) Shared Memory pro SM 16 kB Constant Memory 64 kB Constant Mem. Cache pro SM 8 kB Kernelsize 2 M Instr. Device Memory gesamt 758 MB Device Memory Constants (cached) Textures (cached) Global (uncached) GPU Texture Parallel Data Cache Local (uncached) Stefan Auer Zahlen gelten für GeForce 8800 GTX
Streaming Multiprocessor (SM) GPGPU alla Nvidia CUDA Synchronisation Streaming Multiprocessor (SM) SP 0 SP 1 SP 2 SP 3 SP 4 SP 5 SP 6 SP 7 Constant L1 Cache SFU Instruction Fetch Instruction L1 Cache Thread / Instruction Dispatch Shared Memory RF 0 RF 1 RF 2 RF 3 RF 4 RF 5 RF 6 RF 7 keine Synchronisation zwischen Threads unterschiedlicher Blöcke Ausführungsreihenfolge generell undefiniert Scattersuppport -> Schreiben mehr als ein Thread an selbe Stelle (shared oder global) -> undefiniert Synchronisationspunkte (Barrieren) Atomare Operationen (Read-Modify-Write) (nur Compute Capability 1.1, nur 32 bit INT) Device Memory Constants (cached) Textures (cached) Global (uncached) GPU Texture Parallel Data Cache Local (uncached) Stefan Auer
C/C++ mit ein paar kleinen Erweiterungen: GPGPU alla Nvidia CUDA aus Programmierersicht (1) C/C++ mit ein paar kleinen Erweiterungen: Spezifikation des Funktionstyps __host__, __global__, __device__ Spezifikation des Variablentyps __device__, __constant__, __shared__ Kernelausführungskonfiguration KernelFunc<<<500, 123>>>(...) Stefan Auer
CUDA aus Programmierersicht (2) GPGPU alla Nvidia CUDA aus Programmierersicht (2) Einige integrierte Variablen: gridDim, blockIdx, blockDim, threadIdx zusätzliche Speicherverwaltungs-Funktionen cudaMalloc(), cudaFree() cudaMemcpy(), cudaMemcpy2D() Stefan Auer
CUDA Beispiel: Matrix Mul GPGPU alla Nvidia CUDA Beispiel: Matrix Mul P=M*N jeweils mit Größe WIDTH x WIDTH jeder Thread behandelt ein Element aus P Nutzung nur eines Blocks -> WIDTH <= sqrt(512) schlechtes Verhältnis von OPs zu Speicherzugriffen (ca. 1:1) -> kein Musterbeispiel M N P WIDTH Stefan Auer
Matrix Mul als Host (CPU) Programm GPGPU alla Nvidia Matrix Mul als Host (CPU) Programm void MatrixMulOnHost(const Matrix M, const Matrix N, Matrix P) { for (int i = 0; i < M.height; ++i) for (int j = 0; j < N.width; ++j) { double sum = 0; for (int k = 0; k < M.width; ++k) { double a = M.elements[i * M.width + k]; double b = N.elements[k * N.width + j]; sum += a * b; } P.elements[i * N.width + j] = sum; M N P WIDTH Stefan Auer
CUDA Matrix Mul: Hauptprogramm GPGPU alla Nvidia CUDA Matrix Mul: Hauptprogramm int main(void) { // Allocate and initialize the matrices Matrix M = AllocateMatrix(MATRIX_SIZE, MATRIX_SIZE, 1); Matrix N = AllocateMatrix(MATRIX_SIZE, MATRIX_SIZE, 1); Matrix P = AllocateMatrix(MATRIX_SIZE, MATRIX_SIZE, 0); // M * N on the device MatrixMulOnDevice(M, N, P); // Free matrices FreeMatrix(M); FreeMatrix(N); FreeMatrix(P); return 0; } M N P WIDTH Stefan Auer
CUDA Matrix Mul: Host Funktion (1) GPGPU alla Nvidia CUDA Matrix Mul: Host Funktion (1) // Matrix multiplication on the device void MatrixMulOnDevice(const Matrix M, const Matrix N, Matrix P) { // Load M and N to the device Matrix Md = AllocateDeviceMatrix(M); // contains cudaMalloc() CopyToDeviceMatrix(Md, M); // contains cudaMemcpy() Matrix Nd = AllocateDeviceMatrix(N); CopyToDeviceMatrix(Nd, N); // Allocate P on the device Matrix Pd = AllocateDeviceMatrix(P); CopyToDeviceMatrix(Pd, P); // Clear memory ... M N P WIDTH Stefan Auer
CUDA Matrix Mul: Host Funktion (2) GPGPU alla Nvidia CUDA Matrix Mul: Host Funktion (2) ... // Setup the execution configuration dim3 dimBlock(MATRIX_SIZE, MATRIX_SIZE); dim3 dimGrid(1, 1); // Launch the device computation threads! MatrixMulKernel<<<dimGrid, dimBlock>>>(Md, Nd, Pd); // Read P from the device CopyFromDeviceMatrix(P, Pd); // contains cudaMemcpy() // Free device matrices FreeDeviceMatrix(Md); // contains cudaFree() FreeDeviceMatrix(Nd); FreeDeviceMatrix(Pd); } M N P WIDTH Stefan Auer
CUDA Matrix Mul: Kernel GPGPU alla Nvidia CUDA Matrix Mul: Kernel // Matrix multiplication kernel – thread specification __global__ void MatrixMulKernel(Matrix M, Matrix N, Matrix P) { // 2D Thread ID int tx = threadIdx.x; int ty = threadIdx.y; // Pvalue is used to store one matrix element computed by the thread float Pvalue = 0; for (int k = 0; k < MATRIX_SIZE; ++k) float Melement = M.elements[ty * M.pitch + k]; float Nelement = N.elements[k * N.pitch + tx]; Pvalue += Melement * Nelement; } P.elements[ty * P.pitch + tx] = Pvalue; M N P WIDTH Stefan Auer
CUDA Performance Guidelines GPGPU alla Nvidia CUDA Performance Guidelines auf hohe arithmetische Dichte achten Speicher Latenz verbergen (teilweise schon durch Compiler) Shared Memory (on chip) ausnutzen Cache-freundliche Speicherzugriffe Zugriff auf lokalen und globalen Speicher (uncached) meiden viele (tausende) Blöcke und Threads verwenden Stefan Auer
Zukunft von CUDA GPGPU alla Nvidia Nvida bietet spezielle GP Hardware an: Tesla Nächste GPU Generation (G90) bringt wichtige Änderungen für GPGPU (z.B. double precision), jedoch teils nur für Tesla Stefan Auer
CUDA Bewertung GPGPU alla Nvidia + für nicht grafiknahe Anwendungen besser geeignet als Grafik-API + erstes Produkt das eine kritische Masse von Nicht-Grafik- Programmierern für GPGPU interessieren könnte +/- Weiterentwicklung könnte von kommerziellem Erfolg der damit verbundenen Nvidia Produkte (Tesla) abhängen noch nicht ausgereift (Dokumentation mager, Programmierung könnte eleganter sein) setzt zwingend Nvidia GPUs voraus Stefan Auer
Inhalt Motivation Hardware Design aktueller GPUs GPGPU mit Standard APIs Anwendungsbeispiele GPGPU a la Nvidia GPGPU a la AMD/ATI Stefan Auer
data parallel virtual machine (DPVM) (1) GPGPU alla AMD/ATI data parallel virtual machine (DPVM) (1) Grundgedanken: Programmierer soll GPGPU-relevante Hardware möglichst direkt ansteuern können command processor: liest command buffer und verteilt Arbeit auf prozessor arrays data parallel processor arrays: Floating Point Prozessoren; führen nativen Binärcode aus memory controler: verwaltet GPU zugreifbaren Speicher; Anwendung trägt Verantwortung: Speicherbereiche und -Formate, Cach-Invalidierung rein grafikrelevante Hardware bleibt versteckt Standard-API und Treiber wird umgangen Stefan Auer
data parallel virtual machine (DPVM) (2) GPGPU alla AMD/ATI data parallel virtual machine (DPVM) (2) Stefan Auer
data parallel virtual machine (DPVM) (3) GPGPU alla AMD/ATI data parallel virtual machine (DPVM) (3) Implementierung für die X1k Serie heißt CTM (close to metal) Beschreibung der Architektur (ISA, Ressourcen, etc.) Runtime als low level Treiberkomponenten Support Librarys command buffer Generierung Speicherallokation Assembler Implementierung von high-level-Tools (Programmiersprachen, Debugger, Bibliotheken) bleibt Dritten überlassen Stefan Auer
DPVM/CTM Bewertung GPGPU alla AMD/ATI + interessant für Tool-Entwickler + ermöglicht low-level Optimierungen für viele Programmierer uninteressant, da ZU nah an Hardware (unausgereifter GPU-Assembler vs. ausgereiftes aber unpassendes Grafik-API) Stefan Auer
mögliche GPGPU Zukunft bei AMD GPGPU alla Nvidia mögliche GPGPU Zukunft bei AMD AMD Fusion heterogene Mehrkernprozessoren CPUs und GPUs auf einem Die Performancesteigerung bei Speicherzugriff denkbar Speicher CPU GPU Zeitraum: 2009-2013 General Purpose Data Centric Graphics Centric Media Centric Stefan Auer Quelle: AMD
fin