Prezentacja

Transkrypt

Prezentacja
CUDA
cudniejsze przyk|ady
Agenda:
| CPU vs. GPU
|Mnożenie macierzy – CPU
| Mnożenie macierzy - GPU
|Sploty
Macierze – CPU vs. GPU
CPU:
| Mnożenie wykonywane w kolejnych
iteracjach pętli.
| Przechodzimy przez pierwszy wiersz
tabeli M i pierwszą kolumnę macierzy
N, w pętli liczymy ich iloczyn skalarny
i od razu zapisujemy go w macierzy
wyjściowej.
GPU:
| paralelne wykonanie
mnożenia macierzy
właściwego
| każdy wątek wpisuje jeden element
do macierzy wynikowej
Cdn...
CPU
Istotny dla zrozumienia różnicy pomiędzy tradycyjnym mnożeniem macierzy, a
analogicznymi obliczeniami na GPU jest fakt linearnego adresowania macierzy w
pierwszym przypadku.
Właśnie z linearnego adresowania macierzy wynika występujący w iteracji indeks
postaci:
– i*Width+k. (i – numer wiersza; Width – wymiar n macierzy; k-numer kolumny)
Adresowanie linearne pokazano schematycznie na ilustracji:
CPU
Jak widad, jest to podejście typowo sekwencyjne…
Coś ciekawszego, czyli:
GPU
GPU - (C = AB)
Wersja najprostsza:
Jest to wersja niezoptymalizowana.
Każdy half warp oblicza jeden rząd
tile’a C, polegając przy tym na
jednym rzędzie z A i całym tile’u z B.
W każdej iteracji pętli wszystkie
wątki w half warpie czytają tę samą
wartośd z pamięci globalnej.
__global__ void
simpleMultiply(float *a, float* b,
float *c, int N)
{
int row = blockIdx.y *
blockDim.y + threadIdx.y;
int col = blockIdx.x *
blockDim.x + threadIdx.x;
float sum = 0.0f;
for (int i = 0; i <
TILE_DIM; i++) {
sum += a[row*TILE_DIM+i] *
b[i*N+col];
}
c[row*N+col] = sum;
}
Wersja 2:
GPU - (C = AB)
Pierwszym
z
możliwych
ulepszeo jest wykorzystanie
pamięci współdzielonej. W
drugiej
wersji
algorytmu
wczytujemy tile z A do pamięci
współdzielonej.
__global__ void
coalescedMultiply(float *a, float*
b, float *c,
int N)
{
__shared__ float
aTile[TILE_DIM][TILE_DIM];
int row = blockIdx.y *
blockDim.y + threadIdx.y;
int col = blockIdx.x *
blockDim.x + threadIdx.x;
float sum = 0.0f;
aTile[threadIdx.y][threadIdx.x]
= a[row*TILE_DIM+threadIdx.x];
for (int i = 0; i < TILE_DIM;
i++) {
sum += aTile[threadIdx.y][i]*
b[i*N+col];
}
c[row*N+col] = sum;
}
Wersja 3:
GPU - (C = AB)
Kolejnym możliwym
ulepszeniem jest
jednorazowe wczytywanie
całego rzędu macierzy B do
pamięci współdzielonej.
__global__ void sharedABMultiply(float *a,
float* b, float *c,
int N)
{
__shared__ float aTile[TILE_DIM][TILE_DIM],
bTile[TILE_DIM][TILE_DIM];
int row = blockIdx.y * blockDim.y +
threadIdx.y;
int col = blockIdx.x * blockDim.x +
threadIdx.x;
float sum = 0.0f;
aTile[threadIdx.y][threadIdx.x] =
a[row*TILE_DIM+threadIdx.x];
bTile[threadIdx.y][threadIdx.x] =
b[threadIdx.y*N+col];
__syncthreads();
for (int i = 0; i < TILE_DIM; i++) {
sum += aTile[threadIdx.y][i]*
bTile[i][threadIdx.x];
}
c[row*N+col] = sum;
}
Porównanie:
Capability 1.1 - NVIDIA GeForce
GT 9600M in a MacBook
Pro Laptop, 4 multiprocessors,
32 cores
Capability 1.2 - NVIDIA GeForce
GT 330M in a MacBook Pro
Laptop, 6 multiprocessors, 48
cores
Porównanie:
Capability 1.3 - NVIDIA Tesla
C1060 running in Earlham's
cluster, 30 multiprocessors, 240
cores
Capability 2.0 - NVIDIA Tesla
M2070 at the Texas Advanced
Computing Center, 14
multiprocessors, 448 cores
Matrix Multiplication with CUDA | A basic introduction
to the CUDA programming model
Robert Hochberg
Porównanie:
NVIDIA GeForce NVIDIA GeForce
Optymalizacja
GTX 280(1.3) GTX 8800(1.0)
Bez optymalizacji
8.7 GBps
0.7 GBps
__shared__ float aTile
14.3 GBps
8.2 GBps
__shared__ float aTile, bTile
29.7 GBps
15.7 GBps
Matrix Multiplication with CUDA |
NVIDIA CUDA C Best Practices Guide
Sploty
Splot znajduje szerokie zastosowanie w przetwarzaniu obrazów. Operacja splotu
oblicza nową wartośd piksela obrazu na podstawie wartości pikseli sąsiadujących.
Przed zastosowaniem splotu:
Po zastosowaniu splotu:
Sploty
Simple box blur:
maska:
(jak widad, maska ma efekt uśredniający)
Przed zastosowaniem splotu:
Po zastosowaniu splotu:
Sploty
Gaussian blur:
Przed zastosowaniem splotu:
maska:
Po zastosowaniu splotu:
Sploty
Naiwna
implementacja:
W
najprostszej
wersji
implementacji splotu każdy blok
wątków przetwarza jeden blok
obrazu. Każdy wątek generuje na
wyjściu jeden piksel.
Sploty
Brutalna konfrontacja:
(z rzeczywistością)
Po
zmodyfikowaniu
naiwnego
algorytmu zagadnienie zaczyna się
komplikowad…
Uwzględnienie
w
algorytmie
„otoczki”,
niezbędnej
do
przeliczenia brzegowych pikseli
powoduje, że wątki odpowiedzialne
wcześniej
za
wczytanie
„otaczających”
pikseli
będą
bezczynne
przez
cały
czas
przeliczania maski.
Autorki
| Urszula Jędrzejczak
| Katarzyna Ostrowicz
Koniec
Bibliografia:
|
„CUDA by Example: An Introduction to General-Purpose
GPU Programming” Jason Sanders,Edward Kandrot
|
„Programming Massively Parallel Processors: A Hands-on
Approach”
David B. Kirk, Wen-mei W. Hwu
|
|
Dokumentacja NVIDIA: CUDA C Best Practices Guide
Version 3.1 z 2010-05-28
|
Image Convolution with CUDA Victor
Podlozhnyuk[http://developer.download.nvidia.com/compute/cuda/1.1-
Beta/x86_64_website/projects/convolutionSeparable/doc/convolutionSeparabl
e.pdf]
|
Matrix Multiplication with CUDA | A basic introduction to
the CUDA programming modelRobert Hochberg
[http://www.shodor.org/media/content//petascale/materials/UPModule
s/matrixMultiplication/moduleDocument.pdf]
|
http://www.aishack.in/2010/08/image-convolutionexamples/