Elemi CUDA program
A legelemibb CUDA program nem más, mint egy kernelhívás. Lássuk is ezt egy példán keresztül:
global void kernel()
{
}
int main()
{
kernel<<< 1; 1 >>>();
return 0;
}
Ez a program mindössze annyit csinál, hogy egy szimpla egyetlen szálat tartalmaz
ó kernelt elindít a GPU-n, ami mivel nem tartalmaz semmit, azonnal vissza is
tér, az alkalmazás futása pedig ezután véget ér. Na de következzen valami értelmesebb
alkalmazás.
Szerzõ: Forster Richárd
Év: 2011
Környezet:
- Microsoft Visual Studio 2010
- NVIDIA CUDA Toolkit 4.0
- NVIDIA GeForce GTX 580
Adatmozgatás GPU és CPU között
A hivatalos NVIDIA dokumentációk terminológiájának megfelelõen ettõl kezdve a
példákban a GPU-ra
device néven, míg a CPU-ra
host néven fogunk hivatkozni.
Lássunk is rögtön egy példát az adatmozgatásra:
#include <iostream>
using namespace std;
global void kernel(int* d adatok)
{
*d adatok += *d adatok;
}
int main()
{
int *h adatok, *d adatok;
h adatok = (int*)malloc(sizeof(int));
cudaMalloc(&d adatok,sizeof(int));
*h adatok = 10;
cudaMemcpy(d adatok,h adatok,sizeof(int),cudaMemcpyHostToDevice);
kernel<<< 1; 1 >>>(d adatok);
cudaMemcpy(h adatok,d adatok,sizeof(int),cudaMemcpyDeviceToHost);
cout << *h adatok;
free(h adatok);
cudaFree(d adatok);
return 0;
}
A memóriák közötti másolás iránya a másolást végzõ függvény utolsó paraméterétõl függ. Ha a hostról a
devicera akarunk másolni, akkor a
cudaMemcpyHostToDevice paramétert kell
megadnunk negyedikként, míg fordított esetben a
cudaMemcpyDeviceToHost-ot. Ekkor az irányoknak megfelelõen az elsõ két változó típusa is változik.
A program egyetlen szám, a 10-es kétszeresét számolja GPU-n és adja vissza az eredményt, tehát 20-at.
Szerzõ: Forster Richárd
Év: 2011
Környezet:
- Microsoft Visual Studio 2010
- NVIDIA CUDA Toolkit 4.0
- NVIDIA GeForce GTX 580
Párhuzamos GPU program
Lássuk hát a lényeget, egy valódi többszálú GPU alkalmazást.
#include <iostream>
using namespace std;
global void kernel(int* d adatok)
{
int index = blockIdx.x*blockDim.x+threadIdx.x;
d adatok[index] *= d adatok[index];
}
int main()
{
int *h adatok, *d adatok;
h adatok = (int*)malloc(sizeof(int)*10);
cudaMalloc(&d adatok,sizeof(int)*10);
for(int i = 0 ; i < 10 ; ++i)
h adatok[i] = i+1;
cudaMemcpy(d adatok,h adatok,sizeof(int)*10,cudaMemcpyHostToDevice);
kernel<<< 1; 10 >>>(d adatok);
cudaMemcpy(h adatok,d adatok,sizeof(int)*10,cudaMemcpyDeviceToHost);
for(int i = 0 ; i < 10 ; ++i)
cout << h adatok[i];
free(h adatok);
cudaFree(d adatok);
return 0;
}
Forráskód letöltése
Az alkalmazás célja, hogy veszi a számokat 1-tõl 10-ig és párhuzamosan kiszámolja mindegyiknek a négyzetét. Vegyük észre, hogy az elõzõ
programhoz képest a main függvény csupán az adatok számában, annak feltöltési módjában, a kernel folyamatszámában, valamint a másoláskor átmozgatandó adatok
mennyiségében különbözik. Jelen esetben minden 10 számunk van, így 10 folyamat kerül létrehozásra. A kernel maga egy plusz sorral b®vült, mely a gridben szereplõ blokkokból és az
azon belüli folyamatok indexe alapján kiszámolja, hogy ez hanyadik szál az összes közül. Ennek segítségével a folyamat indexével tudunk hivatkozni az átadott adatokra
is, például az elsõ szál az elsõ elemnek veszi a négyzetét, a tizedik, a tizedikét, stb.
Szerzõ: Forster Richárd
Év: 2011
Környezet:
- Microsoft Visual Studio 2010
- NVIDIA CUDA Toolkit 4.0
- NVIDIA GeForce GTX 580