Pregled
U ovom ćemo vodiču istražiti snagu GPU programiranja s C-om++. Programeri mogu očekivati nevjerojatne performanse sa C ++-om, a pristup fenomenalnoj snazi GPU-a jezikom niske razine može rezultirati nekim od najbržih trenutno dostupnih izračuna.
Zahtjevi
Iako bilo koji stroj sposoban za pokretanje moderne verzije Linuxa može podržavati C ++ kompajler, trebat će vam GPU zasnovan na NVIDIA-i da biste slijedili ovu vježbu. Ako nemate GPU, možete zavrtiti instancu s GPU-om u Amazon Web Services ili drugom davatelju usluga u oblaku po vašem izboru.
Ako odaberete fizički stroj, provjerite imate li instalirane vlasničke upravljačke programe 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 ćemo primjeru koristiti Ubuntu 16.04 LTS, ali dostupna su preuzimanja za većinu glavnih distribucija na sljedećem URL-u: https: // programer.nvidia.com / cuda-downloads
Za Ubuntu biste odabrali .preuzimanje na bazi deb. Preuzeta datoteka neće imati .deb proširenje prema zadanim postavkama, pa preporučujem da ga preimenujete u a .deb na kraju. Zatim možete instalirati sa:
sudo dpkg -i naziv paketa.debVjerojatno će se od vas zatražiti da instalirate GPG ključ, a ako je tako, slijedite upute da biste to učinili.
Kada to učinite, ažurirajte svoja spremišta:
sudo apt-get ažuriranjesudo apt-get install cuda -y
Kada završite, preporučujem ponovno pokretanje kako biste bili sigurni da je sve pravilno učitano.
Blagodati razvoja GPU-a
CPU obrađuju mnoge različite ulaze i izlaze i sadrže velik asortiman funkcija ne samo za rješavanje širokog asortimana programskih potreba već i za upravljanje različitim hardverskim konfiguracijama. Oni također obrađuju memoriju, predmemoriranje, sistemsku sabirnicu, segmentiranje i IO funkcionalnost, što ih čini dizalicom svih zanata.
GPU-ovi su suprotno - sadrže mnogo pojedinačnih procesora koji su usredotočeni na vrlo jednostavne matematičke funkcije. Zbog toga obrađuju zadatke mnogo puta brže od CPU-a. Specijalizacijom za skalarne funkcije (funkcija koja uzima jedan ili više ulaza, ali vraća samo jedan izlaz), postižu ekstremne performanse po cijenu ekstremne specijalizacije.
Primjer koda
U primjeru koda zbrajamo vektore. Dodao sam CPU i GPU verziju koda za usporedbu brzine.
gpu-primjer.cpp sadržaj ispod:
#include
#include
#include
#include
#include
typedef std :: chrono :: high_resolution_clock Clock;
#define ITER 65535
// CPU verzija vektorske funkcije dodavanja
void vector_add_cpu (int * a, int * b, int * c, int n)
int i;
// Dodajte vektorske elemente a i b u vektor c
za (i = 0; i < n; ++i)
c [i] = a [i] + b [i];
// GPU verzija funkcije vektorskog dodavanja
__global__ void vector_add_gpu (int * gpu_a, int * gpu_b, int * gpu_c, int n)
int i = threadIdx.x;
// Ne treba petlja za petlju jer je CUDA vrijeme izvođenja
// će ovo prebaciti ITER puta
gpu_c [i] = gpu_a [i] + gpu_b [i];
int main ()
int * a, * b, * c;
int * gpu_a, * gpu_b, * gpu_c;
a = (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 * sizeof (int));
cudaMallocManaged (& gpu_b, ITER * sizeof (int));
cudaMallocManaged (& gpu_c, ITER * sizeof (int));
za (int i = 0; i < ITER; ++i)
a [i] = i;
b [i] = i;
c [i] = i;
// Pozovite CPU funkciju i odredite vrijeme
auto cpu_start = Clock :: now ();
vector_add_cpu (a, b, c, ITER);
auto cpu_end = Clock :: now ();
std :: cout << "vector_add_cpu: "
<< std::chrono::duration_cast
<< " nanoseconds.\n";
// Pozovite GPU funkciju i odredite vrijeme
// Trostruke kutne zagrade je CUDA runtime produženje koje omogućuje
// parametri poziva CUDA kernela koji se prosljeđuju.
// U ovom primjeru prenosimo jedan blok niti s ITER nitima.
auto gpu_start = Sat :: sada ();
vektor_add_gpu <<<1, ITER>>> (gpu_a, gpu_b, gpu_c, ITER);
cudaDeviceSynchronize ();
auto gpu_end = Sat :: sada ();
std :: cout << "vector_add_gpu: "
<< std::chrono::duration_cast
<< " nanoseconds.\n";
// Oslobodite raspodjelu memorije zasnovane na GPU funkciji
cudaFree (a);
cudaFree (b);
cudaFree (c);
// Oslobodite raspodjelu memorije zasnovane na CPU funkciji
besplatno (a);
besplatno (b);
besplatno (c);
return 0;
Makefile sadržaj ispod:
INC = -I / usr / local / cuda / includeNVCC = / usr / local / cuda / bin / nvcc
NVCC_OPT = -std = c ++ 11
svi:
$ (NVCC) $ (NVCC_OPT) gpu-primjer.cpp -o gpu-primjer
čist:
-rm -f gpu-primjer
Da biste pokrenuli primjer, kompajlirajte ga:
napravitiZatim pokrenite program:
./ gpu-primjerKao što vidite, CPU verzija (vector_add_cpu) radi znatno sporije od verzije GPU (vector_add_gpu).
Ako nije, možda ćete trebati prilagoditi ITER definiranje u primjeru gpu.cu na veći broj. To je zbog toga što je vrijeme postavljanja GPU-a dulje od nekih manjih petlji koje zahtijevaju CPU. Otkrio sam da 65535 dobro radi na mom stroju, ali vaša kilometraža može varirati. Međutim, nakon što očistite 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 GPU programiranje s C-om++. Gornji primjer ne postiže puno, ali demonstrirani koncepti pružaju okvir koji možete koristiti za uključivanje svojih ideja kako biste oslobodili snagu vašeg GPU-a.