Création d'un bot pour participer à la mini coupe AI. Expérience GPU


Suite de l' article 1 et de l' article 2 .


Ci-dessous sous la coupe, je vais parler de l'expérience de l'auteur dans l'utilisation du GPU pour les calculs, y compris dans le cadre de la création d'un bot pour participer à la mini-tasse AI. Mais plutôt, c'est un essai sur le sujet du GPU.


- Votre nom est magique ...
-Tu sais, Joel? .. La magie s'en va ...


Dans l'enfance, nous parlons de l'âge où la chimie n'est pas encore en cours à l'école ou qui commence tout juste à avoir lieu, l'auteur a été fasciné par la réaction brûlante, il se trouve que ses parents ne l'ont pas dérangé et que la friche de Moscou près de la maison a parfois été éclairée par des flashs de diverses activités pour enfants, une fusée maison sur fond noir poudre à canon, sur caramel sucre-nitrate, etc. Deux circonstances ont limité les fantasmes des enfants: la décomposition de la nitroglycérine dans un laboratoire à domicile avec un plafond sifflant d'acides et le trajet vers une salle de police pour tenter d'obtenir des produits chimiques dans l'une des entreprises de défense généreusement dispersées dans la région métropolitaine d'Aviamotornaya.


Et puis une école de physique avec des ordinateurs yamaha msx est apparue, une calculatrice MK programmable à la maison, et il n'y avait pas de temps pour la chimie. L'intérêt de l'enfant s'est déplacé vers les ordinateurs. Et ce qui manquait à l'auteur dès la première connaissance de l'ordinateur, c'était la réaction de brûlure, ses programmes fumaient, il n'y avait pas cette sensation de puissance naturelle. On pouvait voir le processus d'optimisation des calculs dans les jeux, mais à cette époque l'auteur ne savait pas comment remplacer le calcul de sin () par le tableau de valeurs de cette fonction, il n'y avait pas Internet ...


Ainsi, l'auteur a pu ressentir de la joie grâce à la puissance de calcul, à la gravure propre, j'utilise le GPU dans les calculs.


Sur un habr il y a quelques bons articles sur les calculs sur GPU. Il existe également de nombreux exemples sur Internet, il a donc été décidé d'écrire samedi matin sur les sentiments personnels et il est possible de pousser les autres vers le parallélisme de masse.


Commençons par des formulaires simples. L'informatique GPU prend en charge plusieurs cadres, mais les plus connus sont NVIDIA CUDA et OpenCL. Nous prendrons CUDA et nous devrons immédiatement restreindre notre ensemble de langages de programmation au C ++. Il existe des bibliothèques pour se connecter à CUDA dans d'autres langages de programmation, par exemple, ALEA GPU en C #, mais c'est plutôt le sujet d'un article de revue distinct.


Comme ils ne pouvaient pas fabriquer une voiture de masse avec un moteur à réaction à la fois, bien que certains de ses indicateurs soient plus élevés que ceux d'un moteur à combustion interne, des calculs parallèles ne sont pas toujours possibles à appliquer dans des problèmes réels. L'application principale du calcul parallèle: vous avez besoin d'une tâche contenant un élément de caractère de masse, la multiplicité. Dans notre cas de création d'un bot, un réseau de neurones (beaucoup de neurones, de connexions neuronales) tombe sous la masse et une population de bots (calculer la dynamique du mouvement, les collisions pour chaque bot prennent un certain temps, si les bots sont de 300 à 1000, alors le processeur central se rend et vous observerez juste ralentir couve de votre programme, comme de longues pauses entre les images de visualisation).


La meilleure option de masse est lorsque chaque élément des calculs ne dépend pas du résultat des calculs sur un autre élément de la liste, par exemple, la simple tâche de tri d'un tableau est déjà envahie par toutes sortes de trucs, car la position du nombre dans le tableau dépend d'autres chiffres et ne peut pas être prise sur le front sur un cycle parallèle . Pour simplifier la formulation: le premier signe d'un caractère de masse réussi est que si vous n'avez pas besoin de modifier la position d'un élément dans le tableau, vous pouvez effectuer librement des calculs sur celui-ci, prendre les valeurs des autres éléments pour cela, mais ne le déplacez pas de sa place. Quelque chose comme un conte de fées: ne changez pas l'ordre des éléments, sinon le GPU se transformera en citrouille.


Dans les langages de programmation modernes, il existe des constructions qui peuvent être exécutées en parallèle sur plusieurs cœurs d'un processeur central ou de threads logiques et elles sont largement utilisées, mais l'auteur concentre le lecteur sur le parallélisme de masse, lorsque le nombre de modules d'exécution dépasse des centaines ou des milliers d'unités.


Les premiers éléments de structures parallèles sont apparus: un cycle parallèle . Pour la plupart des tâches, ce sera suffisant. Au sens large, c'est la quintessence
calcul parallèle.


Un exemple d'écriture de la boucle principale dans CUDA (noyau):


int tid = blockIdx.x * blockDim.x + threadIdx.x; int threadN = gridDim.x * blockDim.x; for (int pos = tid; pos < numElements; pos += threadN) { //    pos,     ,       thread     pos.  :    thread    ,  thread   pos=1146     thread c  pos=956.        .           . } 

Beaucoup de choses ont été écrites dans la documentation et les revues de CUDA , sur les blocs GPU, sur les threads produits dans ces blocs, comment paralléliser la tâche sur eux. Mais si vous avez un tableau de données et qu'il est clairement constitué d'éléments de masse, utilisez la forme de boucle ci-dessus, car elle est visuellement similaire à une boucle régulière sous forme, ce qui est agréable, mais malheureusement pas dans son contenu.


Je pense que le lecteur comprend déjà que la classe de tâches se rétrécit rapidement par rapport à la programmation parallèle de masse. Si nous parlons de création de jeux, de moteurs de rendu 3D, de réseaux de neurones, de montage vidéo et d'autres tâches similaires, la suppression des actions de lecteur indépendant est fortement épuisée, il existe de grands programmes, de petits programmes, des cadres, des bibliothèques connues et inconnues pour ces tâches. Autrement dit, la zone reste juste du sujet, pour créer votre propre petite fusée informatique, pas SpaceX et Roscosmos, mais quelque chose de simple, mais complètement mauvais pour les calculs.



Voici une photo d'une fusée complètement enflammée représentée.


En parlant de tâches qu'un cycle parallèle entre vos mains ne pourra pas résoudre. Et les créateurs de CUDA en la personne des développeurs NVIDIA y ont déjà pensé.


Il y a une bibliothèque Thrust à certains endroits utile jusqu'à ce que "aucune option" ne soit faite différemment. Soit dit en passant, n'a pas trouvé son examen complet sur Habré.


Pour comprendre comment cela fonctionne, vous devez d'abord dire trois phrases sur les principes de CUDA. Si vous avez besoin de plus de mots, vous pouvez lire le lien.


Les principes de CUDA:


Les calculs ont lieu sur le GPU, dont le programme est le noyau, et vous devez l'écrire en C. Le noyau, à son tour, ne communique qu'avec la mémoire du GPU et vous devez charger les données dans la mémoire du processeur vidéo à partir du programme principal et les télécharger à nouveau dans le programme. Les algorithmes sophistiqués sur CUDA nécessitent une flexibilité de l'esprit.


Ainsi, la bibliothèque Thrust supprime la routine et prend en charge certaines des tâches "complexes" de CUDA, telles que la sommation des tableaux ou leur tri. Vous n'avez plus besoin d'écrire un noyau séparé, de charger des pointeurs dans la mémoire et de copier les données de ces pointeurs dans la mémoire du GPU. Tout le mystère se produira sous vos yeux dans le programme principal et avec une vitesse légèrement inférieure à CUDA. La bibliothèque Thrust est écrite en CUDA, il s'agit donc d'un champ à baie unique en termes de performances.


Ce que vous devez faire dans Thrust est de créer un tableau (thrust :: vector) dans sa bibliothèque, qui est compatible avec les tableaux réguliers (std :: vector). C'est, bien sûr, tout n'est pas si simple, mais le sens de ce que l'auteur a dit est similaire à la vérité. Il y a vraiment deux tableaux, l'un sur le GPU (périphérique), l'autre dans le programme principal (hôte).


Un exemple montrera la simplicité de la syntaxe (tableaux X, Y, Z):


 // initialize X to 0,1,2,3, .... thrust::sequence(X.begin(), X.end()); // compute Y = -X thrust::transform(X.begin(), X.end(), Y.begin(), thrust::negate<int>()); // fill Z with twos thrust::fill(Z.begin(), Z.end(), 2); // compute Y = X mod 2 thrust::transform(X.begin(), X.end(), Z.begin(), Y.begin(), thrust::modulus<int>()); // replace all the ones in Y with tens thrust::replace(Y.begin(), Y.end(), 1, 10); 

Vous pouvez voir à quel point il est inoffensif dans le contexte de la création du noyau CUDA, et l'ensemble des fonctions dans Thrust est grand . En commençant par travailler avec des variables aléatoires, qui dans CUDA est effectué par une bibliothèque cuRAND distincte (de préférence exécutée par un noyau séparé), pour trier, sommer et écrire vos fonctions selon des fonctionnalités proches des fonctions du noyau.


L'auteur a peu d'expérience avec CUDA et C ++, deux mois. À propos de cette année sur C #. Bien sûr, cela contredit légèrement le début de l'article sur sa connaissance précoce des ordinateurs, de la physique scolaire et des mathématiques appliquées en tant qu'éducation. Je vais le dire. Mais pourquoi j'écris cet article, ce n'est pas que je maîtrise tout comme ça, mais que le C ++ s'est avéré être un langage confortable (j'avais l'habitude d'en avoir un peu peur, sur fond d'articles de type Haber «fonctions lambda → surcharge des opérateurs internes, comme tout redéfinir "), il est clair que les années de son développement ont conduit à des environnements de développement (IDE) assez conviviaux. Le langage lui-même dans sa dernière version, on dirait qu'il recueille les ordures de la mémoire, je ne sais pas comment c'était avant. Au moins, les programmes écrits par l'auteur sur les constructions algorithmiques les plus simples ont généré des algorithmes de calcul pour les bots pendant des jours et il n'y a eu aucune fuite de mémoire et autres défaillances à forte charge. Cela vaut également pour CUDA, au début, cela semble compliqué, mais il est basé sur des principes simples et bien sûr, il est difficile d'initialiser des emplacements sur le GPU par endroits s'il y en a beaucoup, mais ensuite vous aurez votre propre petite fusée, avec de la fumée de la carte vidéo.


Parmi les classes d'objets pour la formation avec le GPU, l'auteur recommande les automates cellulaires . À une époque, il y avait une augmentation de la popularité et de la mode pour eux, mais les réseaux de neurones ont ensuite saisi l'esprit des développeurs.
Jusqu'à:


"Chaque quantité en physique, y compris le temps et l'espace, est finie et discrète."
que pas un automate cellulaire.


Mais c'est beau quand trois formules simples peuvent créer ceci:



S'il sera intéressant de lire sur les automates cellulaires sur CUDA, écrivez dans les commentaires, il y aura du matériel dactylographié pour un petit article.
Et voici la source des automates cellulaires (sous la vidéo il y a des liens vers les sources):



L'idée d'écrire un article après le petit déjeuner, en un souffle me semble fonctionner. Deuxième café. Avoir un bon lecteur de week-end.

Source: https://habr.com/ru/post/fr417757/


All Articles