to get the file

Transkrypt

to get the file
Instrukcja
6
1
Współczesne procesory graficzne
Temat: Wstęp do programowania w CUDA
Przygotował: mgr inż. Tomasz Michno
Wstęp
Programowanie z użyciem CUDA różni się nieco od programowania z użyciem OpenCL. Pod pewnymi względami jest ono dużo prostsze, ponieważ
wiele operacji niezbędnych do zaprogramowania w OpenCL wykonuje za nas
kompilator dostarczony wraz z SDK.
W przeciwieństwie do OpenCL, cały kod programu, zarówno dla hosta (dla
CPU), jak i dla karty graficznej (dla GPU) znajduje się w jednym pliku
o rozszerzeniu .cu. Funkcję główną kernela należy oznaczyć przedrostkiem
__global__, natomiast funkcje pomocnicze (oraz wszelkie zmienne po stronie
karty graficznej) przedrostkiem __device__. Wszystkie pozostałe zmienne
i funkcje bez tych przedrostków są uznawane za znajdujące się po stronie
hosta.
Przykładowy kod kernela przepisującego elementy jednej tablicy do drugiej
może wyglądać następująco:
1
2
3
4
5
6
__global__ v o i d mojKernel ( c o n s t f l o a t ∗ we , f l o a t ∗ wy , i n t N)
{
i n t i = blockDim . x ∗ b l o c k I d x . x + t h r e a d I d x . x ;
i f ( i < N)
wy [ i ]=we [ i ] ;
}
Widać tutaj pewne różnice w porównaniu z kodem w OpenCL, jednak
ogólnie kod wygląda podobnie.
Pierwszą różnicą jest sam przedrostek głównej funkcji kernela - zamiast
__kernel__ używany jest przedrostek __global__, który oznacza, że do
tej funkcji będzie możliwy dostęp z kodu hosta (w celu uruchomienia). Kolejna różnica to brak przedrostków przy wskaźnikach w parametrach - domyślnie są one traktowane jako wskaźniki odwołujące się do pamięci karty
graficznej. Ostatnia różnica to nieco bardziej skomplikowane pobieranie globalnego identyfikatora work-item’a (linia nr 3). W C for CUDA nie ma
funkcji get_global_id(), takiej jak w OpenCL. Identyfikator należy wyliczać
ręcznie. W przypadku prostych programów mogłoby często wystarczyć od1
czytanie wartości threadIdx.x, jednak bezpieczniej jest zawsze stosować pełne
obliczanie identyfikatora (takie jak w linii nr 3).
Kod hosta, potrzebny do utworzenia buforów, przesłania danych i uruchomienia kernela jest znacznie prostszy niż w języku OpenCL, co można zauważyć
na poniższym przykładzie:
1
2
cudaMalloc ( ( v o i d ∗ ∗ )&d_we , s i z e ) ;
cudaMalloc ( ( v o i d ∗ ∗ )&d_wy , s i z e ) ;
3
4
5
int threadsPerBlock = 256;
i n t b l o c k s P e r G r i d = (ROZMIAR_TABLICY + t h r e a d s P e r B l o c k − 1 ) /
threadsPerBlock ;
6
7
cudaMemcpy (d_we , h_we , s i z e , cudaMemcpyHostToDevice ) ;
8
9
mojKernel<<<b l o c k s P e r G r i d , t h r e a d s P e r B l o c k >>>(d_we , d_wy, N) ;
10
11
cudaMemcpy (h_wy , d_wy , s i z e , cudaMemcpyDeviceToHost ) ;
Funkcje do tworzenia i przesyłania danych są odpowiednikami funkcji z języka C.
Do tworzenia buforów po stronie karty graficznej (linie 1 i 2) używana jest
funkcja cudaMalloc(), która jako pierwszy parametr przyjmuje wskaźnik na
wskaźnik, który będzie przechowywał adres bufora w pamięci GPU oraz rozmiar przydzielonej pamięci. Z kolei zwalnianie pamięci przydzielonej dla tego
bufora można wykonać wywołując funkcję cudaFree(), która jako parametr
przyjmuje wskaźnik na ten bufor.
Przesyłanie danych pomiędzy pamięciami odbywa się bardzo podobnie jak w
języku C - używając funkcji cudaMemcpy(). Jej parametry są następujące:
1. wskaźnik na obszar pamięci, gdzie zostaną skopiowane dane,
2. wskaźnik na pamięć, z której będą kopiowane dane,
3. rozmiar kopiowanego obszaru,
4. flaga informująca, jakie ma zostać wykonane kopiowanie:
cudaMemcpyHostToDevice - kopiowanie z pamięci głównej do pamięci karty graficznej
cudaMemcpyDeviceToHost - kopiowanie z pamięci karty graficznej
do pamięci głównej
Uruchamianie kernela jest również dużo prostsze niż w OpenCL i w dużej mierze przypomina wywoływanie zwykłej funkcji (linia nr 9) - podajemy nazwę
2
kernela, pomiędzy znakami «< oraz »> określamy ile ma być uruchomionych
work-item’ów. Potem, tak jak przy zwykłej funkcji w nawiasach powinny
znaleźć się parametry.
2
Zadanie
Napisać program/programy, które będą wykonywały operacje: mnożenia i
dzielenia wektora (tablicy) przez skalar (liczbę), dodawania wektorów, odejmowania wektorów. Programy powinny wykonywać obliczenia na karcie graficznej.
3