Algorithm for Terrain/Heightmap Generation in CUDA

Werbung
Hauptseminar MAP08
Random Heightmap on GPU
Hannes Stadler, Sebastian Graf
[email protected], [email protected]
Betreuung: Matthias Hartl, Hritam Dutta, Frank Hannig
Hardware-Software-Co-Design
Universität Erlangen-Nürnberg
Friedrich-Alexander-Universität Erlangen-Nürnberg
Hannes Stadler, Sebastian Graf
1
Gliederung







Was ist eine Heightmap?
Fault Algorithmus
Parallelisierung des Fault Algorithmus
Umsetzung in Cuda
Benchmarks
Probleme
Zusammenfassung
Friedrich-Alexander-Universität Erlangen-Nürnberg
Hannes Stadler, Sebastian Graf
2
Was ist eine Heightmap?




dt. Höhenfeld
Zwei-dimensionales Skalarfeld
Beschreibung eines Höhenreliefs
Jedem Punkt ist ein Wert zugeordnet, der dessen Höhe
angibt
Friedrich-Alexander-Universität Erlangen-Nürnberg
Hannes Stadler, Sebastian Graf
3
Fault Algorithmus
 Erzeuge ein ebenes 2-dimensionales Grid
Algorithmus:
 Wähle zwei zufällige Punkte im R²
 Lege Gerade durch diese zwei Punkte
 Erhöhe alle Punkte auf der eine Seite der Gerade,
erniedrige die auf der anderen um einen konstanten Wert
 Wiederhole diese Schritte für eine vorher festgelegt Anzahl
von Iterationen
Friedrich-Alexander-Universität Erlangen-Nürnberg
Hannes Stadler, Sebastian Graf
4
Fault Algorithmus
 Pseudo-Code:
foreach(Iteration)
{
CreateRandomLine();
foreach( RowOfImage)
{
foreach(PixelOfRow)
{
processNewValue();
}
}
}
Friedrich-Alexander-Universität Erlangen-Nürnberg
Hannes Stadler, Sebastian Graf
5
Fault Algorithmus
Friedrich-Alexander-Universität Erlangen-Nürnberg
Hannes Stadler, Sebastian Graf
6
Variationen des Fault Algorithmus
 Multiplikation der Geraden mit Sinus/Cosinus um weiche
Übergänge an den Kanten zu bekommen
Friedrich-Alexander-Universität Erlangen-Nürnberg
Hannes Stadler, Sebastian Graf
7
Parallelisierung Fault Algorithmus
 Algorithmus besteht aus drei for-Schleifen
 Parallelisierung der Schleifen:

1.for-Schleife durchläuft Anzahl der Iterationen
 mehrere Iterationen parallel möglich, da unabhängig

2.for-Schleife führt Berechung für jede Zeile im Bild aus
Parallelisierbar, da Zeilen unabhängig

3.for-Schleife arbeitet auf genau einer Zeile
Pro Zeile eine Grenze ( Schnittpunkt mit der Geraden),
Aufteilung in Teil der erhöht und der erniedrigt wird
Friedrich-Alexander-Universität Erlangen-Nürnberg
Hannes Stadler, Sebastian Graf
8
Umsetzung in CUDA
 Naiver Ansatz:



Laden des Grids in Global Memory
Threads arbeiten auf Daten im Global Memory
Probleme mit Nebenläufigkeit, Performance etc.
 Optimierter Ansatz:




Aufteilung des Grids in Blöcke
Block in Shared Memory laden
Berechnung aller Iterationen für jeweiligen Block
Danach wieder zurück in Global Memory speichern
 Weitere Optimierungen:


Coalesced Speicherzugriff der Threads
Zugriff auf Zufallzahlen über Constant Memory
-
Oder: Zufallszahlen auf der GPU erzeugen
Friedrich-Alexander-Universität Erlangen-Nürnberg
Hannes Stadler, Sebastian Graf
9
Quellcode – Kernelaufruf
int CreateHeightMap()
{
CUT_DEVICE_INIT();
dim3 threads(TPL,ZPB);// 16 x 16
dim3 grid(WIDTH/BLOCKWIDTH,HEIGHT/ZPB); // 1k x 1k -> 64 x 8, bei 2k x 2k -> 128 x 16
int rand[ITERATIONS*4];
for(i=0;i<ITERATIONS*4;i++){
// rand[ ] mit Zufallszahlen füllen
}
CUDA_SAFE_CALL(cudaMemcpyToSymbol(rand_d, rand ,ITERATIONS*4*sizeof(int),0) );
GLfloat* HeightMap_d;
CUDA_SAFE_CALL(cudaMalloc((void**) &HeightMap_d, WIDTH*HEIGHT*sizeof(float)));
splitpicture<<<grid, threads>>>(HeightMap_d);
CUDA_SAFE_CALL(cudaMemcpy(HeightMap, HeightMap_d , WIDTH*HEIGHT*sizeof(float),
cudaMemcpyDeviceToHost) );
}
Friedrich-Alexander-Universität Erlangen-Nürnberg
Hannes Stadler, Sebastian Graf
10
Quellcode – Kernel
extern __constant__ int rand_d[];
__global__ void splitpicture(GLfloat *HeightMap_d)
{
__shared__ float aRow[ZPB][SMB/ZPB]; // Init. mit default-Wert weggelassen
for(int i = 0; i < ITERATIONS; i++){
// Variableninitialisierung, random-Werte, „Wendestelle“ bestimmen
for(int a=0;a<((SMB/ZPB)/TPL);a++){
int rel_pos=threadIdx.x*(BLOCKWIDTH/TPL) + a;
aRow[threadIdx.y][rel_pos] +=4*faktor*((float)value)*(1-__sinf(phi)/WAVEWIDTH);
}
}
for(int j = 0; j < ((SMB/ZPB)/TPL); j++){
HeightMap_d[offset+threadIdx.y*WIDTH+threadIdx.x*(BW/TPL)+j] =
aRow[threadIdx.y][threadIdx.x*(BLOCKWIDTH/TPL)+j];
}
}
Friedrich-Alexander-Universität Erlangen-Nürnberg
Hannes Stadler, Sebastian Graf
11
Probleme
 CUDA-Kernel kann unter X-Linux leider nur max. 5sek
laufen, bevor er terminiert wird



der Komplexität der Aufgabe ist ein Ende gesetzt ;-)
Komischerweise klappts manchmal doch
ab und an stürzt auch die GPU ab
 Bildgrößen müssen vielfache von Zweierpotenzen sein

Im Idealfall: sind Zweiterpotenzen
 Ursprüngliche (naive) Implementierung hatte (im Vergleich
zur Finalen Version) nur mäßige Performanz

Man muss schon manchmal etwas genauer nachdenken
 Dokumentation von CUDA teilweise ungenau

z.B. Shared Memory kann nicht voll ausgenutzt werden
Friedrich-Alexander-Universität Erlangen-Nürnberg
Hannes Stadler, Sebastian Graf
12
Benchmarks - CPU
• CPU-Implementierung ( P4 – 3,0GHz )
Bildgröße
Iterationen
Laufzeit
MPixel/s
512x512
256
7,2s
9,32
1024x1024
256
28,7s
9,35
1024x1024
512
59,4s
9,04
1024x1024
1024
120,7s
8,90
1024x1024
2048
229s
9,38
1024x1024
4096
457,35s
9,39
2048x2048
4096
1834s
9,37
4096x4096
2048
~3700s
~9,37
Friedrich-Alexander-Universität Erlangen-Nürnberg
Hannes Stadler, Sebastian Graf
13
Benchmarks - CUDA
• CUDA-Implementierung (GF 8800 GTX )
Bildgröße
512x512
1024x1024
1024x1024
1024x1024
1024x1024
1024x1024
2048x2048
2048x2048
2048x2048
4096x4096
4096x4096
3072x4096
Iterationen
256
256
512
1024
2048
4096
1024
2048
4096
1024
2048
2048
Friedrich-Alexander-Universität Erlangen-Nürnberg
Hannes Stadler, Sebastian Graf
Laufzeit
0,015s
0,059s
0,118s
0,235s
0,47s
0,94s
0,92s
1,84s
3,68s
3,60s
7,20s
7,40s
MPixel/s
4473,92
4549,75
4549,75
4569,11
4569,11
4569,11
4668,44
4668,44
4668,44
4772,19
4768,21
3484,76
SpeedUp
480
486,44
503,39
513,62
487,23
486,54
497,83
498,37
498,37
509,72
513,46
372,55
14
Zusammenfassung
 Auf der CPU teilweise nicht zumutbare Ausführungszeiten

Allerdings noch größere Problemgrößen lösbar als mit
CUDA, da kein Timeout
 Primitive CUDA-Implementierung


Relativ schnell lauffähig
Speedup bereits zwischen 10 und 40
 Endversion: Enormer Speedup von ~ 500



Bereits bei kleinen Eingabedaten
Sehr gut skalierend
Allerdings auch nur durch viel Arbeit erreichbar
Friedrich-Alexander-Universität Erlangen-Nürnberg
Hannes Stadler, Sebastian Graf
15
Demo
Genug geredet, jetzt wird’s gezeigt!
Oder gibt’s bisher schon
Fragen?
Friedrich-Alexander-Universität Erlangen-Nürnberg
Hannes Stadler, Sebastian Graf
16
Herunterladen