GPGPU facile avec JavaCL... ... et trivial avec ScalaCL          by Olivier Chafik             @ochafik                   ...
Monstres de puissance                        2
Répartition inéquitable•                              3
Mettons les GPUs au travail !•   Concepts de base d’OpenCL•   JavaCL en quelques lignes de code•   Pour les paresseux et l...
SpeakerOlivier Chafik (@ochafik)•Développeur C++ / Java (3D, finance)•Hobby = interopérabilité • JNAerator, BridJ : des bi...
OpenCL             =OpenGL pour calculs généralistes                                   6
OpenCL              =environnement d’exécution      multi-plateforme                            7
Pourquoi OpenCL ?•   Puissance gratuite (x10 à x100)•   Standard multi-vendeur, CPU + GPU•   Close-to-metal : parallélisme...
Charge utile d’OpenCL•   Task : appel unique•   Kernel : appels massivement parallèles N-dimensions                       ...
Orienté événement•   Opérations asynchrones & enchaînables•   Renvoient events, en attendre d’autres    Écriture buffer  ...
Exemple de boucle CCalcul de sinus & cosinus en boucle void scos(    const double* params,    double* out,    int length){...
Kernel OpenCL équivalentCalcul de sinus & cosinus parallèle (simplifié)#pragma OpenCL EXTENSION cl_khr_fp64 : enablekernel...
Kernels•   Dialecte du C•   Fonctions math vectorielles (SIMD sur float4, int8…)•   Code connait sa position d’exécution• ...
Code hôte•   Choix devices + création contexte•   Création file d’exécution•   Compilation kernels•   Allocation buffers /...
JavaCL (BSD)•   1ers bindings OpenCL•   API orientée-objet•   Maven Central (jar unique)•   Communauté active             ...
DémoHello World              16
Sous le capot…OpenCL bindings générés par JNAerator :BridJ fournit la “colle” à l’exécution                               ...
Plus que des bindings•   Gestion mémoire débrayable•   Cache automatique des binaires•   Inclusion depuis URLs•   Générate...
Plus que des bindings (2)• FFT complexe dun tableau de primitives : DoubleFFTPow2 fft = new DoubleFFTPow2(); double[] tran...
DémoInteractive Image Editor                           20
Les pièges d’OpenCL•   Coût des transferts RAM / GPU•   Contextes mixtes CPU / GPU :    seulement sur APU AMD•   Brancheme...
Encore des pièges•   Peu de formats d’image garantis•   Endianness parfois différente entre hôte & device•   Apple ne supp...
Chasse à la performance sur GPU•   Groupes d’exécution : memoire locale, barrières…•   Répartition de charge multi-GPU•   ...
JavaCL 1.0.0-RC2•   32 & 64 bits / Windows, Linux, MacOS X•   NVIDIA, AMD, Intel & Apple•   OpenCL 1.0 / 1.1    (1.2 peu r...
Rendre OpenCL plus facile ?•   Éviter d’écrire les kernels en C•   Cacher l’asynchronicité•   Accepter performance sub-opt...
OpenCL “traduit”•   Apapapi (AMD)    •   Java    •   Cache beaucoup (trop ?) de détails•   ScalaCL (votre serviteur)      ...
ScalaCL•   Collections Scala asynchrones•   Stockage + Opérations OpenCL (map, filter, sum…)•   Traduction Scala / OpenCL ...
Quest-ce que Scala ?•   Saint-Graal des langages (impératif, fonctionnel, objet…)•   Tuples + Collections mutables / immut...
Quelques lignes de Scala…val intervalle = (0 until 100000)val cosSinCol = intervalle.map(i => {   val f = 0.2   (cos(i * f...
Les mêmes lignes sur GPUimport scalacl._implicit val context = Context.best(DoubleSupport)val intervalle = (0 until 100000...
DémoConsole GPU              31
Comment ça marche•   Conversion fonctions Scala => code OpenCL•   3 collections    •   CLRange : intervalle    •   CLArray...
Des collections asynchrones•   Contrainte : les collections Scala ont l’air synchrones•   Principe : single writer, multip...
Des collections asynchronesval tab = Array(1, 2, 3, 4).clval resultat = tab.map(x => x * 2).map(x – 5)// resultat pas enco...
Traduire Scala en OpenCL ?                             35
Traduire Scala en OpenCL ?•   Limitations (pas de classes)•   Élimination des tuples•   Élimination du code “objet”       ...
Ceci est du code objetfor (i <- 0 until 100){    …}                                  37
Une lambda peut en cacher une autre(0 until 100).foreach(i =>{     …})                                      38
Des boucles 5x plus rapides(0 until 100).foreach(i =>   var i = 0{                            while (i < 100) {     …     ...
Aller plus vite : flot d’opérationscol.map(f).filter(g).map(h).reduceLeft(i)•Collections intermédiaires + lambdas•f, g, h ...
ScalaCL : encore un prototype• Scalaxy : spin-off généraliste plutôt stable• Fonctionalités expérimentales :  captures, ra...
OpenCL• Votre GPU se tourne les pouces• Write once, run (almost) everywhere         http://javacl.googlecode.com         h...
Questions            43
44
CLFilteredArray[T]•   Tableau de valeurs + Bitmap de présence•   Compactage :    •   Somme préfixée    •   Recopie aux inc...
JavaCL : générateur de codeLinearAlgebraKernels.cl :LinearAlgebraKernels.java :                                     46
JavaCL : hôte minimaliste• Choix dun contexte + file dexécution :  CLContext context = JavaCL.createBestContext(DoubleSupp...
Prochain SlideShare
Chargement dans…5
×

GPGPU facile avec JavaCL, et trivial avec ScalaCL !

6 106 vues

Publié le

Présentation JavaCL / ScalaCL au Devoxx France 2012 :

http://devoxx.fr/display/FR12/GPGPU+facile+avec+JavaCL%2C+et+trivial+avec+ScalaCL+%21

0 commentaire
1 j’aime
Statistiques
Remarques
  • Soyez le premier à commenter

Aucun téléchargement
Vues
Nombre de vues
6 106
Sur SlideShare
0
Issues des intégrations
0
Intégrations
10
Actions
Partages
0
Téléchargements
33
Commentaires
0
J’aime
1
Intégrations 0
Aucune incorporation

Aucune remarque pour cette diapositive
  • Bonjour à tous Aujourd’hui je suis venu vous parler de GPGPU, c’est à dire de l’utilisation des cartes graphiques pour effectuer des calculs généralistes.
  • Les cartes graphiques haut de gamme actuelles sont de vrais monstres, Y compris par rapport aux meilleurs CPUs.
  • Mais en dehors des jeux dernier-cri, les cartes graphiques sont rarement exploitées en mesure avec leurs capacités
  • Du coup il est temps de mettre les cartes graphiques au travail ! Dans cette courte intervention je vais donc vous présenter les principes d’OpenCL, et comment l’exploiter en Java. Je terminerai en vous présentant ScalaCL, qui ouvre de nouveaux horizons aux paresseux et aux pressés
  • Tout d’abord un mot sur moi : Développeur C++ / Java avec un background dans la 3D et la finance. Je passe beaucoup de mon temps libre sur des projets Open-Source d’interopérabilité Java / C / C++. J’ai notamment créé JNAerator et BridJ, qui permettent d’obtenir des bindings natifs très facilement. Je me suis basé sur ces deux projets pour créér JavaCL et ScalaCL, qui poussent les limites de l’interopérabilité d’une autre manière.
  • Alors, pour simplifier à l’extrême, OpenCL c’est comme un OpenGL non graphique, où on ne fait que des shaders.
  • Pour ceux à qui OpenGL ne parle pas, On peut aussi voir ça comme un environnement d’exécution de code multi-plateforme (la ressemblance avec Java s’arrête là !)
  • Que gagneriez vous à utiliser OpenCL ? Rendre les parties critiques de vos programmes 10 à 100 fois plus rapides. Exploiter non seulement les GPUs, mais aussi les CPUs (bien plus efficacement qu’en Java !), en particulier en profitant au max du SSE et des multi-coeurs ca peut éventuellement accélérer la visualisation dans votre appli, partage données avec 3D
  • Donc qu’est-ce qu’on exécute au juste, dans OpenCL ? En gros y’a 2 types de charge utiles : Des taches, qui sont juste des fonctions appelées une fois, classiquement Des kernels, qui sont appelés des milliers / millions de fois, partiellement en parallèle, avec un indice qui varie à chaque appel
  • Ces tâches et ces kernels sont asynchrones &amp; enchaînables Chaque opération renvoie un évenement de complétion, et peut attendre d’autres événements avant de s’exécuter On peut donc créer un workflow qui implique des lectures, des écritures, et des éxécutions, et être notifié quand les résultats sont prêts
  • Pour rendre ça un peu plus concret, Prenons un exemple de code C qui calcule des sinus et cosinus de chaque valeur d’un tableau d’entrée, Et les écrit dans un tableau de sortie 2 fois plus grand
  • La version OpenCL de ce code est très proche La boucle for disparait, la fonction / kernel sera appelée length fois Chaque itération pourra connaître son indice i pour savoir quelle inputs prendre et où mettre son output
  • Donc on écrit les kernels dans un dialecte du C Dans lequel on dispose de fonctions mathématiques sur des types vectoriels Le kernel connait sa position d’exécution en N-dimensions Et son activité principale, c’est de lire et d’écrire des données Remarquons au passage que ça peut se compliquer car plusieurs exécutions peuvent être regrouper et utiliser de la synchronisation entre eux
  • Ces kernels doivent être pris en charge et mis en scene par un code hôte, en C ou en Java
  • JavaCL nous permet d’ écrire ce code hôte facilement Ce sont historiquement les premiers bindings java On a pas mal travaillé sur la doc, et il y a meme un chapitre d’un livre qui lui est consacré
  • Faisons donc une pause dans le bla-bla, regardons du code
  • Voila pour un exemple simple. Au passage j’en profite pour dire que JavaCL utilise des bindings autogeneres par JNAerator, Et qu’ils sont linkés dynamiquement par BridJ, qui est compétitif avec JNI.
  • Il faut savoir que JavaCL fournit plus que de simples bindings
  • JavaCL fournit en outre quelques briques utiles, telles que Des transformees de fourier rapides, Des conversions de format d’images Du calcul matriciel
  • Et un éditeur de kernels interactif, que je vais vous montrer rapidement
  • Bon, maintenant que vous pensez qu’OpenCL est trop facile pour etre vrai, passons en revue certains de ses pieges
  • Peu de formats d’images garantis, mais JavaCL fait de son mieux ATI Radeon = Big Endian X86 = little endian
  • Enfin, pour obtenir du code optimal, il faut un peu d’huile de coude
  • Pour terminer sur JavaCL, Dispo sur les plateformes les plus répandues Compatible avec 1.1 (1.2 pas sous mac)
  • OpenCL est assez facile à utiliser, mais on aimerait parfois… … Quitte à accepter une performance sub-optimale
  • Il y a au moins 2 solutions pour la JVM : Aparapi cache cout des transferts de memoire ScalaCL n’a pas (encore) le soutien d’un géant de l’IT
  • import scalacl._ import math._ implicit val context = Context.best val r = (0 until 100000).cl
  • Alors comment ça marche ? Plugin de compilateur Collections stockées sur GPU
  • Y’a une particularité : La contrainte… Donc on respecte le principe Chaque collection maintient une liste d’opérations de lecture et d’écriture en cours
  • Du coup, dans cet exemple : on obtient un resultat pas encore calculé Et on doit attendre avant de pouvoir y écrire
  • Une autre particularité est evidemment la conversion de Scala a OpenCL : y’a qq differences
  • On accepte donc certaines limitations, Et on élimine tout ce qui ne fait pas C : les tuples et les objets en general
  • Quand je dis objet en general…
  • Ce code utilise en fait secretement une closure, qui le rend tres lent
  • En réecrivant les boucles par leur while equivalent, on obtient du code convertible en C Et on rend le scala plus rapide en general !
  • GPGPU facile avec JavaCL, et trivial avec ScalaCL !

    1. 1. GPGPU facile avec JavaCL... ... et trivial avec ScalaCL by Olivier Chafik @ochafik 1
    2. 2. Monstres de puissance 2
    3. 3. Répartition inéquitable• 3
    4. 4. Mettons les GPUs au travail !• Concepts de base d’OpenCL• JavaCL en quelques lignes de code• Pour les paresseux et les pressés : ScalaCL 4
    5. 5. SpeakerOlivier Chafik (@ochafik)•Développeur C++ / Java (3D, finance)•Hobby = interopérabilité • JNAerator, BridJ : des bindings en 1 minute-chrono • JavaCL, ScalaCL 5
    6. 6. OpenCL =OpenGL pour calculs généralistes 6
    7. 7. OpenCL =environnement d’exécution multi-plateforme 7
    8. 8. Pourquoi OpenCL ?• Puissance gratuite (x10 à x100)• Standard multi-vendeur, CPU + GPU• Close-to-metal : parallélisme boucles + SIMD Pousse les multi-coeurs à bout !• Interaction DirectX / OpenGL 8
    9. 9. Charge utile d’OpenCL• Task : appel unique• Kernel : appels massivement parallèles N-dimensions 9
    10. 10. Orienté événement• Opérations asynchrones & enchaînables• Renvoient events, en attendre d’autres Écriture buffer  Exec 1  Exec 2  Lecture buffer• Files d’exécutions attachées à un device 10
    11. 11. Exemple de boucle CCalcul de sinus & cosinus en boucle void scos(    const double* params,    double* out,    int length){    for (int i = 0; i < length; i++)    {        double param = params[i];        out[2 * i] = sin(param);        out[2 * i + 1] = cos(param);    }} 11
    12. 12. Kernel OpenCL équivalentCalcul de sinus & cosinus parallèle (simplifié)#pragma OpenCL EXTENSION cl_khr_fp64 : enablekernel void scos(    global const double* params,    global double2* out,    int length){    int i = get_global_id(0); // indice parallèle    if (i >= length) return; // dépassements possibles    {        double param = params[i];        double c, s = sincos(param, &c); // calcul sin & cos rapide        out[i] = (double2)(s, c);    }} 12
    13. 13. Kernels• Dialecte du C• Fonctions math vectorielles (SIMD sur float4, int8…)• Code connait sa position d’exécution• Lecture / écriture buffers / images• Synchronisation avec voisins (working group) 13
    14. 14. Code hôte• Choix devices + création contexte• Création file d’exécution• Compilation kernels• Allocation buffers / images• Scheduling des exécutions• Lecture / écriture buffers 14
    15. 15. JavaCL (BSD)• 1ers bindings OpenCL• API orientée-objet• Maven Central (jar unique)• Communauté active 15
    16. 16. DémoHello World 16
    17. 17. Sous le capot…OpenCL bindings générés par JNAerator :BridJ fournit la “colle” à l’exécution 17
    18. 18. Plus que des bindings• Gestion mémoire débrayable• Cache automatique des binaires• Inclusion depuis URLs• Générateur de wrappers typés• Parallel Reduction (min, max, sum, product) 18
    19. 19. Plus que des bindings (2)• FFT complexe dun tableau de primitives : DoubleFFTPow2 fft = new DoubleFFTPow2(); double[] transformed = fft.transform(data);• Chargement (+ conversion) dune image : CLImage2D img = context.createImage2D(Usage.Input, ImageIO.read(file));• Multiplication de matrices UJMP : Matrix a = new CLDenseFloatMatrix2D(n, m), Matrix b = new CLDenseFloatMatrix2D(m, q); Matrix c = a.times(b); 19
    20. 20. DémoInteractive Image Editor 20
    21. 21. Les pièges d’OpenCL• Coût des transferts RAM / GPU• Contextes mixtes CPU / GPU : seulement sur APU AMD• Branchements conditionnels lents sur GPU• Besoin de tests d’arrêt 21
    22. 22. Encore des pièges• Peu de formats d’image garantis• Endianness parfois différente entre hôte & device• Apple ne supporte qu’OpenCL 1.0 22
    23. 23. Chasse à la performance sur GPU• Groupes d’exécution : memoire locale, barrières…• Répartition de charge multi-GPU• Exécutions pyramidales pour réductions associatives 23
    24. 24. JavaCL 1.0.0-RC2• 32 & 64 bits / Windows, Linux, MacOS X• NVIDIA, AMD, Intel & Apple• OpenCL 1.0 / 1.1 (1.2 peu répandu) 24
    25. 25. Rendre OpenCL plus facile ?• Éviter d’écrire les kernels en C• Cacher l’asynchronicité• Accepter performance sub-optimale (> 10x plus rapide que Java) 25
    26. 26. OpenCL “traduit”• Apapapi (AMD) • Java • Cache beaucoup (trop ?) de détails• ScalaCL (votre serviteur) 26
    27. 27. ScalaCL• Collections Scala asynchrones• Stockage + Opérations OpenCL (map, filter, sum…)• Traduction Scala / OpenCL 27
    28. 28. Quest-ce que Scala ?• Saint-Graal des langages (impératif, fonctionnel, objet…)• Tuples + Collections mutables / immutables• Syntaxe familière• JVM 28
    29. 29. Quelques lignes de Scala…val intervalle = (0 until 100000)val cosSinCol = intervalle.map(i => { val f = 0.2 (cos(i * f), sin(i * f))})val sum = cosSinCol.map({ case (c, s) => c * s }).sum 29
    30. 30. Les mêmes lignes sur GPUimport scalacl._implicit val context = Context.best(DoubleSupport)val intervalle = (0 until 100000).clval cosSinCol = intervalle.map(i => { val f = 0.2 (cos(i * f), sin(i * f))})val sum = cosSinCol.map({ case (c, s) => c * s }).sum 30
    31. 31. DémoConsole GPU 31
    32. 32. Comment ça marche• Conversion fonctions Scala => code OpenCL• 3 collections • CLRange : intervalle • CLArray : tableau de tuploïdes • CLFilteredArray : tableau filtré, compactable en parallèle 32
    33. 33. Des collections asynchrones• Contrainte : les collections Scala ont l’air synchrones• Principe : single writer, multiple reader 33
    34. 34. Des collections asynchronesval tab = Array(1, 2, 3, 4).clval resultat = tab.map(x => x * 2).map(x – 5)// resultat pas encore calculéresultat(i) = 10 // attente avant écriture 34
    35. 35. Traduire Scala en OpenCL ? 35
    36. 36. Traduire Scala en OpenCL ?• Limitations (pas de classes)• Élimination des tuples• Élimination du code “objet” 36
    37. 37. Ceci est du code objetfor (i <- 0 until 100){ …} 37
    38. 38. Une lambda peut en cacher une autre(0 until 100).foreach(i =>{ …}) 38
    39. 39. Des boucles 5x plus rapides(0 until 100).foreach(i => var i = 0{ while (i < 100) { … …}) } 39
    40. 40. Aller plus vite : flot d’opérationscol.map(f).filter(g).map(h).reduceLeft(i)•Collections intermédiaires + lambdas•f, g, h sans effet de bord ? 40
    41. 41. ScalaCL : encore un prototype• Scalaxy : spin-off généraliste plutôt stable• Fonctionalités expérimentales : captures, random, reduce• Macros Scala 2.10 41
    42. 42. OpenCL• Votre GPU se tourne les pouces• Write once, run (almost) everywhere http://javacl.googlecode.com http://scalacl.googlecode.com 42
    43. 43. Questions 43
    44. 44. 44
    45. 45. CLFilteredArray[T]• Tableau de valeurs + Bitmap de présence• Compactage : • Somme préfixée • Recopie aux incréments• Exemple : filtrage pairs f: x => ((x % 2) == 0) 45
    46. 46. JavaCL : générateur de codeLinearAlgebraKernels.cl :LinearAlgebraKernels.java : 46
    47. 47. JavaCL : hôte minimaliste• Choix dun contexte + file dexécution : CLContext context = JavaCL.createBestContext(DoubleSupport); CLQueue queue = context.createDefaultQueue();• Création de tableaux : DoubleBuffer values = ...; CLDoubleBuffer params = context.createDoubleBuffer(Usage.Input, values);• Compilation=du programme : CLKernel scos context.createProgram(src).createKernel("scos");• Exécution parallèle : length); scos.setArgs(params, out, CLEvent evt = scos.enqueueNDRange(queue, new int[] { length }); 47

    ×