GPU-Programmierung mit C++

Gpu Programming With C



In diesem Handbuch untersuchen wir die Leistungsfähigkeit der GPU-Programmierung mit C++. Entwickler können mit C++ eine unglaubliche Leistung erwarten, und der Zugriff auf die phänomenale Leistung der GPU mit einer Low-Level-Sprache kann einige der schnellsten derzeit verfügbaren Berechnungen liefern.

Anforderungen

Während jeder Computer, auf dem eine moderne Linux-Version ausgeführt werden kann, einen C++-Compiler unterstützen kann, benötigen Sie eine NVIDIA-basierte GPU, um diese Übung durchzuführen. Wenn Sie keine GPU haben, können Sie eine GPU-gestützte Instanz in Amazon Web Services oder einem anderen Cloud-Anbieter Ihrer Wahl einrichten.







Wenn Sie sich für einen physischen Computer entscheiden, stellen Sie bitte sicher, dass die proprietären NVIDIA-Treiber installiert sind. Eine Anleitung dazu findest du 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 sind Downloads für die meisten großen Distributionen unter der folgenden URL verfügbar: https://developer.nvidia.com/cuda-downloads



Für Ubuntu würden Sie den .deb-basierten Download wählen. Die heruntergeladene Datei hat standardmäßig keine .deb-Erweiterung, daher empfehle ich, sie in eine .deb-Datei am Ende umzubenennen. Dann können Sie installieren mit:





sudo dpkg -ichPaketname.deb

Sie werden wahrscheinlich aufgefordert, einen GPG-Schlüssel zu installieren, und befolgen Sie in diesem Fall die bereitgestellten Anweisungen.

Aktualisieren Sie anschließend Ihre Repositorys:



sudo apt-get-Update
sudo apt-get installierenWunder-und

Sobald dies erledigt ist, empfehle ich einen Neustart, um sicherzustellen, dass alles richtig geladen ist.

Die Vorteile der GPU-Entwicklung

CPUs handhaben viele verschiedene Ein- und Ausgänge und enthalten eine große Auswahl an Funktionen, um nicht nur eine breite Palette von Programmanforderungen zu erfüllen, sondern auch um unterschiedliche Hardwarekonfigurationen zu verwalten. Sie verarbeiten auch Speicher, Caching, den Systembus, die Segmentierung und die IO-Funktionalität, was sie zu einem Alleskönner macht.

GPUs sind das Gegenteil – sie enthalten viele einzelne Prozessoren, die sich auf sehr einfache mathematische Funktionen konzentrieren. Aus diesem Grund verarbeiten sie Aufgaben um ein Vielfaches schneller als CPUs. Durch die Spezialisierung auf Skalarfunktionen (eine Funktion, die eine oder mehrere Eingaben entgegennimmt, aber nur eine einzige Ausgabe zurückgibt), erreichen sie eine extreme Leistung auf Kosten einer extremen Spezialisierung.

Beispielcode

Im Beispielcode fügen wir Vektoren zusammen. 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

Typdefstd::Chrono::hochauflösende_UhrUhr;

#define ITER 65535

// CPU-Version der Vektor-Add-Funktion
Leerevector_add_cpu(int *zu,int *B,int *C,intn) {
intich;

// Addiere die Vektorelemente a und b zum Vektor c
zum (ich= 0;ich<n; ++ich) {
C[ich] =zu[ich] +B[ich];
}
}

// GPU-Version der Vektor-Add-Funktion
__global__Leerevector_add_gpu(int *gpu_a,int *gpu_b,int *gpu_c,intn) {
intich=threadIdx.x;
// Keine for-Schleife erforderlich, da die CUDA-Laufzeit
// wird diesen ITER mal einfädeln
gpu_c[ich] =gpu_a[ich] +gpu_b[ich];
}

inthauptsächlich() {

int *zu,*B,*C;
int *gpu_a,*gpu_b,*gpu_c;

zu= (int *)malloc(ITER* Größe von(int));
B= (int *)malloc(ITER* Größe von(int));
C= (int *)malloc(ITER* Größe von(int));

// Wir brauchen Variablen, auf die die GPU zugreifen kann,
// cudaMallocManaged stellt diese zur Verfügung
cudaMallocManaged(&gpu_a, ITER* Größe von(int));
cudaMallocManaged(&gpu_b, ITER* Größe von(int));
cudaMallocManaged(&gpu_c, ITER* Größe von(int));

zum (intich= 0;ich<ITER; ++ich) {
zu[ich] =ich;
B[ich] =ich;
C[ich] =ich;
}

// Rufen Sie die CPU-Funktion auf und timen Sie es
Autocpu_start=Uhr::jetzt();
vector_add_cpu(a, b, c, ITER);
Autocpu_end=Uhr::jetzt();
std::Kosten << 'vector_add_cpu: '
<<std::Chrono::Dauer_Besetzung<std::Chrono::Nanosekunden>(cpu_end-cpu_start).zählen()
<< ' Nanosekunden. ';

// Rufen Sie die GPU-Funktion auf und timen Sie es
// Die Dreifachwinkelbremse ist eine CUDA-Laufzeiterweiterung, die es ermöglicht
// Parameter eines zu übergebenden CUDA-Kernel-Aufrufs.
// In diesem Beispiel übergeben wir einen Threadblock mit ITER-Threads.
Autogpu_start=Uhr::jetzt();
vector_add_gpu<<<1, ITER>>> (gpu_a, gpu_b, gpu_c, ITER);
cudaDeviceSynchronize();
Autogpu_end=Uhr::jetzt();
std::Kosten << 'vector_add_gpu: '
<<std::Chrono::Dauer_Besetzung<std::Chrono::Nanosekunden>(gpu_end-gpu_start).zählen()
<< ' Nanosekunden. ';

// Geben Sie die GPU-funktionsbasierten Speicherzuweisungen frei
cudaFree(zu);
cudaFree(B);
cudaFree(C);

// Die CPU-funktionsbasierten Speicherzuweisungen freigeben
kostenlos(zu);
kostenlos(B);
kostenlos(C);

Rückkehr 0;
}

Makefile Inhalt unten:

INC=-ich/usr/lokal/Wunder/enthalten
NVCC=/usr/lokal/Wunder/bin/nvcc
NVCC_OPT=-std=c++elf

alle:
$(NVCC)$(NVCC_OPT)gpu-beispiel.cpp-oderGPU-Beispiel

sauber:
-rm -FGPU-Beispiel

Um das Beispiel auszuführen, kompilieren Sie es:

machen

Führen Sie dann das Programm aus:

./GPU-Beispiel

Wie Sie sehen, läuft die CPU-Version (vector_add_cpu) deutlich langsamer als die GPU-Version (vector_add_gpu).

Wenn nicht, müssen Sie möglicherweise die ITER-Definition in gpu-example.cu auf eine höhere Zahl anpassen. Dies liegt daran, dass die GPU-Setup-Zeit länger ist als bei einigen kleineren CPU-intensiven Schleifen. Ich habe festgestellt, dass 65535 auf meiner Maschine gut funktioniert, aber Ihre Laufleistung kann variieren. Sobald Sie diesen Schwellenwert jedoch überschreiten, ist die GPU dramatisch schneller als die CPU.

Abschluss

Ich hoffe, Sie haben aus unserer Einführung in die GPU-Programmierung mit C++ viel gelernt. Das obige Beispiel bringt nicht viel, aber die demonstrierten Konzepte bieten einen Rahmen, mit dem Sie Ihre Ideen integrieren können, um die Leistung Ihrer GPU zu entfesseln.