OpenACC Tutorial - Profiling/fr: Difference between revisions
No edit summary |
(Updating to match new version of source page) |
||
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 | ||
}} | }} | ||
<div class="mw-translate-fuzzy"> | |||
== 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 | ||
Line 14: | Line 15: | ||
* comprendre la performance du code, | * comprendre la performance du code, | ||
* savoir comment mieux employer votre temps. | * savoir comment mieux employer votre temps. | ||
</div> | |||
<div class="mw-translate-fuzzy"> | |||
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 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. | ||
</div> | |||
<div class="mw-translate-fuzzy"> | |||
== Préparer le code pour l'exercice == | == Préparer le code pour l'exercice == | ||
Pour notre exemple, nous utilisons du code provenant de [https://github.com/calculquebec/cq-formation-openacc ces dépôts]. Téléchargez les fichiers et utilisez les répertoires ''cpp'' ou ''f90''. Le but de l'exercice est de compiler et lier le code et d'obtenir un exécutable que nous profilerons. | Pour notre exemple, nous utilisons du code provenant de [https://github.com/calculquebec/cq-formation-openacc ces dépôts]. Téléchargez les fichiers et utilisez les répertoires ''cpp'' ou ''f90''. Le but de l'exercice est de compiler et lier le code et d'obtenir un exécutable que nous profilerons. | ||
</div> | |||
{{Callout | {{Callout | ||
|title=Choix du compilateur | |title=Choix du compilateur | ||
|content= | |content= | ||
En date de mai 2016, relativement peu de compilateurs offraient les fonctionnalités d'OpenACC. Les plus avancés en ce sens sont les compilateurs du [http://www.pgroup.com/ Portland Group] de [http://www.nvidia.com/content/global/global.php NVidia] et ceux de [http://www.cray.com/ Cray]. Pour ce est qui de [https://gcc.gnu.org/wiki/OpenACC GNU], l'implémentation d'OpenACC dans la version 5 était expérimentale et devrait être complète dans la version 6. | <div class="mw-translate-fuzzy"> | ||
En date de mai 2016, relativement peu de compilateurs offraient les fonctionnalités d'OpenACC. Les plus avancés en ce sens sont les compilateurs du [http://www.pgroup.com/ Portland Group] de [http://www.nvidia.com/content/global/global.php NVidia] et ceux de [http://www.cray.com/ Cray]. Pour ce est qui de [https://gcc.gnu.org/wiki/OpenACC GNU], l'implémentation d'OpenACC dans la version 5 était expérimentale et devrait être complète dans la version 6. | |||
</div> | |||
As for the [https://gcc.gnu.org/wiki/OpenACC GNU compilers], since GCC version 6, the support for OpenACC 2.x kept improving. | |||
As of July 2022, GCC versions 10, 11 and 12 support OpenACC version 2.6. | |||
Dans ce tutoriel, nous utilisons la version 16.3 des [http://www.pgroup.com/support/download_pgi2016.php?view=current compilateurs du Portland Group] qui sont gratuits pour des fins de recherche universitaire. | <div class="mw-translate-fuzzy"> | ||
Dans ce tutoriel, nous utilisons la version 16.3 des [http://www.pgroup.com/support/download_pgi2016.php?view=current compilateurs du Portland Group] qui sont gratuits pour des fins de recherche universitaire. | |||
</div> | |||
}} | |||
{{Command | |||
|module load nvhpc/22.7 | |||
|result= | |||
Lmod is automatically replacing "intel/2020.1.217" with "nvhpc/22.7". | |||
The following have been reloaded with a version change: | |||
1) gcccore/.9.3.0 => gcccore/.11.3.0 3) openmpi/4.0.3 => openmpi/4.1.4 | |||
2) libfabric/1.10.1 => libfabric/1.15.1 4) ucx/1.8.0 => ucx/1.12.1 | |||
}} | }} | ||
Line 35: | Line 59: | ||
}} | }} | ||
<div class="mw-translate-fuzzy"> | |||
Une fois l'exécutable créé, nous allons profiler le code. | Une fois l'exécutable créé, nous allons profiler le code. | ||
</div> | |||
{{Callout | {{Callout | ||
|title=Choix du profileur | |title=Choix du profileur | ||
|content= | |content= | ||
<div class="mw-translate-fuzzy"> | |||
Dans ce tutoriel, nous utilisons plusieurs des profileurs suivants : | Dans ce tutoriel, nous utilisons plusieurs des profileurs suivants : | ||
* PGPROF : outil simple mais puissant pour l'analyse de programmes parallèles écrits avec OpenMP, OpenACC ou [https://fr.wikipedia.org/wiki/Compute_Unified_Device_Architecture CUDA]; rappelons que PGPROF est gratuit pour des fins de recherche universitaire. | * PGPROF : outil simple mais puissant pour l'analyse de programmes parallèles écrits avec OpenMP, OpenACC ou [https://fr.wikipedia.org/wiki/Compute_Unified_Device_Architecture CUDA]; rappelons que PGPROF est gratuit pour des fins de recherche universitaire. | ||
* NVVP (NVIDIA Visual Profiler) : outil d'analyse multiplateforme pour des programmes écrits avec OpenACC et CUDA C/C++. | * NVVP (NVIDIA Visual Profiler) : outil d'analyse multiplateforme pour des programmes écrits avec OpenACC et CUDA C/C++. | ||
* NVPROF : version ligne de commande du NVIDIA Visual Profiler. | * NVPROF : version ligne de commande du NVIDIA Visual Profiler. | ||
</div> | |||
}} | }} | ||
<div class="mw-translate-fuzzy"> | |||
< | |||
<br /> | <br /> | ||
<br /> | <br /> | ||
Line 77: | Line 82: | ||
=== NVPROF ligne de commande === | === NVPROF ligne de commande === | ||
La version ligne de commande de NVPROF est semblable à GPU prof. | La version ligne de commande de NVPROF est semblable à GPU prof. | ||
</div> | |||
{{Command | |||
|module load cuda/11.7 | |||
}} | |||
To profile a pure CPU executable, we need to add the arguments <code>--cpu-profiling on</code> to the command line: | |||
{{Command | {{Command | ||
|nvprof --cpu-profiling on ./cg.x | |nvprof --cpu-profiling on ./cg.x | ||
|result= | |result= | ||
... | |||
<Program output > | <Program output > | ||
... | |||
======== CPU profiling result (bottom up): | ======== CPU profiling result (bottom up): | ||
Time(%) Time Name | |||
83.54% 90.6757s matvec(matrix const &, vector const &, vector const &) | |||
83.54% 90.6757s {{!}} main | |||
7.94% 8.62146s waxpby(double, vector const &, double, vector const &, vector const &) | |||
2. | 7.94% 8.62146s {{!}} main | ||
2. | 5.86% 6.36584s dot(vector const &, vector const &) | ||
0. | 5.86% 6.36584s {{!}} main | ||
2.47% 2.67666s allocate_3d_poisson_matrix(matrix&, int) | |||
2.47% 2.67666s {{!}} main | |||
0.13% 140.35ms initialize_vector(vector&, double) | |||
0.13% 140.35ms {{!}} main | |||
... | |||
======== Data collected at 100Hz frequency | ======== Data collected at 100Hz frequency | ||
}} | }} | ||
From the above output, the <code>matvec()</code> function is responsible for 83.5% of the execution time, and this function call can be found in the <code>main()</code> function. | |||
==Renseignements sur le compilateur== | ==Renseignements sur le compilateur== |
Revision as of 15:34, 12 December 2022
- comprendre ce qu'est un profileur
- savoir utiliser le profileur NVPROF
- comprendre la performance du code
- savoir concentrer vos efforts et réécrire les routines qui exigent beaucoup de temps
Profiler du code
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),
- comprendre la performance du code,
- savoir comment mieux employer votre temps.
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.
Préparer le code pour l'exercice
Pour notre exemple, nous utilisons du code provenant de ces dépôts. Téléchargez les fichiers et utilisez les répertoires cpp ou f90. Le but de l'exercice est de compiler et lier le code et d'obtenir un exécutable que nous profilerons.
En date de mai 2016, relativement peu de compilateurs offraient les fonctionnalités d'OpenACC. Les plus avancés en ce sens sont les compilateurs du Portland Group de NVidia et ceux de Cray. Pour ce est qui de GNU, l'implémentation d'OpenACC dans la version 5 était expérimentale et devrait être complète dans la version 6.
As for the GNU compilers, since GCC version 6, the support for OpenACC 2.x kept improving. As of July 2022, GCC versions 10, 11 and 12 support OpenACC version 2.6.
Dans ce tutoriel, nous utilisons la version 16.3 des compilateurs du Portland Group qui sont gratuits pour des fins de recherche universitaire.
[name@server ~]$ module load nvhpc/22.7
Lmod is automatically replacing "intel/2020.1.217" with "nvhpc/22.7".
The following have been reloaded with a version change:
1) gcccore/.9.3.0 => gcccore/.11.3.0 3) openmpi/4.0.3 => openmpi/4.1.4
2) libfabric/1.10.1 => libfabric/1.15.1 4) ucx/1.8.0 => ucx/1.12.1
[name@server ~]$ make
nvc++ -c -o main.o main.cpp
nvc++ main.o -o cg.x
Une fois l'exécutable créé, nous allons profiler le code.
Dans ce tutoriel, nous utilisons plusieurs des profileurs suivants :
- PGPROF : outil simple mais puissant pour l'analyse de programmes parallèles écrits avec OpenMP, OpenACC ou CUDA; rappelons que PGPROF est gratuit pour des fins de recherche universitaire.
- NVVP (NVIDIA Visual Profiler) : outil d'analyse multiplateforme pour des programmes écrits avec OpenACC et CUDA C/C++.
- NVPROF : version ligne de commande du NVIDIA Visual Profiler.
NVPROF ligne de commande
La version ligne de commande de NVPROF est semblable à GPU prof.
[name@server ~]$ module load cuda/11.7
To profile a pure CPU executable, we need to add the arguments --cpu-profiling on
to the command line:
[name@server ~]$ nvprof --cpu-profiling on ./cg.x
...
<Program output >
...
======== CPU profiling result (bottom up):
Time(%) Time Name
83.54% 90.6757s matvec(matrix const &, vector const &, vector const &)
83.54% 90.6757s | main
7.94% 8.62146s waxpby(double, vector const &, double, vector const &, vector const &)
7.94% 8.62146s | main
5.86% 6.36584s dot(vector const &, vector const &)
5.86% 6.36584s | main
2.47% 2.67666s allocate_3d_poisson_matrix(matrix&, int)
2.47% 2.67666s | main
0.13% 140.35ms initialize_vector(vector&, double)
0.13% 140.35ms | main
...
======== Data collected at 100Hz frequency
From the above output, the matvec()
function is responsible for 83.5% of the execution time, and this function call can be found in the main()
function.
Renseignements sur le compilateur
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?
- Qu'est-ce qui a empêché d'optimiser davantage?
- La performance serait-elle affectée par les petites modifications?
Le compilateur PGI offre l'indicateur -Minfo avec les options suivantes :
- accel – liste des opérations du compilateur relativement à l'accélérateur
- all – résultats en sortie du compilateur
- intensity – renseignements sur l'intensité de la boucle
- ccff – ajout de renseignements aux fichiers objet pour utilisation future
Obtenir les renseignements sur le compilateur
- Éditez le Makefile.
CXX=nvc++ CXXFLAGS=-fast -Minfo=all,intensity,ccff LDFLAGS=${CXXFLAGS}
- Effectuez un nouveau build.
[name@server ~]$ make clean; make
nvc++ -fast -Minfo=all,intensity,ccff -c -o main.o main.cpp
initialize_vector(vector &, double):
20, include "vector.h"
36, Intensity = 0.0
Memory set idiom, loop replaced by call to __c_mset8
dot(const vector &, const vector &):
21, include "vector_functions.h"
27, Intensity = 1.00
Generated vector simd code for the loop containing reductions
FMA (fused multiply-add) instruction(s) generated
waxpby(double, const vector &, double, const vector &, const vector &):
21, include "vector_functions.h"
39, Intensity = 1.00
Loop not vectorized: data dependency
Generated vector simd code for the loop
Loop unrolled 2 times
FMA (fused multiply-add) instruction(s) generated
allocate_3d_poisson_matrix(matrix &, int):
22, include "matrix.h"
43, Intensity = 0.0
Loop not fused: different loop trip count
44, Intensity = 0.0
Loop not vectorized/parallelized: loop count too small
45, Intensity = 0.0
57, Intensity = 0.0
59, Intensity = 0.0
Loop not vectorized: data dependency
matvec(const matrix &, const vector &, const vector &):
23, include "matrix_functions.h"
29, Intensity = (num_rows*((row_end-row_start)* 2))/(num_rows+(num_rows+(num_rows+((row_end-row_start)+(row_end-row_start)))))
FMA (fused multiply-add) instruction(s) generated
33, Intensity = 1.00
Loop not vectorized: non-stride-1 array reference
Loop not vectorized: mixed data types
Loop unrolled 2 times
FMA (fused multiply-add) instruction(s) generated
main:
38, allocate_3d_poisson_matrix(matrix &, int) inlined, size=41 (inline) file main.cpp (29)
43, Intensity = 0.0
Loop not fused: different loop trip count
44, Intensity = 0.0
Loop not vectorized/parallelized: loop count too small
45, Intensity = 0.0
57, Intensity = 0.0
Loop not fused: function call before adjacent loop
59, Intensity = 0.0
Loop not vectorized: data dependency
42, allocate_vector(vector &, unsigned int) inlined, size=3 (inline) file main.cpp (24)
43, allocate_vector(vector &, unsigned int) inlined, size=3 (inline) file main.cpp (24)
44, allocate_vector(vector &, unsigned int) inlined, size=3 (inline) file main.cpp (24)
45, allocate_vector(vector &, unsigned int) inlined, size=3 (inline) file main.cpp (24)
46, allocate_vector(vector &, unsigned int) inlined, size=3 (inline) file main.cpp (24)
48, initialize_vector(vector &, double) inlined, size=5 (inline) file main.cpp (34)
36, Intensity = 0.0
Loop not vectorized/parallelized: not countable
49, initialize_vector(vector &, double) inlined, size=5 (inline) file main.cpp (34)
36, Intensity = 0.0
Loop not vectorized/parallelized: not countable
52, waxpby(double, const vector &, double, const vector &, const vector &) inlined, size=10 (inline) file main.cpp (33)
39, Intensity = 0.0
Memory copy idiom, loop replaced by call to __c_mcopy8
53, matvec(const matrix &, const vector &, const vector &) inlined, size=19 (inline) file main.cpp (20)
29, Intensity = [symbolic], and not printable, try the -Mpfi -Mpfo options
Loop not fused: different loop trip count
33, Intensity = 1.00
Loop not vectorized: non-stride-1 array reference
Loop not vectorized: mixed data types
Loop unrolled 2 times
54, waxpby(double, const vector &, double, const vector &, const vector &) inlined, size=10 (inline) file main.cpp (33)
27, FMA (fused multiply-add) instruction(s) generated
29, FMA (fused multiply-add) instruction(s) generated
33, FMA (fused multiply-add) instruction(s) generated
39, Intensity = 0.67
Loop not fused: different loop trip count
Loop not vectorized: data dependency
Generated vector simd code for the loop
Loop unrolled 4 times
FMA (fused multiply-add) instruction(s) generated
56, dot(const vector &, const vector &) inlined, size=9 (inline) file main.cpp (21)
27, Intensity = 1.00
Loop not fused: function call before adjacent loop
Generated vector simd code for the loop containing reductions
61, Intensity = 0.0
62, waxpby(double, const vector &, double, const vector &, const vector &) inlined, size=10 (inline) file main.cpp (33)
39, Intensity = 0.0
Memory copy idiom, loop replaced by call to __c_mcopy8
65, dot(const vector &, const vector &) inlined, size=9 (inline) file main.cpp (21)
27, Intensity = 1.00
Loop not fused: different loop trip count
Generated vector simd code for the loop containing reductions
67, waxpby(double, const vector &, double, const vector &, const vector &) inlined, size=10 (inline) file main.cpp (33)
39, Intensity = 0.67
Loop not fused: different loop trip count
Loop not vectorized: data dependency
Generated vector simd code for the loop
Loop unrolled 4 times
72, matvec(const matrix &, const vector &, const vector &) inlined, size=19 (inline) file main.cpp (20)
29, Intensity = [symbolic], and not printable, try the -Mpfi -Mpfo options
Loop not fused: different loop trip count
33, Intensity = 1.00
Loop not vectorized: non-stride-1 array reference
Loop not vectorized: mixed data types
Loop unrolled 2 times
73, dot(const vector &, const vector &) inlined, size=9 (inline) file main.cpp (21)
27, Intensity = 1.00
Loop not fused: different loop trip count
Generated vector simd code for the loop containing reductions
77, waxpby(double, const vector &, double, const vector &, const vector &) inlined, size=10 (inline) file main.cpp (33)
39, Intensity = 0.67
Loop not fused: different loop trip count
Loop not vectorized: data dependency
Generated vector simd code for the loop
Loop unrolled 4 times
78, waxpby(double, const vector &, double, const vector &, const vector &) inlined, size=10 (inline) file main.cpp (33)
39, Intensity = 0.67
Loop not fused: function call before adjacent loop
Loop not vectorized: data dependency
Generated vector simd code for the loop
Loop unrolled 4 times
88, free_vector(vector &) inlined, size=2 (inline) file main.cpp (29)
89, free_vector(vector &) inlined, size=2 (inline) file main.cpp (29)
90, free_vector(vector &) inlined, size=2 (inline) file main.cpp (29)
91, free_vector(vector &) inlined, size=2 (inline) file main.cpp (29)
92, free_matrix(matrix &) inlined, size=5 (inline) file main.cpp (73)
nvc++ main.o -o cg.x -fast -Minfo=all,intensity,ccff
Intensité computationnelle
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.
intensité computationnelle = opérations de calcul / opérations en mémoire
Une valeur de 1 ou plus indique que la boucle serait bien exécutée sur un processeur graphique (GPU).
Comprendre le code
Regardons attentivement le code suivant de matrix_functions.h
:
for(int i=0;i<num_rows;i++) {
double sum=0;
int row_start=row_offsets[i];
int row_end=row_offsets[i+1];
for(int j=row_start; j<row_end;j++) {
unsigned int Acol=cols[j];
double Acoef=Acoefs[j];
double xcoef=xcoefs[Acol];
sum+=Acoef*xcoef;
}
ycoefs[i]=sum;
}
On trouvera les dépendances de données en se posant les questions suivantes :
- Une itération en affecte-t-elle d'autres?
- Les itérations lisent-elles ou écrivent-elles à des endroits différents du même tableau?
- Est-ce que sum est une dépendance? Non, c'est une réduction.
Maintenant que le code est analysé, nous pouvons ajouter des directives au compilateur.
<- Page précédente, Introduction | ^- Retour au début du tutoriel | Page suivante, Ajouter des directives ->