Programovanie GPU v C ++

Gpu Programming With C



V tejto príručke sa pozrieme na silu programovania GPU v C ++. Vývojári môžu očakávať neuveriteľný výkon v jazyku C ++ a prístup k fenomenálnej sile GPU v jazyku nízkej úrovne môže priniesť niektoré z najrýchlejších výpočtov, ktoré sú v súčasnosti k dispozícii.

Požiadavky

Napriek tomu, že každý počítač, na ktorom je spustená moderná verzia systému Linux, môže podporovať prekladač C ++, budete pri tomto cvičení potrebovať GPU na báze NVIDIA. Ak nemáte GPU, môžete roztočiť inštanciu poháňanú GPU v Amazon Web Services alebo u iného poskytovateľa cloudu podľa vášho výberu.







Ak si vyberiete fyzický počítač, uistite sa, že máte nainštalované proprietárne ovládače NVIDIA. Návod na to nájdete tu: https://linuxhint.com/install-nvidia-drivers-linux/



Okrem ovládača budete potrebovať aj sadu nástrojov CUDA. V tomto prípade použijeme Ubuntu 16.04 LTS, ale pre väčšinu veľkých distribúcií je k dispozícii sťahovanie na nasledujúcej adrese URL: https://developer.nvidia.com/cuda-downloads



V prípade Ubuntu by ste zvolili sťahovanie založené na .deb. Stiahnutý súbor nebude mať predvolene príponu .deb, preto odporúčam premenovať ho tak, aby na konci mal .deb. Potom môžete nainštalovať pomocou:





sudo dpkg -inázov-balíka.deb

Pravdepodobne budete vyzvaní k inštalácii kľúča GPG, a ak áno, postupujte podľa uvedených pokynov.

Akonáhle to urobíte, aktualizujte svoje archívy:



sudo apt-get aktualizácia
sudo apt-get nainštalovaťzázraky-a

Po dokončení odporúčam reštartovať, aby ste sa presvedčili, že je všetko správne načítané.

Výhody vývoja GPU

CPU spracováva mnoho rôznych vstupov a výstupov a obsahuje široký sortiment funkcií, ktoré sa hodia nielen na riešenie širokého sortimentu potrieb programu, ale aj na správu rôznych hardvérových konfigurácií. Tiež zvládajú pamäť, ukladanie do pamäte cache, systémovú zbernicu, segmentáciu a IO, čo z nich robí zdvihák všetkých odborov.

GPU sú naopak - obsahujú veľa jednotlivých procesorov, ktoré sú zamerané na veľmi jednoduché matematické funkcie. Z tohto dôvodu spracovávajú úlohy mnohokrát rýchlejšie ako CPU. Špecializáciou na skalárne funkcie (funkcia, ktorá zaberá jeden alebo viac vstupov, ale vracia iba jeden výstup) dosahujú extrémny výkon za cenu extrémnej špecializácie.

Príklad kódu

V ukážkovom kóde sčítame vektory dohromady. Pridal som CPU a GPU verziu kódu na porovnanie rýchlosti.
gpu-example.cpp obsah nižšie:

#include 'cuda_runtime.h'
#zahrnúť
#zahrnúť
#zahrnúť
#zahrnúť
#zahrnúť

typedefhodiny::chrono::vysoké_rozlíšenie_hodinyHodiny;

#define ITER 65535

// Verzia CPU funkcie vektorového pridania
prázdnyvector_add_cpu(int *do,int *b,int *c,intn) {
inti;

// Pridajte vektorové prvky a a b do vektora c
pre (i= 0;i<n; ++i) {
c[i] =do[i] +b[i];
}
}

// GPU verzia funkcie vektorového pridania
__global__prázdnyvector_add_gpu(int *gpu_a,int *gpu_b,int *gpu_c,intn) {
inti=threadIdx.X;
// Nie je potrebná slučka for, pretože runtime CUDA
// toto prevlečiem ITER krát
gpu_c[i] =gpu_a[i] +gpu_b[i];
}

intHlavná() {

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

do= (int *)malloc(ITER* veľkosť(int));
b= (int *)malloc(ITER* veľkosť(int));
c= (int *)malloc(ITER* veľkosť(int));

// Potrebujeme premenné prístupné pre GPU,
// tak cudaMallocManaged poskytuje tieto
cudaMallocSpravované(&gpu_a, ITER* veľkosť(int));
cudaMallocSpravované(&gpu_b, ITER* veľkosť(int));
cudaMallocSpravované(&gpu_c, ITER* veľkosť(int));

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

// Zavolajte funkciu CPU a načasujte ju
autocpu_start=Hodiny::teraz();
vector_add_cpu(a, b, c, ITER);
autocpu_end=Hodiny::teraz();
hodiny::náklady << 'vector_add_cpu:'
<<hodiny::chrono::trvanie_cast<hodiny::chrono::nanosekundy>(cpu_end-cpu_start).počítať()
<< „nanosekundy. n';

// Zavolajte funkciu GPU a načasujte ju
// Trojité uhlové brzdy sú runtime rozšírenie CUDA, ktoré umožňuje
// parametre odovzdaného volania jadra CUDA.
// V tomto prípade prechádzame jedným blokom vlákien s vláknami ITER.
autogpu_start=Hodiny::teraz();
vector_add_gpu<<<1, ITER>>> (gpu_a, gpu_b, gpu_c, ITER);
cudaDeviceSynchronize();
autogpu_end=Hodiny::teraz();
hodiny::náklady << 'vector_add_gpu:'
<<hodiny::chrono::trvanie_cast<hodiny::chrono::nanosekundy>(gpu_end-gpu_start).počítať()
<< „nanosekundy. n';

// Uvoľnenie alokácie pamäte založenej na funkciách GPU
cudaFree(do);
cudaFree(b);
cudaFree(c);

// Uvoľnenie alokácie pamäte založenej na funkciách CPU
zadarmo(do);
zadarmo(b);
zadarmo(c);

vrátiť sa 0;
}

Makefile obsah nižšie:

INC= -I/usr/miestny/zázraky/zahrnúť
NVCC=/usr/miestny/zázraky/dopoludnia/nvcc
NVCC_OPT= -std = c ++jedenásť

všetky:
$(NVCC)$(NVCC_OPT)gpu-example.cpp-alebogpu-príklad

čisté:
-rm -fgpu-príklad

Ak chcete spustiť príklad, skompilujte ho:

urobiť

Potom spustite program:

./gpu-príklad

Ako vidíte, verzia CPU (vector_add_cpu) beží podstatne pomalšie ako verzia GPU (vector_add_gpu).

Ak nie, možno budete musieť upraviť definíciu ITER v gpu-example.cu na vyššie číslo. Je to spôsobené tým, že čas nastavenia GPU je dlhší ako niektoré menšie slučky náročné na CPU. Zistil som, že 65535 funguje na mojom počítači dobre, ale počet najazdených kilometrov sa môže líšiť. Akonáhle však tento prah vymažete, GPU je dramaticky rýchlejšie ako CPU.

Záver

Dúfam, že ste sa veľa naučili z nášho úvodu do programovania GPU v C ++. Vyššie uvedený príklad nedosahuje veľa, ale predvádzané koncepty poskytujú rámec, ktorý môžete použiť na začlenenie svojich myšlienok, aby ste uvoľnili silu svojho GPU.