CUDA

19
CUDA cudniejsze przyk|ady

description

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. - PowerPoint PPT Presentation

Transcript of CUDA

Page 1: CUDA

CUDAcudniejsze przyk|ady

Page 2: CUDA

Agenda:

| CPU vs. GPU| Mnożenie macierzy – CPU| Mnożenie macierzy - GPU| Sploty

Page 3: CUDA

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 właściwego mnożenia macierzy

| każdy wątek wpisuje jeden element do macierzy wynikowej

Cdn...

Page 4: CUDA

CPUIstotny 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:

Page 5: CUDA

CPU

Jak widać, jest to podejście typowo sekwencyjne…

Page 6: CUDA

GPUCoś ciekawszego, czyli:

Page 7: CUDA

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ść 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;

}

Page 8: CUDA

GPU - (C = AB)Wersja 2:Pierwszym z możliwych ulepszeń 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;

}

Page 9: CUDA

GPU - (C = AB)Wersja 3: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;}

Page 10: CUDA

Porównanie:Capability 1.1 - NVIDIA GeForce GT 9600M in a MacBookPro Laptop, 4 multiprocessors, 32 cores

Capability 1.2 - NVIDIA GeForce GT 330M in a MacBook ProLaptop, 6 multiprocessors, 48 cores

Page 11: CUDA

Porównanie:Capability 1.3 - NVIDIA Tesla C1060 running in Earlham'scluster, 30 multiprocessors, 240 cores

Capability 2.0 - NVIDIA Tesla M2070 at the Texas AdvancedComputing Center, 14 multiprocessors, 448 cores

Matrix Multiplication with CUDA | A basic introductionto the CUDA programming modelRobert Hochberg

Page 12: CUDA

Porównanie:

Matrix Multiplication with CUDA | NVIDIA CUDA C Best Practices Guide

Optymalizacja

NVIDIA GeForce

GTX 280(1.3)

NVIDIA GeForce

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

Page 13: CUDA

Sploty

Przed zastosowaniem splotu: Po zastosowaniu splotu:

Splot znajduje szerokie zastosowanie w przetwarzaniu obrazów. Operacja splotu oblicza nową wartość piksela obrazu na podstawie wartości pikseli sąsiadujących.

Page 14: CUDA

Sploty

Simple box blur: maska:(jak widać, maska ma efekt uśredniający)

Przed zastosowaniem splotu: Po zastosowaniu splotu:

Page 15: CUDA

Sploty

Gaussian blur: maska:

Przed zastosowaniem splotu: Po zastosowaniu splotu:

Page 16: CUDA

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.

Page 17: CUDA

Sploty

Brutalna konfrontacja:(z rzeczywistością)

Po zmodyfikowaniu naiwnego algorytmu zagadnienie zaczyna się komplikować…

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.

Page 18: CUDA

Autorki| Urszula Jędrzejczak

| Katarzyna Ostrowicz

Page 19: CUDA

KoniecBibliografia:| „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/convolutionSeparable.pdf]

| Matrix Multiplication with CUDA | A basic introduction to the CUDA programming modelRobert Hochberg [http://www.shodor.org/media/content//petascale/materials/UPModules/matrixMultiplication/moduleDocument.pdf]

| http://www.aishack.in/2010/08/image-convolution-examples/