Pārskats
Šajā ceļvedī mēs izpētīsim GPU programmēšanas spēku ar C++. Izstrādātāji var sagaidīt neticamu veiktspēju ar C ++, un piekļuve GPU fenomenālajai jaudai ar zema līmeņa valodu var dot dažus no visātrākajiem pašlaik pieejamajiem aprēķiniem.
Prasības
Lai gan jebkura mašīna, kas spēj darbināt modernu Linux versiju, var atbalstīt C ++ kompilatoru, jums būs nepieciešams NVIDIA bāzes GPU, lai sekotu līdzi šim vingrinājumam. Ja jums nav GPU, varat izveidot GPU darbināmu instanci Amazon Web Services vai citā izvēlētā mākoņa nodrošinātājā.
Ja izvēlaties fizisku mašīnu, lūdzu, pārliecinieties, vai ir instalēti NVIDIA patentētie draiveri. Norādījumus par to varat atrast šeit: https: // linuxhint.com / install-nvidia-drivers-linux /
Papildus draiverim jums būs nepieciešams CUDA rīkkopa. Šajā piemērā mēs izmantosim Ubuntu 16.04 LTS, taču lielākajai daļai galveno izplatītāju ir pieejamas lejupielādes ar šādu vietrādi URL: https: // developer.nvidia.com / cuda-downloads
Operētājsistēmai Ubuntu jūs izvēlaties .deb balstīta lejupielāde. Lejupielādētajā failā nebūs .deb paplašinājums pēc noklusējuma, tāpēc es iesaku to pārdēvēt par .deb beigās. Pēc tam varat instalēt, izmantojot:
sudo dpkg -i pakotnes nosaukums.debJums, iespējams, tiks piedāvāts instalēt GPG atslēgu, un, ja tā, izpildiet sniegtos norādījumus, lai to izdarītu.
Kad esat to izdarījis, atjauniniet krātuves:
sudo apt-get atjauninājumssudo apt-get install cuda -y
Kad tas ir izdarīts, es iesaku atsāknēt, lai nodrošinātu, ka viss ir pareizi ielādēts.
GPU izstrādes priekšrocības
Centrālie procesori apstrādā daudz dažādu ieeju un izeju, un tajos ir plašs funkciju klāsts, lai risinātu ne tikai plašu programmu vajadzību klāstu, bet arī dažādu aparatūras konfigurāciju pārvaldību. Viņi arī apstrādā atmiņu, kešatmiņu, sistēmas kopni, segmentēšanu un IO funkcionalitāti, padarot tos par visu darījumu domkratu.
GPU ir pretēji - tajos ir daudz atsevišķu procesoru, kas ir vērsti uz ļoti vienkāršām matemātiskām funkcijām. Tādēļ viņi uzdevumus apstrādā daudzas reizes ātrāk nekā procesori. Specializējoties skalārajās funkcijās (funkcija, kas prasa vienu vai vairākas ievades, bet atgriež tikai vienu izvadi), tās sasniedz ārkārtas veiktspēju par galējas specializācijas cenu.
Koda piemērs
Koda piemērā mēs saskaitām vektorus kopā. Ātruma salīdzināšanai esmu pievienojis koda CPU un GPU versiju.
gpu-piemērs.cpp saturs zemāk:
# iekļaut
# iekļaut
# iekļaut
# iekļaut
# iekļaut
typedef std :: chrono :: high_resolution_clock Pulkstenis;
#define ITER 65535
// vektora pievienošanas funkcijas CPU versija
void vector_add_cpu (int * a, int * b, int * c, int n)
int i;
// Pievienojiet vektora elementus a un b vektoram c
par (i = 0; i < n; ++i)
c [i] = a [i] + b [i];
// vektora pievienošanas funkcijas GPU versija
__global__ void vector_add_gpu (int * gpu_a, int * gpu_b, int * gpu_c, int n)
int i = threadIdx.x;
// Cilpai nav nepieciešams, jo CUDA izpildlaiks
// pavedīs šo ITER reizes
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 * (int) lielums);
b = (int *) malloc (ITER * (int) lielums);
c = (int *) malloc (ITER * (int) lielums);
// Mums ir nepieciešami mainīgie, kas pieejami GPU,
// tā nodrošina cudaMallocManaged
cudaMallocManaged (& gpu_a, ITER * sizeof (int));
cudaMallocManaged (& gpu_b, ITER * sizeof (int));
cudaMallocManaged (& gpu_c, ITER * sizeof (int));
par (int i = 0; i < ITER; ++i)
a [i] = i;
b [i] = i;
c [i] = i;
// Izsauciet CPU funkciju un noregulējiet to
auto cpu_start = Pulkstenis :: tagad ();
vector_add_cpu (a, b, c, ITER);
auto cpu_end = Pulkstenis :: tagad ();
std :: cout << "vector_add_cpu: "
<< std::chrono::duration_cast
<< " nanoseconds.\n";
// Izsauciet GPU funkciju un noregulējiet to
// Trīskāršā leņķa bremzes ir CUDA izpildlaika paplašinājums, kas ļauj
// jānodod CUDA kodola izsaukuma parametri.
// Šajā piemērā mēs nododam vienu pavedienu bloku ar ITER pavedieniem.
auto gpu_start = Pulkstenis :: tagad ();
vector_add_gpu <<<1, ITER>>> (gpu_a, gpu_b, gpu_c, ITER);
cudaDeviceSynchronize ();
auto gpu_end = Pulkstenis :: tagad ();
std :: cout << "vector_add_gpu: "
<< std::chrono::duration_cast
<< " nanoseconds.\n";
// Bezmaksas atmiņas piešķiršana uz GPU funkcijām
cudaFree (a);
cudaFree (b);
cudaFree (c);
// Atbrīvojiet atmiņas piešķīrumus, kuru pamatā ir CPU funkcija
bezmaksas (a);
bezmaksas (b);
bezmaksas (c);
atgriešanās 0;
Makefile saturs zemāk:
INC = -I / usr / local / cuda / includeNVCC = / usr / local / cuda / bin / nvcc
NVCC_OPT = -std = c ++ 11
visi:
$ (NVCC) $ (NVCC_OPT) GPU piemērs.cpp -o gpu-piemērs
tīrs:
-rm -f gpu-piemērs
Lai palaistu piemēru, apkopojiet to:
veidotPēc tam palaidiet programmu:
./ gpu-exampleKā redzat, CPU versija (vector_add_cpu) darbojas ievērojami lēnāk nekā GPU versija (vector_add_gpu).
Ja nē, jums, iespējams, būs jāpielāgo ITER definīcija GPU piemērā.cu uz lielāku skaitli. Tas ir saistīts ar to, ka GPU iestatīšanas laiks ir ilgāks nekā dažās mazākās procesora ietilpīgās cilpās. Es atklāju, ka 65535 labi darbojas manā mašīnā, taču jūsu nobraukums var atšķirties. Tomēr, kad esat notīrījis šo slieksni, GPU ir dramatiski ātrāks nekā centrālais procesors.
Secinājums
Es ceru, ka esat daudz iemācījušies no mūsu ieviešanas GPU programmēšanā ar C++. Iepriekš minētais piemērs nedod daudz panākumu, taču parādītie jēdzieni nodrošina sistēmu, kuru varat izmantot, lai iekļautu savas idejas, lai atbrīvotu savu GPU spēku.