Programování GPU v C ++

Gpu Programming With C



V této příručce prozkoumáme sílu programování GPU v jazyce C ++. Vývojáři mohou očekávat neuvěřitelný výkon s C ++ a přístup k fenomenálnímu výkonu GPU s jazykem nízké úrovně může přinést jedny z nejrychleji dostupných výpočtů.

Požadavky

I když jakýkoli počítač schopný provozovat moderní verzi Linuxu může podporovat kompilátor C ++, budete potřebovat GPU založené na NVIDIA, abyste se s tímto cvičením mohli řídit. Pokud nemáte GPU, můžete instanci poháněnou GPU roztočit v Amazon Web Services nebo u jiného poskytovatele cloudu podle vašeho výběru.







Pokud zvolíte fyzický počítač, ujistěte se, že máte nainstalované vlastní ovladače NVIDIA. Pokyny k tomu najdete zde: https://linuxhint.com/install-nvidia-drivers-linux/



Kromě ovladače budete potřebovat také sadu nástrojů CUDA. V tomto příkladu použijeme Ubuntu 16.04 LTS, ale pro většinu hlavních distribucí je k dispozici stahování na následující adrese URL: https://developer.nvidia.com/cuda-downloads



Pro Ubuntu byste zvolili stahování založené na .deb. Stažený soubor nebude mít ve výchozím nastavení příponu .deb, proto doporučuji přejmenovat jej tak, aby na konci měl .deb. Poté můžete nainstalovat pomocí:





sudo dpkg -inázev-balíčku.deb

Pravděpodobně budete vyzváni k instalaci klíče GPG, a pokud ano, postupujte podle uvedených pokynů.

Jakmile to uděláte, aktualizujte svá úložiště:



sudo apt-get aktualizace
sudo apt-get installzázraky-a

Jakmile budete hotovi, doporučuji restartovat, abyste se ujistili, že je vše správně načteno.

Výhody vývoje GPU

CPU zpracovávají mnoho různých vstupů a výstupů a obsahují velký sortiment funkcí nejen pro řešení širokého sortimentu potřeb programu, ale také pro správu různých konfigurací hardwaru. Zvládají také paměť, ukládání do mezipaměti, systémovou sběrnici, segmentaci a funkce IO, což z nich dělá jack všech obchodů.

GPU jsou opakem - obsahují mnoho jednotlivých procesorů, které jsou zaměřeny na velmi jednoduché matematické funkce. Z tohoto důvodu zpracovávají úkoly mnohonásobně rychleji než CPU. Specializací na skalární funkce (funkce, která přebírá jeden nebo více vstupů, ale vrací pouze jeden výstup) dosahují extrémního výkonu za cenu extrémní specializace.

Příklad kódu

V ukázkovém kódu sčítáme vektory dohromady. Přidal jsem CPU a GPU verzi kódu pro srovnání rychlosti.
gpu-example.cpp obsah níže:

#include 'cuda_runtime.h'
#zahrnout
#zahrnout
#zahrnout
#zahrnout
#zahrnout

typedefhodiny::chrono::vysoké_rozlišení_hodinyHodiny;

#definovat ITER 65535

// Verze CPU funkce vektorového přidání
prázdnévector_add_cpu(int *na,int *b,int *C,intn) {
int;

// Přidejte vektorové prvky a a b do vektoru c
pro (= 0;<n; ++) {
C[] =na[] +b[];
}
}

// Verze GPU funkce přidání vektoru
__globální__prázdnévector_add_gpu(int *gpu_a,int *gpu_b,int *gpu_c,intn) {
int=threadIdx.X;
// Není potřeba smyčka for, protože běhový modul CUDA
// toto vlákno ITER krát
gpu_c[] =gpu_a[] +gpu_b[];
}

inthlavní() {

int *na,*b,*C;
int *gpu_a,*gpu_b,*gpu_c;

na= (int *)malloc(ITER* velikost(int));
b= (int *)malloc(ITER* velikost(int));
C= (int *)malloc(ITER* velikost(int));

// Potřebujeme proměnné přístupné pro GPU,
// tak cudaMallocManaged tyto poskytuje
cudaMallocManaged(&gpu_a, ITER* velikost(int));
cudaMallocManaged(&gpu_b, ITER* velikost(int));
cudaMallocManaged(&gpu_c, ITER* velikost(int));

pro (int= 0;<ITER; ++) {
na[] =;
b[] =;
C[] =;
}

// Zavolejte funkci CPU a načasujte ji
autocpu_start=Hodiny::Nyní();
vector_add_cpu(a, b, c, ITER);
autocpu_end=Hodiny::Nyní();
hodiny::náklady << 'vector_add_cpu:'
<<hodiny::chrono::doba trvání_cast<hodiny::chrono::nanosekund>(cpu_end-cpu_start).počet()
<< 'nanosekund. n';

// Zavolejte funkci GPU a načasujte ji
// Trojité úhlové brzdy jsou runtime rozšíření CUDA, které umožňuje
// parametry, které mají být předány volání jádra CUDA.
// V tomto případě předáváme jeden blok vláken s vlákny ITER.
autogpu_start=Hodiny::Nyní();
vector_add_gpu<<<1, ITER>>> (gpu_a, gpu_b, gpu_c, ITER);
cudaDeviceSynchronize();
autogpu_end=Hodiny::Nyní();
hodiny::náklady << 'vector_add_gpu:'
<<hodiny::chrono::doba trvání_cast<hodiny::chrono::nanosekund>(gpu_end-gpu_start).počet()
<< 'nanosekund. n';

// Uvolněte přidělení paměti založené na funkci GPU
cudaFree(na);
cudaFree(b);
cudaFree(C);

// Uvolnění alokace paměti založené na funkci CPU
volný, uvolnit(na);
volný, uvolnit(b);
volný, uvolnit(C);

vrátit se 0;
}

Makefile obsah níže:

INC= -I/usr/místní/zázraky/zahrnout
NVCC=/usr/místní/zázraky/dopoledne/nvcc
NVCC_OPT= -std = c ++jedenáct

Všechno:
$(NVCC)$(NVCC_OPT)gpu-example.cpp-nebogpu-příklad

čistý:
-rm -Fgpu-příklad

Chcete -li spustit příklad, zkompilujte jej:

udělat

Poté spusťte program:

./gpu-příklad

Jak vidíte, verze CPU (vector_add_cpu) běží podstatně pomaleji než verze GPU (vector_add_gpu).

Pokud ne, možná budete muset upravit definici ITER v gpu-example.cu na vyšší číslo. To je způsobeno tím, že doba nastavení GPU je delší než některé menší smyčky náročné na CPU. Zjistil jsem, že 65535 funguje dobře na mém stroji, ale počet najetých kilometrů se může lišit. Jakmile však tuto prahovou hodnotu vymažete, GPU je dramaticky rychlejší než CPU.

Závěr

Doufám, že jste se z našeho úvodu do programování GPU s C ++ hodně naučili. Výše uvedený příklad nedosahuje velkého úspěchu, ale předvedené koncepty poskytují rámec, který můžete použít k začlenění svých myšlenek, aby se uvolnila síla vašeho GPU.