C++

Programmazione GPU con C++

Programmazione GPU con C++

Panoramica

In questa guida, esploreremo la potenza della programmazione GPU con C++. Gli sviluppatori possono aspettarsi prestazioni incredibili con C++ e l'accesso alla potenza fenomenale della GPU con un linguaggio di basso livello può produrre alcuni dei calcoli più veloci attualmente disponibili.

Requisiti

Sebbene qualsiasi macchina in grado di eseguire una versione moderna di Linux possa supportare un compilatore C++, avrai bisogno di una GPU basata su NVIDIA per seguire questo esercizio. Se non disponi di una GPU, puoi avviare un'istanza basata su GPU in Amazon Web Services o in un altro provider cloud di tua scelta.

Se scegli una macchina fisica, assicurati di avere i driver proprietari NVIDIA installati. Puoi trovare le istruzioni per questo qui: https://linuxhint.com/install-nvidia-drivers-linux/

Oltre al driver, avrai bisogno del toolkit CUDA. In questo esempio, useremo Ubuntu 16.04 LTS, ma sono disponibili download per la maggior parte delle principali distribuzioni al seguente URL: https://developer.nvidia.com/cuda-downloads

Per Ubuntu, sceglieresti il .download basato su deb. Il file scaricato non avrà un .deb per impostazione predefinita, quindi consiglio di rinominarlo per avere a .deb alla fine. Quindi, puoi installare con:

sudo dpkg -i nome-pacchetto.deb

Probabilmente ti verrà richiesto di installare una chiave GPG e, in tal caso, segui le istruzioni fornite per farlo.

Dopo averlo fatto, aggiorna i tuoi repository:

sudo apt-get update
sudo apt-get install cuda -y

Una volta fatto, ti consiglio di riavviare per assicurarti che tutto sia caricato correttamente.

I vantaggi dello sviluppo GPU

Le CPU gestiscono molti input e output diversi e contengono un vasto assortimento di funzioni non solo per gestire un vasto assortimento di esigenze del programma, ma anche per gestire diverse configurazioni hardware. Gestiscono anche la memoria, la memorizzazione nella cache, il bus di sistema, la segmentazione e la funzionalità IO, rendendoli un tuttofare.

Le GPU sono l'opposto: contengono molti processori individuali che si concentrano su funzioni matematiche molto semplici. Per questo motivo, elaborano le attività molte volte più velocemente delle CPU. Specializzandosi in funzioni scalari (una funzione che prende uno o più input ma restituisce un solo output), ottengono prestazioni estreme a costo di un'estrema specializzazione.

Codice di esempio

Nel codice di esempio, aggiungiamo i vettori insieme. Ho aggiunto una versione CPU e GPU del codice per il confronto della velocità.
gpu-esempio.cpp contenuti di seguito:

#include "cuda_runtime.h"
#includere
#includere
#includere
#includere
#includere
typedef std::chrono::high_resolution_clock Orologio;
#define ITER 65535
// Versione CPU della funzione vector add vector
void vector_add_cpu(int *a, int *b, int *c, int n)
int io;
// Aggiungi gli elementi del vettore a e b al vettore c
per (i = 0; i < n; ++i)
c[i] = a[i] + b[i];


// Versione GPU della funzione di aggiunta vettoriale
__global__ void vector_add_gpu(int *gpu_a, int *gpu_b, int *gpu_c, int n)
int i = threadIdx.X;
// Nessun ciclo for necessario perché il runtime CUDA
// infilerà questo ITER volte
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));
// Abbiamo bisogno di variabili accessibili alla GPU,
// quindi cudaMallocManaged fornisce questi
cudaMallocManaged(&gpu_a, ITER * sizeof(int));
cudaMallocManaged(&gpu_b, ITER * sizeof(int));
cudaMallocManaged(&gpu_c, ITER * sizeof(int));
per (int i = 0; i < ITER; ++i)
a[i] = io;
b[i] = io;
c[i] = io;

// Chiama la funzione CPU e cronometrala
auto cpu_start = Orologio::ora();
vector_add_cpu(a, b, c, ITER);
auto cpu_end = Orologio::ora();
std::cout << "vector_add_cpu: "
<< std::chrono::duration_cast(cpu_end - cpu_start).contare()
<< " nanoseconds.\n";
// Chiama la funzione GPU e cronometrala
// Le parentesi a triplo angolo sono un'estensione di runtime CUDA che consente
// parametri di una chiamata al kernel CUDA da passare.
// In questo esempio, stiamo passando un blocco di thread con thread ITER.
auto gpu_start = Orologio::ora();
vector_add_gpu <<<1, ITER>>> (gpu_a, gpu_b, gpu_c, ITER);
cudaDeviceSynchronize();
auto gpu_end = Orologio::ora();
std::cout << "vector_add_gpu: "
<< std::chrono::duration_cast(gpu_end - gpu_start).contare()
<< " nanoseconds.\n";
// Libera le allocazioni di memoria basate sulla funzione GPU
cudaFree(a);
cudaFree(b);
cudaFree(c);
// Libera le allocazioni di memoria basate sulla funzione della CPU
libero(a);
libero(b);
libero(c);
restituisce 0;

Makefile contenuti di seguito:

INC=-I/usr/local/cuda/include
NVCC=/usr/local/cuda/bin/nvcc
NVCC_OPT=-std=c++11
tutti:
$(NVCC) $(NVCC_OPT) esempio gpu.cpp -o gpu-esempio
pulito:
-rm -f gpu-esempio

Per eseguire l'esempio, compilalo:

rendere

Quindi eseguire il programma:

./esempio-gpu

Come puoi vedere, la versione CPU (vector_add_cpu) funziona molto più lentamente della versione GPU (vector_add_gpu).

In caso contrario, potrebbe essere necessario modificare la definizione di ITER in gpu-example.cu a un numero più alto. Ciò è dovuto al fatto che il tempo di configurazione della GPU è più lungo di alcuni loop più piccoli che richiedono molta CPU. Ho trovato che 65535 funziona bene sulla mia macchina, ma il tuo chilometraggio può variare. Tuttavia, una volta superata questa soglia, la GPU è notevolmente più veloce della CPU.

Conclusione

Spero che tu abbia imparato molto dalla nostra introduzione alla programmazione GPU con C++. L'esempio sopra non fa molto, ma i concetti dimostrati forniscono un framework che puoi usare per incorporare le tue idee per liberare la potenza della tua GPU.

Installa l'ultimo gioco di strategia OpenRA su Ubuntu Linux
OpenRA è un motore di gioco di strategia in tempo reale gratuito/libero che ricrea i primi giochi di Westwood come il classico Command & Conquer: Red ...
Installa l'ultimo Dolphin Emulator per Gamecube e Wii su Linux
L'emulatore Dolphin ti consente di giocare ai giochi Gamecube e Wii scelti su Personal Computer Linux (PC). Essendo un emulatore di giochi liberament...
Come utilizzare GameConqueror Cheat Engine in Linux
L'articolo copre una guida sull'utilizzo del motore cheat GameConqueror in Linux. Molti utenti che giocano su Windows utilizzano spesso l'applicazione...