Bureaucrats, cc_docs_admin, cc_staff, rsnt_translations
2,837
edits
(Updating to match new version of source page) |
No edit summary |
||
(31 intermediate revisions by one other user not shown) | |||
Line 9: | Line 9: | ||
* savoir concentrer vos efforts et réécrire les routines qui exigent beaucoup de temps | * savoir concentrer vos efforts et réécrire les routines qui exigent beaucoup de temps | ||
}} | }} | ||
== Profiler du code == | == Profiler du code == | ||
Pourquoi auriez-vous besoin de profiler du code? Parce que c'est la seule façon de comprendre | Pourquoi auriez-vous besoin de profiler du code? Parce que c'est la seule façon de comprendre | ||
* comment le temps est employé aux points critiques (''hotspots''), | * comment le temps est employé aux points critiques (''hotspots''), | ||
* comprendre la performance du code, | * comprendre la performance du code, | ||
* savoir comment mieux employer votre temps. | * savoir comment mieux employer votre temps de développement. | ||
Pourquoi est-ce important de connaitre les points critiques dans le code? | Pourquoi est-ce important de connaitre les points critiques dans le code? | ||
D'après la loi d'Amdahl, paralléliser les routines qui exigent le plus de temps d'exécution (les points critiques) produit le plus d'impact. | D'après [https://en.wikipedia.org/wiki/Amdahl%27s_law la loi d'Amdahl], paralléliser les routines qui exigent le plus de temps d'exécution (les points critiques) produit le plus d'impact. | ||
== Préparer le code pour l'exercice == | == Préparer le code pour l'exercice == | ||
Pour | Pour l'exemple suivant, nous utilisons du code provenant de [https://github.com/calculquebec/cq-formation-ce dépôt de données Git]. | ||
[https://github.com/calculquebec/cq-formation-openacc/archive/refs/heads/main.zip Téléchargez et faites l'extraction du paquet] et positionnez-vous dans le répertoire <code>cpp</code> ou <code>f90</code>. Le but de cet exemple est de compiler et lier le code pour obtenir un exécutable pour en profiler le code source avec un profileur. | |||
{{Callout | {{Callout | ||
|title=Choix du compilateur | |title=Choix du compilateur | ||
|content= | |content= | ||
Mis de l'avant par [https://www.cray.com/ Cray] et par [https://www.nvidia.com NVIDIA] via sa division | |||
[https://www.pgroup.com/support/release_archive.php Portland Group] jusqu'en 2020 puis via [https://developer.nvidia.com/hpc-sdk sa trousse HPC SDK], ceux deux types de compilateurs offrent le support le plus avancé pour OpenACC. | |||
Quant aux [https://gcc.gnu.org/wiki/OpenACC compilateurs GNU], le support pour OpenACC 2.x continue de s'améliorer depuis la version 6 de GCC. | |||
En date de juillet 2022, les versions GCC 10, 11 et 12 supportent la version 2.6 d'OpenACC. | |||
Dans ce tutoriel, nous utilisons la version 22.7 de [https://developer.nvidia.com/nvidia-hpc-sdk-releases NVIDIA HPC SDK]. Notez que les compilateurs NVIDIA sont gratuits à des fins de recherche universitaire. | |||
Dans ce tutoriel, nous utilisons la version | |||
}} | }} | ||
Line 59: | Line 51: | ||
}} | }} | ||
Une fois l'exécutable <code>cg.x</code> créé, nous allons profiler son code source. Le profileur mesure les appels des fonctions en exécutant et en surveillant ce programme. | |||
Une fois l'exécutable créé, nous allons profiler | '''Important :''' Cet exécutable utilise environ 3Go de mémoire et un cœur CPU presque à 100 %. '''L'environnement de test devrait donc avoir 4Go de mémoire disponible et au moins deux (2) cœurs CPU'''. | ||
{{Callout | {{Callout | ||
|title=Choix du profileur | |title=Choix du profileur | ||
|content= | |content= | ||
Dans ce tutoriel, nous utilisons deux profileurs : | |||
Dans ce tutoriel, nous utilisons | |||
* | * '''[https://docs.nvidia.com/cuda/profiler-users-guide/ <code>nvprof</code> de NVIDIA]''' , un profileur en ligne de commande capable d'analyser des codes non GPU | ||
* | * '''[[OpenACC_Tutorial_-_Adding_directives#NVIDIA_Visual_Profiler|<code>nvvp</code> (NVIDIA Visual Profiler) ]]''', un outil d'analyse multiplateforme pour des programmes écrits avec OpenACC et CUDA C/C++. | ||
Puisque <code>cg.x</code> que nous avons construit n'utilise pas encore le GPU, nous allons commencer l'analyse avec le profileur <code>nvprof</code>. | |||
</ | |||
}} | }} | ||
=== Profileur en ligne de commande NVIDIA <code>nvprof</code> === | |||
Dans sa trousse de développement pour le calcul de haute performance, NVIDIA fournit habituellement <code>nvprof</code>, mais la version qu'il faut utiliser sur nos grappes est incluse dans un module CUDA. | |||
< | |||
< | |||
< | |||
< | |||
{{Command | {{Command | ||
|module load cuda/11.7 | |module load cuda/11.7 | ||
}} | }} | ||
Pour profiler un exécutable CPU pur, nous devons ajouter les arguments <code>--cpu-profiling on</code> à la ligne de commande. | |||
{{Command | {{Command | ||
|nvprof --cpu-profiling on ./cg.x | |nvprof --cpu-profiling on ./cg.x | ||
Line 109: | Line 92: | ||
======== Data collected at 100Hz frequency | ======== Data collected at 100Hz frequency | ||
}} | }} | ||
Dans le résultat, la fonction <code>matvec()</code> utilise 83.5 % du temps d'exécution; son appel se trouve dans la fonction <code>main()</code>. | |||
==Renseignements sur le compilateur== | ==Renseignements sur le compilateur== | ||
Avant de travailler sur la routine, nous devons comprendre ce que fait le compilateur; posons-nous les questions suivantes : | Avant de travailler sur la routine, nous devons comprendre ce que fait le compilateur; posons-nous les questions suivantes : | ||
* Quelles sont les optimisations qui ont été appliquées? | * Quelles sont les optimisations qui ont été automatiquement appliquées par le compilateur? | ||
* Qu'est-ce qui a empêché d'optimiser davantage? | * Qu'est-ce qui a empêché d'optimiser davantage? | ||
* La performance serait-elle affectée par les petites modifications? | * La performance serait-elle affectée par les petites modifications? | ||
Le compilateur NVIDIA offre l'indicateur <code>-Minfo</code> avec les options suivantes : | |||
Le compilateur | * <code>all</code>, pour imprimer presque tous les types d'information, incluant | ||
* accel | ** <code>accel</code> pour les opérations du compilateur en rapport avec l'accélérateur | ||
* | ** <code>inline</code> pour l'information sur les fonctions extraites et alignées | ||
* | ** <code>loop,mp,par,stdpar,vect</code> pour les renseignements sur l'optimisation et la vectorisation des boucles | ||
* | * <code>intensity</code>, pour imprimer l'information sur l'intensité des boucles | ||
</ | * (aucune option) produit le même résultat que l'option <code>all</code>, mais sans l'information fournie par <code>inline</code>. | ||
== Obtenir les renseignements sur le compilateur == | == Obtenir les renseignements sur le compilateur == | ||
* | * Modifiez le Makefile. | ||
CXX=nvc++ | |||
CXXFLAGS=-fast -Minfo=all,intensity | |||
LDFLAGS=${CXXFLAGS} | |||
* Effectuez un nouveau build. | * Effectuez un nouveau build. | ||
{{Command | {{Command | ||
|make clean; make | |make clean; make | ||
Line 262: | Line 240: | ||
}} | }} | ||
== Interpréter le résultat == | |||
== | L'''intensité computationnelle'' d'une boucle représente la quantité de travail accompli par la boucle en fonction des opérations effectuées en mémoire, soit | ||
L'intensité computationnelle d'une boucle représente la quantité de travail accompli par la boucle en fonction des opérations effectuées en mémoire | |||
< | <math>\mbox{intensité computationnelle} = \frac{\mbox{opérations de calcul}}{\mbox{opérations en mémoire}}</math> | ||
</ | |||
< | Dans le résultat, une valeur supérieure à 1 pour <code>Intensity</code> indique que la boucle serait bien exécutée sur un processeur graphique (GPU). | ||
== Comprendre le code == | == Comprendre le code == | ||
Regardons attentivement | Regardons attentivement la boucle principale de | ||
[https://github.com/calculquebec/cq-formation-openacc/blob/main/cpp/matrix_functions.h#L29 la fonction <code>matvec()</code> implémentée dans <code>matrix_functions.h</code>]: | |||
<syntaxhighlight lang="cpp" line start="29" highlight="1,5,10,12"> | <syntaxhighlight lang="cpp" line start="29" highlight="1,5,10,12"> | ||
for(int i=0;i<num_rows;i++) { | for(int i=0;i<num_rows;i++) { | ||
Line 293: | Line 264: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
On trouvera les dépendances de données en se posant les questions suivantes : | On trouvera les dépendances de données en se posant les questions suivantes : | ||
* Une itération en affecte-t-elle d'autres? | * Une itération en affecte-t-elle d'autres? | ||
* | ** par exemple, quand une '''[https://fr.wikipedia.org/wiki/Suite_de_Fibonacci suite de Fibonacci]''' est générée, chaque nouvelle valeur dépend des deux valeurs qui la précèdent. Il est donc très difficile, sinon impossible, d'implémenter un parallélisme efficace. | ||
* | * L'accumulation des valeurs dans <code>sum</code> est-elle une dépendance? | ||
** Non, c'est une''' [https://en.wikipedia.org/wiki/Reduction_operator réduction]'''! Et les compilateurs modernes optimisent bien ce genre de réduction. | |||
* Est-ce que les itérations de boucle écrivent et lisent dans les mêmes vecteurs de sorte que les valeurs sont utilisées ou écrasées par d'autres itérations? | |||
** Heureusement, ceci ne se produit pas dans le code ci-dessus. | |||
Maintenant que le code est analysé, nous pouvons ajouter des directives au compilateur. | Maintenant que le code est analysé, nous pouvons ajouter des directives au compilateur. | ||
[[OpenACC Tutorial - Introduction/fr|<- Page précédente, ''Introduction'']] | [[OpenACC Tutorial/fr|^- Retour au début du tutoriel]] | [[OpenACC Tutorial - Adding directives/fr|Page suivante, ''Ajouter des directives'' ->]] | [[OpenACC Tutorial - Introduction/fr|<- Page précédente, ''Introduction'']] | [[OpenACC Tutorial/fr|^- Retour au début du tutoriel]] | [[OpenACC Tutorial - Adding directives/fr|Page suivante, ''Ajouter des directives'' ->]] |