Programmation GPU avec C ++

Programmation GPU avec C ++

Aperçu

Dans ce guide, nous explorerons la puissance de la programmation GPU avec C++. Les développeurs peuvent s'attendre à des performances incroyables avec C ++, et l'accès à la puissance phénoménale du GPU avec un langage de bas niveau peut produire une partie du calcul le plus rapide actuellement disponible.

Exigences

Bien que toute machine capable d'exécuter une version moderne de Linux puisse prendre en charge un compilateur C ++, vous aurez besoin d'un GPU basé sur NVIDIA pour suivre cet exercice. Si vous n'avez pas de GPU, vous pouvez faire tourner une instance alimentée par GPU dans Amazon Web Services ou un autre fournisseur de cloud de votre choix.

Si vous choisissez une machine physique, veuillez vous assurer que les pilotes propriétaires NVIDIA sont installés. Vous pouvez trouver des instructions pour cela ici: https: // Linuxhint.com / install-nvidia-pilotes-linux /

En plus du conducteur, vous aurez besoin de la boîte à outils CUDA. Dans cet exemple, nous utiliserons Ubuntu 16.04 LTS, mais il y a des téléchargements disponibles pour la plupart des distributions majeures à l'URL suivante: https: // développeur.nvidia.com / Cuda-Downloads

Pour Ubuntu, vous choisissez le .Téléchargement basé sur Deb. Le fichier téléchargé n'aura pas de .extension deb par défaut, je recommande donc de le renommer pour avoir un .Deb à la fin. Ensuite, vous pouvez installer avec:

Sudo dpkg -i package-name.deb

Vous serez probablement invité à installer une clé GPG, et si oui, suivez les instructions fournies pour le faire.

Une fois que vous avez fait cela, mettez à jour vos référentiels:

Mise à jour Sudo apt-get
sudo apt-get install cuda -y

Une fois terminé, je recommande de redémarrer pour s'assurer que tout est correctement chargé.

Les avantages du développement du GPU

Les processeurs gèrent de nombreuses entrées et sorties différentes et contiennent un grand assortiment de fonctions pour gérer non seulement un large assortiment de besoins du programme, mais aussi pour gérer des configurations matérielles variables. Ils gèrent également la mémoire, la mise en cache, le bus système, la segmentation et les fonctionnalités IO, ce qui en fait une prise de tous les métiers.

Les GPU sont le contraire - ils contiennent de nombreux processeurs individuels qui se concentrent sur des fonctions mathématiques très simples. Pour cette raison, ils traitent les tâches plusieurs fois plus rapidement que les processeurs. En se spécialisant dans les fonctions scalaires (une fonction qui prend une ou plusieurs entrées mais ne renvoie qu'une seule sortie), elles atteignent des performances extrêmes au prix de l'extrême spécialisation.

Exemple de code

Dans l'exemple de code, nous ajoutons des vecteurs ensemble. J'ai ajouté une version CPU et GPU du code pour la comparaison de vitesse.
exemple de GPU.cpp Contenu ci-dessous:

#include "cuda_runtime.H "
#inclure
#inclure
#inclure
#inclure
#inclure
typedef std :: chrono :: high_resolution_clock horloge;
#define iter 65535
// version CPU de la fonction Vector Add
void vector_add_cpu (int * a, int * b, int * c, int n)
int i;
// Ajouter les éléments vectoriels A et B au vecteur c
pour (i = 0; i < n; ++i)
c [i] = a [i] + b [i];


// version GPU de la fonction Vector Add
__Global__ void vector_add_gpu (int * gpu_a, int * gpu_b, int * gpu_c, int n)
int i = threadidx.X;
// non pour la boucle nécessaire car le temps d'exécution CUDA
// enfilera ces temps iter
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));
// Nous avons besoin de variables accessibles au GPU,
// donc cudamallocmanged les fournit
CUDAMALLOCMANED (& gpu_a, iter * sizeof (int));
CUDAMALLOCMANED (& gpu_b, iter * sizeof (int));
CUDAMALLOCMANED (& gpu_c, iter * sizeof (int));
pour (int i = 0; i < ITER; ++i)
a [i] = i;
b [i] = i;
c [i] = i;

// appelle la fonction CPU et le temps
auto cpu_start = horloge :: maintenant ();
vector_add_cpu (a, b, c, iter);
auto cpu_end = horloge :: maintenant ();
std :: cout << "vector_add_cpu: "
<< std::chrono::duration_cast(cpu_end - cpu_start).compter()
<< " nanoseconds.\n";
// appelle la fonction GPU et le temps
// Les freins à triple angle sont une extension d'exécution CUDA qui permet
// Paramètres d'un appel de noyau Cuda à passer.
// Dans cet exemple, nous passons un bloc de thread avec des threads iter.
auto gpu_start = horloge :: maintenant ();
vector_add_gpu <<<1, ITER>>> (gpu_a, gpu_b, gpu_c, iter);
CudadevicesYnChronize ();
auto gpu_end = horloge :: maintenant ();
std :: cout << "vector_add_gpu: "
<< std::chrono::duration_cast(gpu_end - gpu_start).compter()
<< " nanoseconds.\n";
// Libère les allocations de mémoire basées sur la fonction GPU
Cudafree (a);
Cudafree (b);
Cudafree (c);
// Libère les allocations de mémoire basées sur la fonction du processeur
libre (a);
libre (b);
libre (c);
retour 0;

Makefile Contenu ci-dessous:

Inc = -i / usr / local / cuda / inclure
Nvcc = / usr / local / cuda / bin / nvcc
Nvcc_opt = -std = c ++ 11
tous:
$ (Nvcc) $ (nvcc_opt) GPU-exemple.CPP -O-GPU-exemple
faire le ménage:
-Exemple de GPU RM -F

Pour exécuter l'exemple, compilez-le:

faire

Ensuite, exécutez le programme:

./ Exemple de GPU

Comme vous pouvez le voir, la version CPU (vector_add_cpu) s'exécute considérablement plus lente que la version GPU (vector_add_gpu).

Sinon, vous devrez peut-être ajuster l'itément de définition dans l'exemple de GPU.cu à un nombre plus élevé. Cela est dû au temps de configuration du GPU étant plus long que certaines boucles à forte intensité de processeur. J'ai trouvé 65535 pour bien fonctionner sur ma machine, mais votre kilométrage peut varier. Cependant, une fois que vous avez effacé ce seuil, le GPU est considérablement plus rapide que le CPU.

Conclusion

J'espère que vous avez beaucoup appris de notre introduction dans la programmation GPU avec C++. L'exemple ci-dessus n'accepte pas beaucoup, mais les concepts démontrés fournissent un cadre que vous pouvez utiliser pour incorporer vos idées pour libérer la puissance de votre GPU.