Tout d'abord, vous devez être conscient du fait que CUDA ne rendra pas automatiquement les calculs plus rapides. D'une part, parce que la programmation GPU est un art, et il peut être très, très difficile de l' obtenir droit . D'autre part, parce que les GPU ne sont bien adaptés qu'à certains types de calculs.
Cela peut sembler déroutant, car vous pouvez essentiellement calculer n'importe quoi sur le GPU. Le point clé est, bien sûr, de savoir si vous obtiendrez une bonne accélération ou non. La classification la plus importante ici est de savoir si un problème est parallèle de tâches ou parallèle de données . Le premier se réfère, grosso modo, à des problèmes où plusieurs threads travaillent sur leurs propres tâches, plus ou moins indépendamment. Le second fait référence à des problèmes où de nombreux threads font tous la même chose - mais sur différentes parties des données.
Ce dernier est le genre de problème pour lequel les GPU sont bons: ils ont de nombreux cœurs, et tous les cœurs font de même, mais fonctionnent sur différentes parties des données d'entrée.
Vous avez mentionné que vous avez «des calculs simples mais avec une énorme quantité de données». Bien que cela puisse sembler un problème parfaitement parallèle aux données et donc qu'il était bien adapté pour un GPU, il y a un autre aspect à considérer: les GPU sont ridiculement rapides en termes de puissance de calcul théorique (FLOPS, Floating Point Operations Per Second). Mais ils sont souvent limités par la bande passante mémoire.
Cela conduit à une autre classification des problèmes. À savoir si les problèmes sont liés à la mémoire ou au calcul .
Le premier fait référence à des problèmes où le nombre d'instructions exécutées pour chaque élément de données est faible. Par exemple, considérons une addition de vecteur parallèle: vous devrez lire deux éléments de données, puis effectuer une seule addition, puis écrire la somme dans le vecteur de résultat. Vous ne verrez pas d'accélération en faisant cela sur le GPU, car l'ajout unique ne compense pas les efforts de lecture / écriture de la mémoire.
Le deuxième terme, «calcul lié», fait référence à des problèmes où le nombre d'instructions est élevé par rapport au nombre de lectures / écritures de mémoire. Par exemple, considérons une multiplication matricielle: le nombre d'instructions sera O (n ^ 3) lorsque n est la taille de la matrice. Dans ce cas, on peut s'attendre à ce que le GPU surclasse un CPU à une certaine taille de matrice. Un autre exemple pourrait être celui où de nombreux calculs trigonométriques complexes (sinus / cosinus, etc.) sont effectués sur "quelques" éléments de données.
En règle générale: vous pouvez supposer que la lecture / écriture d'un élément de données à partir de la mémoire GPU "principale" a une latence d'environ 500 instructions ....
Par conséquent, un autre point clé pour les performances des GPU est la localité des données : si vous devez lire ou écrire des données (et dans la plupart des cas, vous devrez le faire ;-)), vous devez vous assurer que les données sont conservées aussi près que possible. possible aux cœurs du GPU. Les GPU disposent ainsi de certaines zones mémoire (appelées «mémoire locale» ou «mémoire partagée») qui ne font généralement que quelques Ko, mais particulièrement efficaces pour les données sur le point d'être impliquées dans un calcul.
Donc, pour le souligner à nouveau: la programmation GPU est un art, qui n'est lié qu'à distance à la programmation parallèle sur le CPU. Des choses comme Threads en Java, avec toute l'infrastructure de concurrence comme ThreadPoolExecutors
, ForkJoinPools
etc. peuvent donner l'impression que vous devez simplement diviser votre travail d'une manière ou d'une autre et le répartir entre plusieurs processeurs. Sur le GPU, vous pouvez rencontrer des défis à un niveau beaucoup plus bas: occupation, pression de registre, pression de mémoire partagée, fusion de mémoire ... pour n'en nommer que quelques-uns.
Cependant, lorsque vous avez un problème lié au calcul et parallèle aux données à résoudre, le GPU est la solution.
Une remarque générale: vous avez spécifiquement demandé CUDA. Mais je vous recommande fortement de jeter également un œil à OpenCL. Il présente plusieurs avantages. Tout d'abord, il s'agit d'une norme industrielle ouverte et indépendante du fournisseur, et il existe des implémentations d'OpenCL par AMD, Apple, Intel et NVIDIA. De plus, il existe une prise en charge beaucoup plus large d'OpenCL dans le monde Java. Le seul cas où je préfère me contenter de CUDA est lorsque vous souhaitez utiliser les bibliothèques d'exécution CUDA, comme CUFFT pour FFT ou CUBLAS pour BLAS (opérations Matrix / Vector). Bien qu'il existe des approches pour fournir des bibliothèques similaires pour OpenCL, elles ne peuvent pas être utilisées directement du côté Java, sauf si vous créez vos propres liaisons JNI pour ces bibliothèques.
Vous pourriez également trouver intéressant d'entendre qu'en octobre 2012, le groupe OpenJDK HotSpot a lancé le projet "Sumatra": http://openjdk.java.net/projects/sumatra/ . Le but de ce projet est de fournir un support GPU directement dans la JVM, avec le support du JIT. Le statut actuel et les premiers résultats peuvent être vus dans leur liste de diffusion à http://mail.openjdk.java.net/mailman/listinfo/sumatra-dev
Cependant, il y a quelque temps, j'ai rassemblé quelques ressources liées à "Java sur le GPU" en général. Je vais les résumer à nouveau ici, sans ordre particulier.
( Avertissement : je suis l'auteur de http://jcuda.org/ et http://jocl.org/ )
Traduction de code (octet) et génération de code OpenCL:
https://github.com/aparapi/aparapi : une bibliothèque open-source créée et activement maintenue par AMD. Dans une classe spéciale "Kernel", on peut surcharger une méthode spécifique qui doit être exécutée en parallèle. Le code d'octet de cette méthode est chargé lors de l'exécution à l'aide d'un propre lecteur de bytecode. Le code est traduit en code OpenCL, qui est ensuite compilé à l'aide du compilateur OpenCL. Le résultat peut ensuite être exécuté sur le périphérique OpenCL, qui peut être un GPU ou un CPU. Si la compilation dans OpenCL n'est pas possible (ou si aucun OpenCL n'est disponible), le code sera toujours exécuté en parallèle, en utilisant un Thread Pool.
https://github.com/pcpratts/rootbeer1 : une bibliothèque open-source pour convertir des parties de Java en programmes CUDA. Il propose des interfaces dédiées qui peuvent être implémentées pour indiquer qu'une certaine classe doit être exécutée sur le GPU. Contrairement à Aparapi, il essaie de sérialiser automatiquement les données «pertinentes» (c'est-à-dire la partie pertinente complète du graphe d'objets!) En une représentation adaptée au GPU.
https://code.google.com/archive/p/java-gpu/ : une bibliothèque pour traduire du code Java annoté (avec certaines limitations) en code CUDA, qui est ensuite compilé dans une bibliothèque qui exécute le code sur le GPU. La bibliothèque a été développée dans le cadre d'une thèse de doctorat, qui contient des informations de base approfondies sur le processus de traduction.
https://github.com/ochafik/ScalaCL : liaisons Scala pour OpenCL. Permet aux collections Scala spéciales d'être traitées en parallèle avec OpenCL. Les fonctions qui sont appelées sur les éléments des collections peuvent être des fonctions Scala habituelles (avec quelques limitations) qui sont ensuite traduites en noyaux OpenCL.
Extensions de langue
http://www.ateji.com/px/index.html : Une extension de langage pour Java qui permet des constructions parallèles (par exemple des boucles for parallèles, style OpenMP) qui sont ensuite exécutées sur le GPU avec OpenCL. Malheureusement, ce projet très prometteur n'est plus maintenu.
http://www.habanero.rice.edu/Publications.html (JCUDA): Une bibliothèque qui peut traduire du code Java spécial (appelé code JCUDA) en code Java et CUDA-C, qui peut ensuite être compilé et exécuté sur le GPU. Cependant, la bibliothèque ne semble pas être accessible au public.
https://www2.informatik.uni-erlangen.de/EN/research/JavaOpenMP/index.html : extension de langage Java pour les constructions OpenMP, avec un backend CUDA
Bibliothèques de liaison Java OpenCL / CUDA
https://github.com/ochafik/JavaCL : liaisons Java pour OpenCL: bibliothèque OpenCL orientée objet, basée sur des liaisons de bas niveau générées automatiquement
http://jogamp.org/jocl/www/ : liaisons Java pour OpenCL: bibliothèque OpenCL orientée objet, basée sur des liaisons de bas niveau générées automatiquement
http://www.lwjgl.org/ : liaisons Java pour OpenCL: liaisons de bas niveau générées automatiquement et classes de commodité orientées objet
http://jocl.org/ : liaisons Java pour OpenCL: liaisons de bas niveau qui sont un mappage 1: 1 de l'API OpenCL d'origine
http://jcuda.org/ : liaisons Java pour CUDA: liaisons de bas niveau qui sont un mappage 1: 1 de l'API CUDA d'origine
Divers
http://sourceforge.net/projects/jopencl/ : liaisons Java pour OpenCL. Semblent ne plus être entretenus depuis 2010
http://www.hoopoe-cloud.com/ : liaisons Java pour CUDA. Semblent ne plus être entretenu