GPU -Programmierung mit C ++

GPU -Programmierung mit C ++

Überblick

In diesem Leitfaden werden wir die Kraft der GPU -Programmierung mit C untersuchen++. Entwickler können eine unglaubliche Leistung mit C ++ erwarten, und der Zugriff auf die phänomenale Kraft der GPU mit einer Sprache auf niedriger Ebene kann einige der schnellsten Berechnungen liefern, die derzeit verfügbar sind.

Anforderungen

Während jede Maschine, die in der Lage ist, eine moderne Version von Linux auszuführen. Wenn Sie keine GPU haben, können Sie eine GPU-Instanz in Amazon-Webdiensten oder einen anderen Cloud-Anbieter Ihrer Wahl einstellen.

Wenn Sie eine physische Maschine auswählen, stellen Sie bitte sicher, dass die proprietären Treiber von Nvidia installiert sind. Hier finden Sie Anweisungen hier: https: // linuxHint.com/install-nvidia-drivers-linux/

Zusätzlich zum Treiber benötigen Sie das CUDA -Toolkit. In diesem Beispiel verwenden wir Ubuntu 16.04 LTS, aber es gibt Downloads für die meisten wichtigen Verteilungen in der folgenden URL: https: // Entwickler.Nvidia.com/cuda-downloads

Für Ubuntu würden Sie die wählen .Deb -basierter Download. Die heruntergeladene Datei hat keine .Deb -Erweiterung standardmäßig, ich empfehle, es umzubenennen, um eine zu haben .Deb am Ende. Dann können Sie mit:

sudo dpkg -i paketname.Deb

Sie werden wahrscheinlich aufgefordert, einen GPG -Schlüssel zu installieren, und befolgen Sie dies, wenn ja.

Sobald Sie das getan haben, aktualisieren Sie Ihre Repositorys:

sudo apt-Get-Update
sudo apt -get installieren cuda -y

Sobald ich fertig ist, empfehle ich das Neustart, um sicherzustellen, dass alles ordnungsgemäß geladen ist.

Die Vorteile der GPU -Entwicklung

CPUs behandeln viele verschiedene Eingänge und Ausgänge und enthalten eine große Auswahl an Funktionen, um nicht nur eine breite Auswahl an Programmanforderungen, sondern auch für die Verwaltung unterschiedlicher Hardwarekonfigurationen zu bewältigen. Sie bewältigen auch Speicher, Caching, den Systembus, Segmentierung und IO.

GPUs sind das Gegenteil - sie enthalten viele einzelne Prozessoren, die sich auf sehr einfache mathematische Funktionen konzentrieren. Aus diesem Grund bearbeiten sie Aufgaben um ein Vielfaches schneller als CPUs. Indem Sie sich auf skalare Funktionen spezialisiert haben (eine Funktion, die eine oder mehrere Eingänge nimmt, aber nur eine einzelne Ausgabe zurückgibt), erzielen sie auf Kosten der extremen Spezialisierung eine extreme Leistung.

Beispielcode

Im Beispielcode fügen wir zusammen Vektoren hinzu. Ich habe eine CPU- und GPU -Version des Codes zum Geschwindigkeitsvergleich hinzugefügt.
GPU-Beispiel.CPP Inhalt unten:

#Include "CUDA_RUNTIME.H"
#enthalten
#enthalten
#enthalten
#enthalten
#enthalten
typedef std :: chrono :: High_Resolution_clock clock;
#define iter 65535
// CPU -Version der Vektor -Funktion hinzufügen
void vector_add_cpu (int *a, int *b, int *c, int n)
int i;
// Fügen Sie die Vektorelemente A und B zum Vektor C hinzu
für (i = 0; i < n; ++i)
c [i] = a [i] + b [i];


// GPU -Version der Vektor -Funktion hinzufügen
__global__ void vector_add_gpu (int *gpu_a, int *gpu_b, int *gpu_c, int n)
int i = threadIdx.X;
// Nein für die Schleife benötigt, weil die CUDA -Laufzeit
// fädelt diese Iterzeiten ein
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 * sizeof (int));
b = (int *) malloc (iter * sizeof (int));
c = (int *) malloc (iter * sizeof (int));
// Wir benötigen Variablen, die der GPU zugänglich sind,
// so Cudamallocmanaged liefert diese
cudamallocmanaged (& gpu_a, iter * sizeof (int));
cudamallocmanaged (& gpu_b, iter * sizeof (int));
cudamallocmanaged (& gpu_c, iter * sizeof (int));
für (int i = 0; i < ITER; ++i)
a [i] = i;
B [i] = i;
c [i] = i;

// Rufen Sie die CPU -Funktion und die Zeit auf
auto cpu_start = clock :: now ();
vector_add_cpu (a, b, c, iter);
auto cpu_end = clock :: jetzt ();
std :: Cout << "vector_add_cpu: "
<< std::chrono::duration_cast(cpu_end - cpu_start).zählen()
<< " nanoseconds.\n";
// Rufen Sie die GPU -Funktion und die Zeit auf
// Die dreifachen Brakets ist eine Cuda -Laufzeitverlängerung, die es erlaubt
// Parameter eines Cuda -Kernel -Aufrufs.
// In diesem Beispiel geben wir einen Threadblock mit Iter -Threads über.
auto gpu_start = clock :: now ();
vector_add_gpu <<<1, ITER>>> (gpu_a, gpu_b, gpu_c, iter);
cudadevicesynchronize ();
auto gpu_end = clock :: now ();
std :: Cout << "vector_add_gpu: "
<< std::chrono::duration_cast(gpu_end - gpu_start).zählen()
<< " nanoseconds.\n";
// Die GPU-Funktionsbasierten Speicherzuweisungen befreien
Cudafree (a);
Cudafree (b);
Cudafree (c);
// Die CPU-Funktionsbasierten Speicherzuweisungen befreien
befreie ein);
frei (b);
frei (c);
Rückkehr 0;

Makefile Inhalt unten:

Inc = -i/usr/local/cuda/einschließen
Nvcc =/usr/local/cuda/bin/nvcc
Nvcc_opt = -std = c ++ 11
alle:
$ (NVCC) $ (NVCC_OPT) GPU-Beispiel.CPP -O GPU -Exampe
sauber:
-RM -f GPU -Exampe

Um das Beispiel auszuführen, kompilieren Sie es:

machen

Dann führen Sie das Programm aus:

./GPU-Beispiel

Wie Sie sehen können, läuft die CPU -Version (vector_add_cpu) erheblich langsamer als die GPU -Version (vector_add_gpu).

Wenn nicht, müssen Sie möglicherweise die ITER-Definition im GPU-Beispiel einstellen.Cu zu einer höheren Zahl. Dies ist darauf zurückzuführen, dass die GPU-Setup-Zeit länger ist als einige kleinere CPU-intensive Schleifen. Ich fand 65535 gut auf meiner Maschine, aber Ihre Kilometerleistung kann variieren. Sobald Sie diesen Schwellenwert jedoch gelöscht haben, ist die GPU dramatisch schneller als die CPU.

Abschluss

Ich hoffe, Sie haben viel aus unserer Einführung in die GPU -Programmierung mit C gelernt++. Das obige Beispiel wird nicht viel erreicht, aber die demonstrierten Konzepte bieten einen Rahmen, mit dem Sie Ihre Ideen einbeziehen können, um die Kraft Ihrer GPU auszulösen.