Upload
joern-dinkla
View
438
Download
1
Embed Size (px)
Citation preview
GPU-COMPUTING MIT CUDA UND OPENCLJörn Dinkla, para//el 2017, 31. März 2017, Heidelberg
GPU COMPUTING IST ÜBERALL
EINSATZ VON GPU-COMPUTING
Es locken große Speedups:
2 3 4 5 6 7 8 9 10 11 …
ProfiFortgeschrittenAnfänger
„Enabler“
ABER GPU-COMPUTING IST NICHT TRIVIAL
Entwickler
Kenntnisse, Lernbereitschaft, Zeit
Code
Reorganisation, Anpassung
Management
Time is Money
ZIEL: GRUNDLAGEN FÜR ERFOLGREICHEN EINSATZ
Grundlagen der Parallelisierung
Architektur CPU und GPU
Programmierung mit CUDA und OpenCL
Optimierung
2 3 4 5 6 7 8 9 10 11 …
Fortgeschritten
JÖRN DINKLA
Software-Developer und Berater
TechEd von OpenCL in Action
Konferenzen & Artikel
Auch: JVM, C++, JavaScript
A community of passionate
individuals whose purpose is to
revolutionize software design,
creation and delivery, while
advocating for positive social
change.
LINKS
Source-Code
https://github.com/jdinkla/parallel2017_gpucomputing
Weitere Vorträge und Schulungen
https://www.slideshare.net/dinkla
VOM MODELL ZUM BILD
Bilder aus Tomas Akenine-Mőller © 2002, Quelle: http://www.realtimerendering.com/
Vertex
Pixel
PROGRAMMIERBARE SHADER
Ab 2002 HLSL, Cg, GLSL
GPGPU UND GPU-COMPUTING
GPGPU
Generall Purpose computing on GPUs
2003 – 2008 ein „hack“
GPU-Computing
2007 erste Version von CUDA
2008 von OpenCL
2012 C++ AMP
FRAMEWORKS IM ÜBERBLICK
Device
Driver
C ++ 11
C ++
C
CUDARuntime
C++ AMP
MS DirectX
AMDNVIDIA
Thrust
C++-Wrapper
OpenCL
BoostCompute
IntelAMD
CUDADriver
OpenACC
VOR- UND NACHTEILE
CUDA
+ Verbreitung + C++ 11 - nur NVIDIA
OpenCL
+ Offen - C99 - Nicht so viele Libraries
C++ AMP
+ C++ 11 - DirectX - geringe Verbreitung
VERSIONEN DES APIS
CUDA
8.0 Pascal, Unified Memory
7.5 Lambdas, 7.0 C++
OpenCL
2.2 C++ Kernel (Zukunft)
2.1 SPIR-V, Dynamic Parallelism
VERSIONEN DER HARDWARE
Unterschiede zwischen Karten-Generationen
z. B. NVIDIA
Pascal: Unified Memory incl. Management
Maxwell: Dynamic Parallelism für alle
Kepler: Unified Memory
VERSIONEN VON OPENCL HARDWARE
Beispiel Intel®SDK 2016 R3
Siehe https://software.intel.com/file/529975/download
FÜR WELCHE API UND HARDWARE SCHREIBT MAN?
Am einfachsten: Für jeweils die Neueste
Neue Hardware preiswerter als Entwickler
Aber Kunden mit älteren Karten?
Maintenance-Probleme später berücksichtigen
SPMD - SINGLE PROGRAM MULTIPLE DATA
1. Übergebe ID als Parameter
2. Hole Daten anhand ID
3. Verarbeitung
4. Speichere Daten anhand ID
MAP: UNABHÄNGIGE ELEMENTE
for i = 0 to n-1 d[i] = f(s[i]);
CPUGPU Granularität
Daten-parallel
REDUKTION: ABHÄNGIGE ELEMENTE
Sequentiell
Zeit O(n), Arbeit O(n)
Parallel
Zeit O(log(n)), Arbeit O(n)
sum = 0for i = 0 to n-1
sum += s[i];
ANMERKUNG: MAPREDUCE
Verteilte Systeme, Cluster
Siehe http://www.drdobbs.com/database/hadoop-the-lay-of-the-land/240150854
FORK-JOIN-PATTERN
Eingabe 2 3 Ausgabe1
Eingabe
2a
2b
3 AusgabeFork Join1
AMDAHL‘S GESETZ
1 32
1 3
2a
2b
Siehe https://en.wikipedia.org/wiki/Amdahl's_law#/media/File:AmdahlsLaw.svg
1 3
2a
2b
2D-MAP
https://uploads5.wikiart.org/images/vincent-van-gogh/rest-work-after-millet-1890.jpg
2D-MAP
for y = 0 to n-1 for x = 0 to n-1
int i = y*width+xd[i] = f(s[i]);
0 1 2 3
0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
1
2
3
y*width+x
OPENMP
Präprozessor-Direktive
Runtime im Hintergrund
ARITHMETISCHE INTENSITÄT
for y = 0 to n-1 for x = 0 to n-1
int i = y*w+xd[i] = f(s[i]);
𝑎𝑖 =𝑅𝑒𝑐ℎ𝑒𝑛𝑜𝑝𝑒𝑟𝑎𝑡𝑖𝑜𝑛𝑒𝑛
𝑆𝑝𝑒𝑖𝑐ℎ𝑒𝑟𝑧𝑢𝑔𝑟𝑖𝑓𝑓𝑒
ARTEN DER AUSLASTUNG
Computation Bound
Alle Prozessoren 100% ausgelastet
Memory Bound
Bandbreite zum Speicher voll ausgelastet
Latency Bound
Warten auf die Daten
AUFBAU EINES COMPUTERS
CPU 1
Core
L1 / L2
Core
L1 / L2
L3 Cache
Memory Controller PCIe
Global Memory Devices
CPU 2
Core
L1 / L2
Core
L1 / L2
L3 Cache
QPI
Memory ControllerPCIe
Global MemoryDevices
≈25 ≈14
≈26
Bus ist „von-Neumann-Flaschenhals“
≈50
≈100
Transparent für Programmierer
OPTIMIERUNG DER CPU
Clock Frequency
Memory Hierarchy / Cache
Parallelizing ALU
Pipelining
Very-long Instruction
Words (VLIW)
Instruction-Level
parallelism (ILP)
Superscalar processors
Vector data types (SIMD)
Multithreaded
Multicore / Manycore
AMD RYZEN UND NVIDIA PASCAL
Mehr Platz für Kerne
NVIDIA PASCAL
60 SMs á 64 Kerne á 32 Bit
3840 Kerne
4 MB L2 Cache
1080: 2560 Kerne
1080 Ti: 3584 Kerne
https://devblogs.nvidia.com/parallelforall/wp-content/uploads/2016/04/gp100_block_diagram-1-624x368.png
AMD FIJI
64 CUs á 64 Kerne á 32 Bit
4096 Kerne
2 MB L2 Cache
Siehe http://www.anandtech.com/show/9390/the-amd-radeon-r9-fury-x-review/4
AUFBAU DER GPU
CPU 1
Core
L1 / L2
Core
L1 / L2
L3 Cache
Memory Controller PCIe
Global Memory Devices
≈25≈ 14
≈50
≈100
≈ 14
GPU
Global Memory
Constant Texture
Prozessor (SM, CU)
Shared / L1
Registers
C
L2 Cache
C
C
C
C
C
C
C
C
C
C
C
C
C
SM
SM
SM
SM
SM
SM
SM
SM
SM
SM
≈ 8000
≈ 1600
≈ 320 - 700
Schneller Device-Speicher
Daten-Lokalität
DIE UNTERSCHIEDE
1. Daten-Lokalität wegen Flaschenhals
GPU-IFIZIERUNG
Ho
st Algorithmu
sBuffer
Buffer‘
Ho
st
BufferBuffer
‘
Devic
e
KernelBuffer Buffer‘
HOST UND DEVICE
Host
Der „Kopf“ des Ganzen
Management, Synchronisation
Speicher, Queues, Events
Aufruf von Kerneln
Device
Traditionell „reine Arbeitspferde“, Ausnahme: Dynamic
Parallelism
SPEICHER ALLOKIEREN
C/C++
CUDA
OpenCL
MAP-2D FÜR CUDA
Alloc
Free
__host__
__device__
KERNEL-KONFIGURATION UND -AUFRUF
Aufruf
Synchronisation
Block & Grid
2D-MAP-KERNEL
Kernel
Template
ID berechnen
Wo ist die for-Schleife?
KERNEL-SCHEDULING
Abbildung von Berechnungen/Daten auf SMs
unter Berücksichtigung der Hardware-Constraints
4000 x 3000 Pixel
4000 Kerne
BLOCKGRÖßE DURCH HARDWARE DEFINIERT
Pascal-SM: 64 Kerne (128 Kepler, 192 Maxwell)
Fiji-CU: 64 Kerne
Kleinste Schedulingeinheit:
NVIDIA: Warp 32 Threads
AMD: Wavefront 64 Threads
SIMT - SINGLE INSTRUCTION MULTIPLE THREADS
Nur ein PC für Warp
if (threadIdx.x < 2) {expensive_function1();
} else {expensive_function2();
}
0 1 2 3
Divergenz
SIMT - SINGLE INSTRUCTION MULTIPLE THREADS
Bei sequentiellen Programmen (Single-Thread)
𝑇 = max(𝑇𝑖𝑓, 𝑇𝑒𝑙𝑠𝑒)
Laufzeit bei SIMT
𝑇 = 𝑇𝑖𝑓 + 𝑇𝑒𝑙𝑠𝑒
AUSWIRKUNG VON DIVERGENZ
Wenn divergente Threads vorhanden sind
Kerne sind idle
SM wird nicht ausgelastet
Berechnung dauert länger als notwendig
Daher Divergenz vermeiden
DIE UNTERSCHIEDE
1. Daten-Lokalität wegen Flaschenhals
2. Divergenz
GRID = DATEN/BLOCK
Grid: Einteilung der Daten in Blöcke
Beispiel
Daten 8x8
Block 4x4
→ Grid 2x2
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
0
1
2
3
4
5
6
7
0 1
2 3
Daten, Pixel, Voxel Grid
BESTIMMUNG DER ID
threadIdx innerhalb Block
blockIdx innerhalb Grid
blockDim Größe eines Blocks
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
0
1
2
3
4
5
6
7
0 1
2 3
Daten, Pixel, Voxel Grid
OCCUPANCY
Pascal-SM kann „gleichzeitig“
32 Blöcke, 64 Warps bzw. 2048 Threads
ausführbereit halten („in flight“, „resident“)
W0SM W1 W2 W3 W4 -- -- --
LATENCY HIDING
Speicherzugriffe sind langsam
Durch Berechnungen „überdecken“
Immer genügend Warps „in flight“ haben
DER Grund für gute Performance der GPU
Memory-Flaschenhals überwunden
W0SM W1 W2 W3 W4 -- -- --
Occupancy5/8
WARUM NICHT IMMER 100% OCCUPANCY?
Platz auf SM beschränkt
Warps auf SM teilen sich
Shared-Memory
L1-Cache
Register-Speicher
GPU
Global Memory
Constant Texture
Prozessor (SM)
Shared / L1
Registers
C
L2 Cache
C
C
C
C
C
C
C
C
C
C
C
C
C
SM
SM
SM
SM
SM
SM
SM
SM
SM
SM
OCCUPANCY-CALCULATOR
DIE UNTERSCHIEDE
1. Daten-Lokalität wegen Flaschenhals
2. Divergenz
3. Occupancy – Latency Hiding
2D-KERNEL
Unterschiedliche Layouts
8x1
4x2
2x4
1x8
0 1 2 3 4 5 6 7
0 1 2 3
4 5 6 7
0 1
2 3
4 5
6 7
0
1
2
3
4
5
6
7
8X1 ZUGRIFFE AUF DEN SPEICHER
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 1
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 2
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 3
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 4
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 5
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 6
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 7
4X2 ZUGRIFFE AUF DEN SPEICHER
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 0
4 5 6 7 4 5 6 7 4 5 6 7 4 5 6 7 4 5 6 7 1
0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 2
4 5 6 7 4 5 6 7 4 5 6 7 4 5 6 7 3
0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 4
4 5 6 7 4 5 6 7 4 5 6 7 4 5 6 7 5
0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 6
4 5 6 7 4 5 6 7 4 5 6 7 4 5 6 7 7
2X4 ZUGRIFFE AUF DEN SPEICHER
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
2 3 2 3 2 3 2 3 2 3 2 3 2 3 2 3 2 3 1
4 5 4 5 4 5 4 5 4 5 4 5 4 5 4 5 4 5 2
6 7 6 7 6 7 6 7 6 7 6 7 6 7 6 7 6 7 3
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 4
2 3 2 3 2 3 2 3 2 3 2 3 2 3 2 3 5
4 5 4 5 4 5 4 5 4 5 4 5 4 5 4 5 6
6 7 6 7 6 7 6 7 6 7 6 7 6 7 6 7 7
1X8 ZUGRIFFE AUF DEN SPEICHER
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
COALESCING
Zugriffe innerhalb eines Warps können in
32, 64 und 128 Byte „zusammengefasst“ werden0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 0
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 4 5 6 7 4 5 6 7 4 5 6 7 4 5 6 7 1
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 2
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 4 5 6 7 4 5 6 7 4 5 6 7 4 5 6 7 3
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 4
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 4 5 6 7 4 5 6 7 4 5 6 7 4 5 6 7 5
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 6
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 4 5 6 7 4 5 6 7 4 5 6 7 4 5 6 7 7
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
2 3 2 3 2 3 2 3 2 3 2 3 2 3 2 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
4 5 4 5 4 5 4 5 4 5 4 5 4 5 4 5 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
6 7 6 7 6 7 6 7 6 7 6 7 6 7 6 7 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
2 3 2 3 2 3 2 3 2 3 2 3 2 3 2 3 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
4 5 4 5 4 5 4 5 4 5 4 5 4 5 4 5 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
6 7 6 7 6 7 6 7 6 7 6 7 6 7 6 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
1 pro Warp insg. 16
2 pro Warp insg. 32
4 pro Warp insg. 64
8 pro Warp insg. 128
X-Y- STATT Y-X- ZUGRIFF
CPU „hintereinander“: for y … for x …
GPU „nebeneinander“: for x .. for y …
Gut für CPU Gut für GPU
ARRAY OF STRUCTURES VS. STRUCTURE OF ARRAYS
Siehe Stroustrup, „C++ Prog. Language“, 4th ed., p. 207
mem 0 1 2 3 4 5
AoS value 1 2 3 4 5 6
SoA value 1 3 5 2 4 6
AOS - ARRAY OF STRUCTURES
mem 0 1 2 3 4 5
AoS value 1 2 3 4 5 6
SoA value 1 3 5 2 4 6
0 1 20 1 2
Verschwenden Bandbreite
SOA - STRUCTURE OF ARRAYS
mem 0 1 2 3 4 5
AoS value 1 2 3 4 5 6
SoA value 1 3 5 2 4 6
0 1 2 0 1 2
Nebeneinander gut
ALIGNMENT IST AUCH WICHTIG
31 63 95 127 159 191 223 255 287 319 351 383
31 63 95 127 159 191 223 255 287 319 351 383
ALIGNMENT ERZWINGEN
sizeof(SoA) == 24
sizeof(SoA2) == 32
DIE UNTERSCHIEDE
1. Daten-Lokalität wegen Flaschenhals
2. Divergenz
3. Occupancy – Latency Hiding
4. Coalescing von Speicherzugriffen
2D-MAP IN OPENCL C++ BINDING
Programmbauen
Context & Queue
2D-MAP IN OPENCL C++ BINDINGDevice-
Speicher
Argumente
2D-MAP IN OPENCL C++ BINDING
Sync
H2D
Kernel-Konfiguration
D2H
2D-MAP IN OPENCL - KERNEL
__kernel
__global
ID
no C++no templates
SMOOTHING
STENCIL-PATTERN
Durchschnitt, Smoothing, PDEs
Nachbarschaften
von-Neumann, Moore
Optimierung
Speicherzugriffe und Cache
Berechnungen speichern
SMOOTHING-ALGORITHMUS
for all pixel at x,y in imagesum = 0, c = 0for all dx,dy in neighborhoodsum += e[x+dx, y+dy]c += 1
a[x,y] = sum / c
𝑎𝑖 =𝑅𝑒𝑐ℎ𝑒𝑛𝑜𝑝𝑒𝑟𝑎𝑡𝑖𝑜𝑛𝑒𝑛
𝑆𝑝𝑒𝑖𝑐ℎ𝑒𝑟𝑧𝑢𝑔𝑟𝑖𝑓𝑓𝑒
OPTIMIERUNG
Ermittle das Bottleneck mit Profiler
Verwende Amdahls Gesetz für Abschätzung
Siehe https://devblogs.nvidia.com/parallelforall/cuda-8-features-revealed/
TOOLS
z. B. NVVP‘s „Guided Analysis“
Viele nützliche Infos und Analysen
MAXIMALE RECHENLEISTUNG
TFLOPS = Anzahl Kerne * Takt * 2 / (1000^2)
Beispiel GTX 1080 Ti:
3584 * 1480 * 2 ≈ 10.61 TFLOPS
Beispiel GTX 1080:
2560 * 1607 * 2 ≈ 8.23 TFLOPS
FMA: fusedmultiply add
Basistakt
IN DER PRAXIS WENIGER
1080: 4,5 TFLOPS
OPTIMIERUNG (AUS DER CUDA-DOKU)
Maximiere …
1. Parallelität
2. Speicherdurchsatz
3. Berechnungsdurchsatz
MÖGLICHKEITEN FÜR PARALLELITÄT
CPU 1
Core
L1 / L2
Core
L1 / L2
L3 Cache
Memory Controller PCIe
Global Memory Devices
GPU
Global Memory
Constant Texture
Prozessor (SM)
Shared / L1
Registers
C
L2 Cache
C
C
C
C
C
C
C
C
C
C
C
C
C
SM
SM
SM
SM
SM
SM
SM
SM
SM
SMCPU || GPUCores
Calc || Mem
SMs
Cores
HT
Kernel || Copy
MAXIMIERE PARALLELITÄT
Auslastung der GPU
Parallele Kernel, Streams
Auslastung der SMs
Thread-Block, Occupancy
Auslastung der Kerne
„Instruction level parallelism“ (ILP)
AUSLASTUNG BEI SCHLEIFE
Devic
eH
os
t
I
KI O
O I
KI O
O I
KI O
O
La
st Nicht
ausgelastet
Busausgelastet
STREAMS & OVERLAP
Schritt Ein Stream Stream 1 Stream 2 Stream 1 Stream 2
1 H2D H2D H2D
2 Kernel 1 Kernel 1 H2D Kernel 1 H2D
3 D2H D2H Kernel 2 D2H Kernel 2
4 H2D D2H H2D D2H
5 Kernel 2 H2D Kernel 3
6 D2H Kernel 3 D2H
7 H2D D2H
8 Kernel 3
9 D2H
Kernel + Kopie
überlappend
Kernel + Kopie und
H2D und D2H
MAXIMIERE SPEICHERDURCHSATZ
Daten-Lokalität
Minimiere Kopien
On-Chip-Speicher: Register, Shared-Memory
Coalesced Zugriffe
Pinned-Host-Speicher
MAXIMIERE BERECHNUNGSDURCHSATZ
Minimiere Divergenz
Berechnen statt Speichern
Loop-Unrolling
Fast-Math
Float statt Double
Fusion von Kerneln
NOCHMAL: DIE UNTERSCHIEDE
1. Daten-Lokalität
2. Divergenz
3. Occupancy – Latency Hiding
4. Coalescing von Speicherzugriffen
METHODIK
Entwickle
1. „Golden Code“ sequentiell und korrekt
2. Parallelisiere auf CPU
3. Parallelisiere und transformiere zu GPU-Code
Unit-Tests stellen Korrektheit sicher
FAZIT
Happy GPU-Computing !
2 3 4 5 6 7 8 9 10 11 …
Fortgeschritten