Je travaille sur un projet d'entreprise réalisé en Java, et il a besoin d'une énorme puissance de calcul pour calculer les marchés commerciaux. Mathématiques simples, mais avec une énorme quantité de données.
Nous avons commandé des GPU CUDA pour l'essayer et comme Java n'est pas pris en charge par CUDA, je me demande par où commencer. Dois-je créer une interface JNI? Dois-je utiliser JCUDA ou existe-t-il d'autres moyens?
Je n'ai pas d'expérience dans ce domaine et j'aimerais que quelqu'un puisse me diriger vers quelque chose pour que je puisse commencer à faire des recherches et à apprendre.
Réponses:
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
la source
Je commencerais par utiliser l'un des projets disponibles pour Java et CUDA: http://www.jcuda.org/
la source
D'après les recherches que j'ai faites, si vous ciblez les GPU Nvidia et avez décidé d'utiliser CUDA sur OpenCL , j'ai trouvé trois façons d'utiliser l'API CUDA en java.
Toutes ces réponses ne sont fondamentalement que des moyens d'utiliser le code C / C ++ en Java. Vous devriez vous demander pourquoi vous devez utiliser Java et si vous ne pouvez pas le faire en C / C ++ à la place.
Si vous aimez Java et savez comment l'utiliser et que vous ne voulez pas travailler avec toute la gestion des pointeurs et ce qui n'est pas fourni avec C / C ++, JCuda est probablement la réponse. D'un autre côté, la bibliothèque CUDA Thrust et d'autres bibliothèques comme elle peuvent être utilisées pour faire une grande partie de la gestion des pointeurs en C / C ++ et peut-être devriez-vous regarder cela.
Si vous aimez C / C ++ et que la gestion des pointeurs ne vous dérange pas, mais qu'il existe d'autres contraintes qui vous obligent à utiliser Java, JNI pourrait être la meilleure approche. Cependant, si vos méthodes JNI ne sont que des wrappers pour les commandes du noyau, vous pouvez tout aussi bien utiliser JCuda.
Il existe quelques alternatives à JCuda telles que Cuda4J et Root Beer, mais celles-ci ne semblent pas être maintenues. Alors qu'au moment de la rédaction de cet article, ce JCuda prend en charge CUDA 10.1. qui est le SDK CUDA le plus à jour.
De plus, il existe quelques bibliothèques java qui utilisent CUDA, telles que deeplearning4j et Hadoop, qui peuvent être en mesure de faire ce que vous recherchez sans vous obliger à écrire directement le code du noyau. Je ne les ai pas trop examinés cependant.
la source
Marco13 a déjà fourni une excellente réponse .
Dans le cas où vous êtes à la recherche d'un moyen d'utiliser le GPU sans implémenter les noyaux CUDA / OpenCL, je voudrais ajouter une référence à finmath-lib-cuda-extensions (finmath-lib-gpu-extensions) http: // finmath .net / finmath-lib-cuda-extensions / (avertissement: je suis le mainteneur de ce projet).
Le projet fournit une implémentation de "classes vectorielles", pour être précis, une interface appelée
RandomVariable
, qui fournit des opérations arithmétiques et une réduction sur les vecteurs. Il existe des implémentations pour le CPU et le GPU. Il existe des implémentations utilisant la différenciation algorithmique ou des évaluations simples.Les améliorations de performances sur le GPU sont actuellement faibles (mais pour les vecteurs de taille 100.000, vous pouvez obtenir un facteur> 10 améliorations de performances). Cela est dû à la petite taille du noyau. Cela s'améliorera dans une version future.
L'implémentation GPU utilise JCuda et JOCL et est disponible pour les GPU Nvidia et ATI.
La bibliothèque est Apache 2.0 et disponible via Maven Central.
la source