GPU programiranje s C ++

Gpu Programming With C

U ovom vodiču istražit ćemo moć programiranja GPU -a s C ++. Programeri mogu očekivati ​​nevjerojatne performanse s C ++, a pristup fenomenalnoj snazi ​​GPU-a s jezikom niske razine može dati neke od najbržih računanja koja su trenutno dostupna.

Zahtjevi

Iako svaki stroj sposoban za pokretanje moderne verzije Linuxa može podržati C ++ prevoditelj, trebat će vam GPU zasnovan na NVIDIA-i koji ćete slijediti zajedno s ovom vježbom. Ako nemate GPU, možete pokrenuti instancu s GPU-om u Amazon Web Services ili drugom davatelju usluga oblaka po vašem izboru.



Ako odaberete fizički stroj, provjerite imate li instalirane upravljačke programe za NVIDIA. Upute za to možete pronaći ovdje: https://linuxhint.com/install-nvidia-drivers-linux/



Osim upravljačkog programa, trebat će vam CUDA alat. U ovom primjeru koristit ćemo Ubuntu 16.04 LTS, ali za većinu velikih distribucija dostupna su preuzimanja na sljedećem URL -u: https://developer.nvidia.com/cuda-downloads



Za Ubuntu biste odabrali preuzimanje zasnovano na .deb -u. Preuzeta datoteka prema zadanim postavkama neće imati .deb ekstenziju, pa preporučujem da je preimenujete tako da na kraju ima .deb. Zatim možete instalirati pomoću:

sudo dpkg -iime-paketa.deb

Vjerojatno ćete biti upitani da instalirate GPG ključ, a ako je tako, slijedite upute za to.

Nakon što to učinite, ažurirajte svoja spremišta:



sudo apt-get ažuriranje
sudo apt-get installčuda-i

Nakon što završite, preporučujem ponovno podizanje sustava kako biste bili sigurni da je sve pravilno učitano.

Prednosti razvoja GPU -a

CPU -i obrađuju mnogo različitih ulaza i izlaza i sadrže veliki izbor funkcija za ne samo rješavanje širokog asortimana programskih potreba, već i za upravljanje različitim hardverskim konfiguracijama. Također se bave memorijom, predmemoriranjem, sistemskom sabirnicom, segmentiranjem i IO funkcionalnošću, što ih čini džekom svih zanata.

GPU -i su suprotni - sadrže mnoge pojedinačne procesore koji su fokusirani na vrlo jednostavne matematičke funkcije. Zbog toga obrađuju zadatke mnogo puta brže od CPU -a. Specijaliziranjem za skalarne funkcije (funkcija koja uzima jedan ili više ulaza, ali vraća samo jedan izlaz), postižu iznimne performanse po cijenu ekstremne specijalizacije.

Primjer koda

U primjeru koda zbrajamo vektore. Dodao sam CPU i GPU verziju koda za usporedbu brzine.
gpu-example.cpp sadržaj ispod:

#include 'cuda_runtime.h'
#uključi
#uključi
#uključi
#uključi
#uključi

typedefsati::chrono::visoki_rezolucijski_satSat;

#definirajte ITER 65535

// CPU verzija funkcije vektorskog dodavanja
poništitivector_add_cpu(int *do,int *b,int *c,intn) {
inti;

// Dodajte vektorske elemente a i b vektoru c
za (i= 0;i<n; ++i) {
c[i] =do[i] +b[i];
}
}

// GPU verzija funkcije dodavanja vektora
__globalno__poništitivector_add_gpu(int *gpu_a,int *gpu_b,int *gpu_c,intn) {
inti=threadIdx.x;
// Nije potrebna petlja for jer je vrijeme izvođenja CUDA
// ovo će provući ITER puta
gpu_c[i] =gpu_a[i] +gpu_b[i];
}

intglavni() {

int *do,*b,*c;
int *gpu_a,*gpu_b,*gpu_c;

do= (int *)malloc(ITER* veličina(int));
b= (int *)malloc(ITER* veličina(int));
c= (int *)malloc(ITER* veličina(int));

// Trebamo varijable dostupne GPU -u,
// pa ih cudaMallocManaged pruža
cudaMallocManaged(&gpu_a, ITER* veličina(int));
cudaMallocManaged(&gpu_b, ITER* veličina(int));
cudaMallocManaged(&gpu_c, ITER* veličina(int));

za (inti= 0;i<ITER; ++i) {
do[i] =i;
b[i] =i;
c[i] =i;
}

// Pozovite funkciju CPU -a i odredite joj vrijeme
autocpu_start=Sat::sada();
vector_add_cpu(a, b, c, ITER);
autocpu_end=Sat::sada();
sati::trošak << 'vector_add_cpu:'
<<sati::chrono::trajanje_cast<sati::chrono::nanosekundi>(cpu_end-cpu_start).računati()
<< 'nanosekunde. n';

// Pozovite GPU funkciju i odredite joj vrijeme
// Trostruki kutni nosači su CUDA produžetak koji omogućuje
// parametri poziva jezgre CUDA koji se prosljeđuju.
// U ovom primjeru prenosimo jedan blok niti s ITER nitima.
autogpu_start=Sat::sada();
vector_add_gpu<<<1, ITER>>> (gpu_a, gpu_b, gpu_c, ITER);
cudaDeviceSynchronize();
autogpu_end=Sat::sada();
sati::trošak << 'vector_add_gpu:'
<<sati::chrono::trajanje_cast<sati::chrono::nanosekundi>(gpu_end-gpu_start).računati()
<< 'nanosekunde. n';

// Oslobađanje dodjele memorije temeljene na GPU funkciji
cudaBESPLATNO(do);
cudaBESPLATNO(b);
cudaBESPLATNO(c);

// Oslobađanje dodjele memorije temeljene na CPU funkciji
besplatno(do);
besplatno(b);
besplatno(c);

povratak 0;
}

Makefile sadržaj ispod:

INC= -Ja/usr/lokalno/čuda/uključuju
NVCC=/usr/lokalno/čuda/am/nvcc
NVCC_OPT= -std = c ++jedanaest

svi:
$(NVCC)$(NVCC_OPT)gpu-example.cpp-iligpu-primjer

čist:
-rm -fgpu-primjer

Da biste pokrenuli primjer, sastavite ga:

napraviti

Zatim pokrenite program:

./gpu-primjer

Kao što vidite, verzija CPU -a (vector_add_cpu) radi znatno sporije od verzije GPU -a (vector_add_gpu).

Ako nije, možda ćete morati prilagoditi ITER definiciju u gpu-example.cu na veći broj. To je zbog toga što je vrijeme postavljanja GPU-a duže od nekih manjih petlji intenzivnih procesora. Otkrio sam da 65535 dobro radi na mom stroju, ali vaša kilometraža može varirati. Međutim, nakon što uklonite ovaj prag, GPU je dramatično brži od CPU -a.

Zaključak

Nadam se da ste puno naučili iz našeg uvoda u programiranje GPU -a s C ++. Gornji primjer ne postiže mnogo, ali prikazani koncepti pružaju okvir koji možete koristiti za uključivanje svojih ideja kako biste oslobodili moć svog GPU -a.