Pagprogram ng GPU na may C ++

Gpu Programming With C



Sa gabay na ito, tuklasin namin ang lakas ng pag-program ng GPU sa C ++. Maaaring asahan ng mga developer ang hindi kapani-paniwala na pagganap sa C ++, at ang pag-access sa phenomenal power ng GPU na may isang mababang antas na wika ay maaaring magbunga ng ilan sa pinakamabilis na pagkalkula na kasalukuyang magagamit.

Mga Kinakailangan

Habang ang anumang makina na may kakayahang magpatakbo ng isang modernong bersyon ng Linux ay maaaring suportahan ang isang C ++ compiler, kakailanganin mo ng isang NVIDIA-based GPU upang sundin kasama ang ehersisyo na ito. Kung wala kang isang GPU, maaari mong paikutin ang isang halimbawa na pinalakas ng GPU sa Amazon Web Services o ibang cloud provider na iyong pinili.







Kung pipiliin mo ang isang pisikal na makina, mangyaring tiyaking mayroon kang naka-install na mga driver ng pagmamay-ari ng NVIDIA. Maaari kang makahanap ng mga tagubilin para dito: https://linuxhint.com/install-nvidia-drivers-linux/



Bilang karagdagan sa driver, kakailanganin mo ang toolkit ng CUDA. Sa halimbawang ito, gagamitin namin ang Ubuntu 16.04 LTS, ngunit may mga magagamit na pag-download para sa karamihan ng mga pangunahing pamamahagi sa sumusunod na URL: https://developer.nvidia.com/cuda-downloads



Para sa Ubuntu, pipiliin mo ang pag-download na batay sa .deb. Ang na-download na file ay hindi magkakaroon ng isang .deb extension bilang default, kaya inirerekumenda kong palitan ang pangalan nito upang magkaroon ng isang .deb sa dulo. Pagkatapos, maaari kang mag-install sa:





sudo dpkg -akopackage-name.deb

Malamang sasabihan ka na mag-install ng isang GPG key, at kung gayon, sundin ang mga tagubiling ibinigay upang gawin ito.

Kapag nagawa mo na iyon, i-update ang iyong mga repository:



sudo apt-get update
sudo apt-get installhimala-at

Kapag tapos na, inirerekumenda ko ang pag-reboot upang matiyak na ang lahat ay maayos na na-load.

Ang Mga Pakinabang ng Pag-unlad ng GPU

Ang mga CPU ay humahawak ng maraming iba't ibang mga input at output at naglalaman ng isang malaking assortment ng mga pagpapaandar para sa hindi lamang pagharap sa isang malawak na assortment ng mga pangangailangan ng programa ngunit din para sa pamamahala ng iba't ibang mga pagsasaayos ng hardware. Hawak din nila ang memorya, pag-cache, ang system bus, pagse-segment, at pag-andar ng IO, na ginagawang isang jack ng lahat ng mga kalakal.

Ang mga GPU ay kabaligtaran - naglalaman ang mga ito ng maraming mga indibidwal na processor na nakatuon sa napakasimpleng mga pagpapaandar sa matematika. Dahil dito, pinoproseso nila ang mga gawain nang maraming beses nang mas mabilis kaysa sa mga CPU. Sa pamamagitan ng pagdadalubhasa sa mga pagpapaandar ng scalar (isang pagpapaandar na tumatagal ng isa o higit pang mga input ngunit nagbabalik lamang sa isang solong output), nakakamit nila ang matinding pagganap sa gastos ng matinding pagdadalubhasa.

Halimbawa ng Code

Sa halimbawa ng code, magkakasama kaming nagdagdag ng mga vector. Nagdagdag ako ng isang bersyon ng CPU at GPU ng code para sa paghahambing ng bilis.
gpu-halimbawa.cpp mga nilalaman sa ibaba:

# isama ang 'cuda_runtime.h'
# isama
# isama
# isama
# isama
# isama

typedeforas::chrono::high_resolution_clockOrasan;

# tukuyin ang ITER 65535

// CPU bersyon ng pag-andar ng vector add
walang bisavector_add_cpu(int *sa,int *b,int *c,intn) {
intako;

// Idagdag ang mga elemento ng vector a at b sa vector c
para sa (ako= 0;ako<n; ++ako) {
c[ako] =sa[ako] +b[ako];
}
}

// GPU bersyon ng pag-andar ng vector add
__global__walang bisavector_add_gpu(int *gpu_a,int *gpu_b,int *gpu_c,intn) {
intako=threadIdx.x;
// Hindi kailangan ng loop dahil ang CUDA runtime
// ay i-thread ang ITER beses na ito
gpu_c[ako] =gpu_a[ako] +gpu_b[ako];
}

intpangunahing() {

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

sa= (int *)malloc(ITER* sukat ng(int));
b= (int *)malloc(ITER* sukat ng(int));
c= (int *)malloc(ITER* sukat ng(int));

// Kailangan namin ng mga variable na naa-access sa GPU,
// kaya cudaMallocManaged ay nagbibigay ng mga ito
cudaMallocManaged(&gpu_a, ITER* sukat ng(int));
cudaMallocManaged(&gpu_b, ITER* sukat ng(int));
cudaMallocManaged(&gpu_c, ITER* sukat ng(int));

para sa (intako= 0;ako<ITER; ++ako) {
sa[ako] =ako;
b[ako] =ako;
c[ako] =ako;
}

// Call the CPU function and time it
awtomatikocpu_start=Orasan::ngayon();
vector_add_cpu(a, b, c, ITER);
awtomatikocpu_end=Orasan::ngayon();
oras::gastos << 'vector_add_cpu:'
<<oras::chrono::tagal_cast<oras::chrono::nanoseconds>(cpu_end-cpu_start).bilangin()
<< 'nanoseconds. n';

// Call the GPU function and time it
// Ang triple angle brakets ay isang CUDA runtime extension na nagbibigay-daan
// mga parameter ng isang tawag sa kernel ng CUDA upang maipasa.
// Sa halimbawang ito, nagpapasa kami ng isang bloke ng thread na may mga thread ng ITER.
awtomatikogpu_start=Orasan::ngayon();
vector_add_gpu<<<1, ITER>>> (gpu_a, gpu_b, gpu_c, ITER);
cudaDeviceSynchronize();
awtomatikogpu_end=Orasan::ngayon();
oras::gastos << 'vector_add_gpu:'
<<oras::chrono::tagal_cast<oras::chrono::nanoseconds>(gpu_end-gpu_start).bilangin()
<< 'nanoseconds. n';

// Libre ang paglalaan ng memory na batay sa pagpapaandar ng GPU
cudaFree(sa);
cudaFree(b);
cudaFree(c);

// Libre ang paglalaan ng memory na batay sa pagpapaandar ng CPU
libre(sa);
libre(b);
libre(c);

bumalik ka 0;
}

Makefile mga nilalaman sa ibaba:

INC= -Ako/usr/lokal/himala/isama
NVCC=/usr/lokal/himala/am/nvcc
NVCC_OPT= -std = c ++labing-isang

lahat:
$(NVCC)$(NVCC_OPT)gpu-halimbawa.cpp-o kayagpu-halimbawa

malinis:
-rm -fgpu-halimbawa

Upang patakbuhin ang halimbawa, i-compile ito:

gumawa

Pagkatapos ay patakbuhin ang programa:

./gpu-halimbawa

Tulad ng nakikita mo, ang bersyon ng CPU (vector_add_cpu) ay tumatakbo nang mas mabagal kaysa sa bersyon ng GPU (vector_add_gpu).

Kung hindi, maaaring kailanganin mong ayusin ang tinukoy ng ITER sa gpu-example.cu sa isang mas mataas na numero. Ito ay dahil sa oras ng pag-setup ng GPU na mas mahaba kaysa sa ilang mas maliit na mga loop na masinsinang CPU. Natagpuan ko ang 65535 upang gumana nang maayos sa aking makina, ngunit maaaring mag-iba ang iyong agwat ng mga milya. Gayunpaman, kapag na-clear mo ang threshold na ito, ang GPU ay mas mabilis na mas mabilis kaysa sa CPU.

Konklusyon

Inaasahan kong marami kang natutunan mula sa aming pagpapakilala sa pagprograma ng GPU sa C ++. Ang halimbawa sa itaas ay hindi nakakamit ng napakahusay, ngunit ang mga ipinakitang konsepto ay nagbibigay ng isang balangkas na maaari mong gamitin upang isama ang iyong mga ideya upang maipalabas ang lakas ng iyong GPU.