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 334 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

  • Soyez le premier à commenter

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

×