Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Les deux révisions précédentes Révision précédente
Prochaine révision
Révision précédente
Prochaine révision Les deux révisions suivantes
formation:gpu4cbp [2023/03/10 11:04]
equemene [Un intermède CUDA et son implémentation PyCUDA]
formation:gpu4cbp [2023/03/13 08:54]
equemene [Implémenter une fonction coûteuse, la Transformée de Fourier]
Ligne 741: Ligne 741:
   - la OpenACC reste supérieure aux implémentations OpenCL/CPU et OpenCL/GPU d'un facteur 3   - la OpenACC reste supérieure aux implémentations OpenCL/CPU et OpenCL/GPU d'un facteur 3
  
-Se contenter uniquement de ce test inviterait à fuire Python/​OpenCL. Cependant, nous avons vu dans sur ''​MySteps_2.py''​ que la charge calculatoire doit être "vraiement" significative pour que le Python/​OpenCL l'​emporte de manière significative. Nous reviendrons donc dans la suite sur des versions modifiées de ces programmes C intégrant la fonction de Mylq ''​MySillyFunction'',​ appelée plusieurs fois, pour juger si "​vraiment" ​Python/​OpenCL reste compétitif face à OpenMP et OpenACC.+Se contenter uniquement de ce test inviterait à fuire Python/​OpenCL. Cependant, nous avons vu dans sur ''​MySteps_2.py''​ que la charge calculatoire doit être "vraiment" significative pour que le Python/​OpenCL l'​emporte de manière significative. Nous reviendrons donc dans la suite sur des versions modifiées de ces programmes C intégrant la fonction de Mylq ''​MySillyFunction'',​ appelée plusieurs fois, pour juger si Python/​OpenCL reste compétitif face à OpenMP et OpenACC.
 ===== Un intermède CUDA et son implémentation PyCUDA ===== ===== Un intermède CUDA et son implémentation PyCUDA =====
  
Ligne 866: Ligne 866:
 ===== Comparaison de toutes les implémentations : victoire incontestée de OpenCL ===== ===== Comparaison de toutes les implémentations : victoire incontestée de OpenCL =====
  
-Nous indiquions dans la "​timide"​ implémentation de ''​MySteps_1''​ en C, OpenMP/C et OpenACC/C que nous allions revenir pour comparer finalement les 6 implémentations d'un même algorithme "​empâté"​ d'​addition de vecteurs, histoire de véritablement fixer les idées. De manière à juger l'​influence de la charge, nous avons intégré le recours successifs à la //fonction de Mylq// ''​MySillyFunction''​ pour moduler la "​charge calculatoire'' ​de chaque élément de vecteur de l'​addition.+Nous indiquions dans la "​timide"​ implémentation de ''​MySteps_1''​ en C, OpenMP/C et OpenACC/C que nous allions revenir pour comparer finalement les 6 implémentations d'un même algorithme "​empâté"​ d'​addition de vecteurs, histoire de véritablement fixer les idées. De manière à juger l'​influence de la charge, nous avons intégré le recours successifs à la //fonction de Mylq// ''​MySillyFunction''​ pour moduler la "​charge calculatoire" ​de chaque élément de vecteur de l'​addition.
  
 Ces 6 implémentations sont réparties dans 4 programmes : Ces 6 implémentations sont réparties dans 4 programmes :
Ligne 872: Ligne 872:
   * ''​MySteps_6_openmp.c''​ : implémentation OpenMP en C   * ''​MySteps_6_openmp.c''​ : implémentation OpenMP en C
   * ''​MySteps_6_openacc.c''​ : implémentation OpenACC en C   * ''​MySteps_6_openacc.c''​ : implémentation OpenACC en C
-  * ''​MySteps_6.py''​ : implémentations Numpy native, OpenCL et CUDA en Python+  * ''​MySteps_6.py''​ : implémentations Numpy, OpenCL et CUDA en Python
  
 Les trois implémentations en C ont comme argument la taille et le nombre d'​applications de la fonction de Mylq. Les trois implémentations en C ont comme argument la taille et le nombre d'​applications de la fonction de Mylq.
Ligne 885: Ligne 885:
   * ''​-n''​ la suspension de l'​exécution en "mode natif"   * ''​-n''​ la suspension de l'​exécution en "mode natif"
  
-Ainsi, pour une exécution sur une machine disposant d'un GPU avec 6GB de RAM, l'​exécution de ''​./​MySteps_6.py -d 0 -g OpenCL -s $((2**25)) -c 10'' ​donne :<​code>​+Ainsi, pour une exécution sur une machine disposant d'un GPU avec 6GB de RAM, l'​exécution de <​code>​./​MySteps_6.py -d 0 -g OpenCL -s $((2**25)) -c 10</​code> ​donne :<​code>​
 Device Selection : 0 Device Selection : 0
 GpuStyle used : OpenCL GpuStyle used : OpenCL
Ligne 914: Ligne 914:
 </​code>​ </​code>​
  
-Ainsi, dans cette machine, l'​exécution Python/​OpenCL est 271 fois plus rapide sur le GPU qu'en Python natif. Si nous désirons "​rester"​ sur le CPU, les résultats sont aussi éloquents. L'​exécution de ''​./​MySteps_6.py -d 4 -g OpenCL -s $((2**25)) -c 10'' ​sur son processeur ​assez ancien ​ne disposant que de 6 coeurs : <​code> ​+Ainsi, dans cette machine, l'​exécution Python/​OpenCL est 271 fois plus rapide sur le GPU qu'en Python natif. Si nous désirons "​rester"​ sur le CPU, les résultats sont aussi éloquents. ​ 
 + 
 +La version CUDA appelée par <​code>​./​MySteps_6.py -d 0 -g CUDA -s $((2**25)) -c 10 -t 32</​code>​ donne :<​code>​ 
 +Device Selection : 0 
 +GpuStyle used : CUDA 
 +Size of complex vector : 33554432 
 +Number of silly calls : 10 
 +Number of Threads : 32 
 +Serial compute : 1 
 +Device #0 of type GPU : NVIDIA GeForce GTX TITAN 
 +Device #1 of type GPU : Quadro K420 
 +NativeRate: 130602 
 +Definition of kernel : 1.646 
 +Synthesis of kernel : 0.000 
 +Allocation on Host for results : 0.041 
 +Execution of kernel : 0.869 
 +Execution of kernel : 0.869 
 +CUDARate: 12511868 
 +[ 0.0000000e+00 ​ 3.5762787e-07 -5.9604645e-07 ...  2.9802322e-06 
 +  5.9604645e-07 ​ 7.1525574e-07] 
 +0.016533133 
 +Results between Native & CUDA seem to be too different! 
 +CUDAvsNative ratio: 95.801504</​code>​ 
 + 
 +L'​exécution de de <​code>​./​MySteps_6.py -d 4 -g OpenCL -s $((2**25)) -c 10</​code> ​sur sa CPU assez ancienne ​ne disposant que de 6 coeurs ​ ​donne ​: <​code> ​
 Device Selection : 4 Device Selection : 4
 GpuStyle used : OpenCL GpuStyle used : OpenCL
Ligne 945: Ligne 969:
 </​code>​ </​code>​
  
-Pour une exécution en C, sérielle, OpenMP et OpenACC, nous avons :+Pour une exécution en OpenMP et OpenACC avec les mêmes argumentsnous avons : 
 +  * en OpenMP : <​code>​ 
 +33554432 10 
 +Norm: 0.00000000e+00 
 +Elapsed Time: 418.251 
 +OMP Elapsed Time: 47.402 
 +NativeRate: 80225 
 +OMPRate: 707876 
 +AccRatio: 8.824</​code>​ 
 +  * en OpenACC : <​code>​ 
 +33554432 10 
 +Norm: 0.00000000e+00 
 +Elapsed Time: 416.558 
 +OpenACC Elapsed Time: 12.511 
 +NativeRate: 80551 
 +OpenACCRate:​ 2681906 
 +ACCRatio: 33.294 
 +</​code>​ 
 + 
 +Ainsi, pour une charge calculatoire de 321 fonctions arithmétiques sur chaque élément du tableau (10x la fonction de Mylq sur chaque élément et l'​addition finale) : 
 +  * Python/​Numpy est presque 2x plus rapide que la C sérielle 
 +  * Python/​OpenCL/​GPU est 8x plus rapide que la C/OpenACC271x plus rapide que la Python Numpy 
 +  * Python/​CUDA/​GPU est 3x plus rapide que la C/OpenACC, 10x plus rapide que la Python Numpy 
 +  * Python/​OpenCL/​CPU est presque 3x plus rapide que la C/OpenMP, 23x plus rapide que le Python Numpy 
 + 
 +Si nous augmentons la charge individuelle de chaque addition, nous augmentons de manière très significative l'​accéleration de calcul via OpenCL. Pour la configuration matérielle ci-dessus, l'​accélération frise avec les 1000 pour le GPU et les 23 pour le CPU, en sollicitant 1000 fois la fonction de Mylq (soit 32001 opérations pour chaque somme). 
 + 
 +Le tableau suivant montre, pour les 7 implémentations,​ le gain face à l'​implémentation native Python : 
 +^  Silly Calls  ^  C/​Serial ​ ^  C/​OpenMP ​ ^  C/OpenACC ​ ​^ ​ PyCL CPU  ^  PyCL GPU  ^  PyCUDA 32T  ^ 
 +^  0|  3.89|  9.39|  1.45|  0.82|  0.60|  0.57| 
 +^  1|  0.40|  4.82|  12.95| ​ 15.40| ​ 56.43| ​ 46.08| 
 +^  10|  0.46|  4.69|  15.96| ​ 20.30| ​ 393.72| ​ 190.89| 
 +^  100|  0.54|  5.18|  17.97| ​ 23.24| ​ 904.59| ​ 294.82| 
 +^  1000|  0.52|  5.10|  17.30| ​ 22.40| ​ 967.09| ​ 290.60| 
 + 
 +Pour obtenir de tels gains, nous avons la conjonction des 2 facteurs : l'​exploitation massive de tous les //​cudacores//​ (les ALU de la GPU) et l'​utilisation de toute la bande passante mémoire de la GPU (10x supérieure généralement à la bande passante mémoire). 
 + 
 +L'​objectif de cette partie TP est donc de retrouver, par soi-même, les facteurs d'​accélération de OpenCL pour des charges croissantes par l'​application de 0, 1, 10, 100, 1000 fois la fonction de Mylq. 
 + 
 +La difficulté viendra du temps d'​exécution de la version séquentielle qui atteint son pallier de performances pour une taille croissante de vecteurs assez rapidement et du choix judicieux de l'​inhibition de l'​exécution séquentielle pour permettre d'​atteindre des tailles de vecteurs significatives. 
 + 
 +<note warning>​**Exercice #4.1 : exécution des différentes implémentations** 
 +  - Compilez ''​MySteps_6.c''​ en ''​MySteps_6''​ 
 +  - Compilez ''​MySteps_6_openmp.c''​ en ''​MySteps_6_openmp''​  
 +  - Compilez ''​MySteps_6_openacc.c''​ en ''​MySteps_6_openacc''​ 
 +  - Compilez ''​MySteps_6_openmp.c''​ en ''​MySteps_6_openmp_NoSerial'',​ sans exécution séquentielle  
 +  - Compilez ''​MySteps_6_openacc.c''​ en ''​MySteps_6_openacc_NoSerial'',​ sans exécution séquentielle 
 +  - Exécutez ''​MySteps_6''​ sur des tailles de 32768 et 65536, pour les différentes charges ci-dessus 
 +  - Relevez les **NativeRate** pour les différentes charges : que constatez vous ? 
 +  - Exécutez ''​MySteps_6_openmp''​ sur des tailles de 32768 et 65536, pour les mêmes charges 
 +  - Relevez les **NativeRate**,​ **OMPRate** pour les différentes charges 
 +  - Exécutez ''​MySteps_6_openacc''​ sur des tailles de 32768 et 65536, pour les mêmes charges 
 +  - Relevez les **NativeRate**,​ **ACCRate** pour les différentes charges 
 +  - Exécutez ''​MySteps_6.py''​ en OpenCL/CPU avec l'​Intel sur des tailles de 32768 et 65536 
 +  - Relevez les **NativeRate**,​ **OpenCLRate** pour les différentes charges 
 +  - Exécutez ''​MySteps_6.py''​ en OpenCL/GPU sur le plus gros GPU sur des tailles de 32768 et 65536 
 +  - Relevez les **NativeRate**,​ **OpenCLRate** pour les différentes charges 
 +  - Exécutez ''​MySteps_6.py''​ en CUDA/GPU sur le plus gros GPU sur des tailles de 32768 et 65536 
 +  - Relevez les **NativeRate**,​ **CUDARate** pour les différentes charges  
 +  - Placez dans un tableau les différentes valeurs : que constatez-vous ? 
 +  - Exécutez ''​MySteps_6_openmp_NoSerial''​ sur des tailles de 1048576 et 2097152, pour les mêmes charges 
 +  - Relevez **OMPRate** pour les différentes charges 
 +  - Exécutez ''​MySteps_6_openacc_NoSerial''​ sur des tailles de 1048576 et 2097152, pour les mêmes charges 
 +  - Relevez **ACCRate** pour les différentes charges 
 +  - Exécutez ''​MySteps_6.py''​ en OpenCL Intel sur des tailles de 1048576 et 2097152, avec l'​option ''​-n''​ 
 +  - Relevez **OpenCLRate** pour les différentes charges 
 +  - Exécutez ''​MySteps_6.py''​ en OpenCL sur le même GPU sur des tailles de 1048576 et 2097152 
 +  - Relevez **OpenCLRate** pour les différentes charges 
 +  - Exécutez ''​MySteps_6.py''​ en CUDA/GPU sur le même GPU sur des tailles de 1048576 et 2097152 
 +  - Relevez **CUDARate** pour les différentes charges  
 +  - Placez dans un tableau les différentes valeurs : que constatez-vous comme gain en performance en OpenCL ? 
 +</​note>​ 
 + 
 +Vous pouvez juger, du fait de lancement pour des tailles doublées, et pour les différentes charges, que vous avez obtenu des optimums pour certaines implémentations,​ mais pas encore pour d'​autres,​ notamment OpenCL et CUDA.  
 + 
 +Si la performance pour une taille de 2097152 est moins de 5% supérieure à la performance pour une taille de 1048576, vous pouvez considérer que vous avez atteint le quasi-optimum de performance. L'​objectif est d'​atteindre cette limite. 
 + 
 +<note warning>​**Exercice #4.2 : exploration de la meilleure performance OpenCL et CUDA** 
 +  - Exécutez ''​MySteps_6.py''​ en OpenCL Intel sur des tailles croissantes avec l'​option ''​-n''​ 
 +  - Relevez **OpenCLRate** pour les différentes charges 
 +  - Exécutez ''​MySteps_6.py''​ en OpenCL sur le même GPU sur des tailles de 1048576 et 2097152 
 +  - Relevez **OpenCLRate** pour les différentes charges 
 +  - Exécutez ''​MySteps_6.py''​ en CUDA/GPU sur le même GPU sur des tailles de 1048576 et 2097152 
 +  - Relevez **CUDARate** pour les différentes charges  
 +  - Placez dans un tableau les différentes valeurs ​que constatez-vous comme gain en performance en OpenCL ? 
 +</​note>​ 
  
 ===== Implémenter une fonction "​coûteuse",​ la Transformée de Fourier ===== ===== Implémenter une fonction "​coûteuse",​ la Transformée de Fourier =====
Ligne 981: Ligne 1091:
 Il sera alors possible d'​estimer l'​erreur numérique à ce calcul. Il sera alors possible d'​estimer l'​erreur numérique à ce calcul.
  
-<note warning>​**Exercice #4.1 : implémentation Python "​naïve"​**+<note warning>​**Exercice #5.1 : implémentation Python "​naïve"​**
   - Modifiez ''​MyDFT_1.py''​ suivant les 7 spécifications ci-dessus   - Modifiez ''​MyDFT_1.py''​ suivant les 7 spécifications ci-dessus
   - Exécutez le programme pour une taille de **16** et contrôler la cohérence   - Exécutez le programme pour une taille de **16** et contrôler la cohérence
Ligne 1014: Ligne 1124:
   - comparer les résultats entre les deux avec ''​linalg.norm''​   - comparer les résultats entre les deux avec ''​linalg.norm''​
  
-<note warning>​**Exercice #4.2 : implémentation Python Numpy**+<note warning>​**Exercice #5.2 : implémentation Python Numpy**
   - Copiez le programme ''​MyDFT_1.py''​ en ''​MyDFT_2.py''​   - Copiez le programme ''​MyDFT_1.py''​ en ''​MyDFT_2.py''​
   - Modifiez ''​MyDFT_2.py''​ suivant les 7 spécifications ci-dessus   - Modifiez ''​MyDFT_2.py''​ suivant les 7 spécifications ci-dessus
Ligne 1041: Ligne 1151:
   - changer le domaine d'​itération pour la boucle : ''​range()''​ par ''​numba.prange()''​   - changer le domaine d'​itération pour la boucle : ''​range()''​ par ''​numba.prange()''​
  
-<note warning>​**Exercice #4.3 : implémentation Python Numpy**+<note warning>​**Exercice #5.3 : implémentation Python Numpy**
   - Copiez le programme ''​MyDFT_2.py''​ en ''​MyDFT_3.py''​ et exploitez ce dernier   - Copiez le programme ''​MyDFT_2.py''​ en ''​MyDFT_3.py''​ et exploitez ce dernier
   - Copiez la fonction ''​NumpyDFT''​ en ''​NumbaDFT''​   - Copiez la fonction ''​NumpyDFT''​ en ''​NumbaDFT''​
Ligne 1071: Ligne 1181:
 Pour l'​implémentation OpenCL, la version "​naïve"​ de l'​implémentation va servir. Pour cela, il suffit de reprendre la définition de la méthode naïve et de l'​implémenter en C dans un noyau OpenCL. A noter que Pi n'​étant dans une variable définie, il faut explicitement la détailler dans le noyau OpenCL. Autre détail important : le //cast//. De manière a éviter tout effet de bord, il est fortement recommandé de //caster// les opérations dans la précision flottante souhaitée pour des opérations sur des indices entiers. Pour l'​implémentation OpenCL, la version "​naïve"​ de l'​implémentation va servir. Pour cela, il suffit de reprendre la définition de la méthode naïve et de l'​implémenter en C dans un noyau OpenCL. A noter que Pi n'​étant dans une variable définie, il faut explicitement la détailler dans le noyau OpenCL. Autre détail important : le //cast//. De manière a éviter tout effet de bord, il est fortement recommandé de //caster// les opérations dans la précision flottante souhaitée pour des opérations sur des indices entiers.
  
-<note warning>​**Exercice #4.4 : implémentation Python OpenCL**+<note warning>​**Exercice #5.4 : implémentation Python OpenCL**
   - Copiez le programme ''​MyDFT_3.py''​ en ''​MyDFT_4.py''​ et exploitez ce dernier   - Copiez le programme ''​MyDFT_3.py''​ en ''​MyDFT_4.py''​ et exploitez ce dernier
   - Copiez la fonction python ''​OpenCLAddition''​ en ''​OpenCLDFT''​   - Copiez la fonction python ''​OpenCLAddition''​ en ''​OpenCLDFT''​
Ligne 1146: Ligne 1256:
   * modifier les vecteurs en sortie (2 vecteurs)   * modifier les vecteurs en sortie (2 vecteurs)
  
-<note warning>​**Exercice #4.5 : implémentation Python CUDA**+<note warning>​**Exercice #5.5 : implémentation Python CUDA**
   - Copiez le programme ''​MyDFT_4.py''​ en ''​MyDFT_5.py''​ et exploitez ce dernier   - Copiez le programme ''​MyDFT_4.py''​ en ''​MyDFT_5.py''​ et exploitez ce dernier
   - Copiez la fonction python ''​CUDAAddition''​ en ''​CUDADFT''​   - Copiez la fonction python ''​CUDAAddition''​ en ''​CUDADFT''​
formation/gpu4cbp.txt · Dernière modification: 2024/02/22 11:47 par equemene