Le calcul numérique de haute performance - Université de Laval
Le calcul numérique de haute performance - Université de Laval
Le calcul numérique de haute performance - Université de Laval
Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
<strong>Le</strong> <strong>calcul</strong> <strong>numérique</strong> <strong>de</strong> <strong>haute</strong> <strong>performance</strong><br />
en astrophysique<br />
Hugo Martel<br />
<strong>Université</strong> <strong>Laval</strong><br />
Québec, 31 août 2007
1) Introduction<br />
<strong>Le</strong> <strong>calcul</strong> <strong>de</strong> <strong>haute</strong> <strong>performance</strong> (CHP) s’est développé selon 3 axes:<br />
1) Résolution (nombre <strong>de</strong> particules N)<br />
2) Précision <strong>de</strong>s algorithmes<br />
3) Détails <strong>de</strong>s processus physiques<br />
précision<br />
P 3 M<br />
PM<br />
Arbre + Quad<br />
512 3<br />
64 3<br />
1000<br />
Arbre<br />
résolution<br />
2048 3<br />
256 3<br />
32 3 (1985)<br />
50<br />
Gravité<br />
Hydrodynamique Transfer radiatif<br />
Réactions<br />
chimiques<br />
Relativité<br />
physique
À partir <strong>de</strong> quand un <strong>calcul</strong> <strong>de</strong>vient “<strong>haute</strong> <strong>performance</strong>” ?<br />
Réponse: à partir du moment où il est dans notre intérêt<br />
d’optimiser le programme.<br />
Exemple: Supposons qu’en travaillant pendant 1 mois sur<br />
l’optimisation d’un programme, on peut quadrupler sa vitesse<br />
d’exécution.<br />
• Temps d’exécution <strong>de</strong> une minute: on investi 1 mois <strong>de</strong> travail<br />
pour sauver 45 secon<strong>de</strong>s: ça ne vaut pas la peine.<br />
• Temps d’exécution <strong>de</strong> 2 ans: on investi 1 mois <strong>de</strong> travail pour<br />
sauver 18 mois: ça vaut la peine.<br />
L’optimisation est le concept fondamental du CHP<br />
optimisation <strong>performance</strong><br />
Langage <strong>de</strong> programation: FORTRAN<br />
3 types d’architectures: • Ordinateurs sériels<br />
• Ordinateurs vectoriels<br />
• Ordinateurs parallèles
2) Ordinateurs sériels<br />
Tous les ordinateurs datant d’avant 1980.<br />
Tous les ordinateurs portables.<br />
Toutes les stations <strong>de</strong> travail.<br />
En gros, tous les ordinateurs qu’on n’appelle pas “superordinateur”.<br />
Ordinateur sériel: <strong>Le</strong>s instructions sont exécutées en série (une à la fois).
Exemple concret: <strong>calcul</strong> <strong>de</strong> la correction à courte distance dans l’algorithme P 3 M<br />
�(r) =<br />
Volume cubique contenant N particules.<br />
On i<strong>de</strong>ntifie les paires <strong>de</strong> particules séparées<br />
par une courte distance r < 2.8� (particules<br />
voisines).<br />
Pour chaque paire <strong>de</strong> particules, on <strong>calcul</strong>e<br />
la fonction �(r).<br />
� est la longeur d’adoucissement.<br />
�(224x�224x 3 +70x 4 +48x 5 �21x 6 )/35� 2 , x < 1;<br />
�(12/x 2 �224+896x�840x 2 +224x 3 +70x 4 �48x 5 +7x 6 )/35� 2 , 1 < x < 2;<br />
�1/r 2 , x > 2.<br />
où x = 2r/�
subroutine shortrange(r,pairs,src,npairs,nmax,np)<br />
integer pairs(nmax,2)<br />
dimension src(nmax), r(np,3)<br />
do m=1,npairs<br />
i=pairs(m,1)<br />
j=pairs(m,2)<br />
dx=r(i,1)-r(j,1)<br />
dy=r(i,2)-r(j,2)<br />
dz=r(i,3)-r(j,3)<br />
r2=dx*dx+dy*dy+dz*dz<br />
r=sqrt(r2)<br />
src(m)=CHI(r)<br />
enddo<br />
return<br />
end<br />
*--------------------------------------------------------------function<br />
chi(r)<br />
parameter (epsilon=0.00001)<br />
c1=-1./(35.*epsilon*epsilon)<br />
x=2*r/epsilon<br />
if(x.lt.1.) then<br />
chi=c1*x*(224.+x*x*(-224.+x*(70.+x*(48.-21.*x))))<br />
else if(x.lt.2.) then<br />
chi=c1*(12.+x*x*(-224.+x*(896.+x*(-840.+x*(224.+x*<br />
+ (70.+x*(-48.+7.*x)))))))/x/x<br />
else<br />
chi=-1./r**2<br />
endif<br />
return<br />
end
chi(0:npt)<br />
0<br />
1<br />
2<br />
3<br />
4<br />
5<br />
6<br />
7<br />
8<br />
9<br />
npt<br />
�(0)<br />
�(�r)<br />
�(2�r)<br />
�(3�r)<br />
�(4�r)<br />
�(5�r)<br />
�(6�r)<br />
�(7�r)<br />
�(8�r)<br />
�(9�r)<br />
...<br />
�(npt*�r)<br />
Table d’interpolation pour �(r)<br />
chi(k+1)<br />
chi(k)<br />
�<br />
c = (r � k�r) / �r<br />
� = (1 � c) chi(k) + c chi(k+1)<br />
k�r r (k+1)�r<br />
d = r / �r<br />
k = int(d), c = frac(d)
subroutine shortrange(r,pairs,src,npairs,nmax,np)<br />
parameter (npt=10000)<br />
integer pairs(nmax,2)<br />
dimension src(nmax), r(np,3)<br />
common /short/ chi(0:npt), dr<br />
do m=1,npairs<br />
i=pairs(m,1)<br />
j=pairs(m,2)<br />
dx=r(i,1)-r(j,1)<br />
dy=r(i,2)-r(j,2)<br />
dz=r(i,3)-r(j,3)<br />
r2=dx*dx+dy*dy+dz*dz<br />
r=sqrt(r2)<br />
d=r/dr<br />
k=int(d)<br />
c=d-k<br />
src(m)=(1.-c)*chi(k)+c*chi(k+1)<br />
enddo<br />
return<br />
end<br />
+ + � � �
� = (1 � c) chi(k) + c chi(k+1)<br />
� = chi(k) + c [ chi(k+1) � chi(k) ]<br />
� = chi(k) + c �chi(k)<br />
où: �chi(k) = chi(k+1) � chi(k)<br />
chi(0:npt)<br />
0<br />
1<br />
2<br />
3<br />
4<br />
5<br />
6<br />
7<br />
8<br />
9<br />
npt<br />
�(0)<br />
�(�r)<br />
�(2�r)<br />
�(3�r)<br />
�(4�r)<br />
�(5�r)<br />
�(6�r)<br />
�(7�r)<br />
�(8�r)<br />
�(9�r)<br />
...<br />
�(npt*�r)<br />
0<br />
1<br />
2<br />
3<br />
4<br />
5<br />
6<br />
7<br />
8<br />
9<br />
npt-1<br />
dchi(0:npt-1)<br />
�(�r)��(0)<br />
�(2�r)��(�r)<br />
�(3�r)��(2�r)<br />
�(4�r)��(3�r)<br />
�(5�r)��(4�r)<br />
�(6�r)��(5�r)<br />
�(7�r)-�(6�r)<br />
�(8�r)��(7�r)<br />
�(9�r)��(8�r)<br />
�(10�r)��(9�r)<br />
...<br />
�(npt*�r)-�[(npt-1)*�r]
subroutine shortrange(r,pairs,src,npairs,nmax,np)<br />
parameter (npt=10000)<br />
integer pairs(nmax,2)<br />
dimension src(nmax), r(np,3)<br />
common /short/ chi(0:npt), dchi(0:npt-1), dr<br />
do m=1,npairs<br />
i=pairs(m,1)<br />
j=pairs(m,2)<br />
dx=r(i,1)-r(j,1)<br />
dy=r(i,2)-r(j,2)<br />
dz=r(i,3)-r(j,3)<br />
r2=dx*dx+dy*dy+dz*dz<br />
r=sqrt(r2)<br />
d=r/dr<br />
k=int(d)<br />
c=d-k<br />
src(m)=chi(k)+c*dchi(k)<br />
enddo<br />
return<br />
end<br />
+ + �
subroutine shortrange(r,pairs,src,npairs,nmax,np)<br />
parameter (npt=10000)<br />
integer pairs(nmax,2)<br />
dimension src(nmax), r(np,3)<br />
common /short/ chi(0:npt), dchi(0:npt-1), dr<br />
do m=1,npairs<br />
i=pairs(m,1)<br />
j=pairs(m,2)<br />
dx=r(i,1)-r(j,1)<br />
dy=r(i,2)-r(j,2)<br />
dz=r(i,3)-r(j,3)<br />
r2=dx*dx+dy*dy+dz*dz<br />
r=sqrt(r2)<br />
d=r/dr<br />
k=int(d)<br />
c=d-k<br />
src(m)=chi(k)+c*dchi(k)<br />
enddo<br />
return<br />
end<br />
+ + + +<br />
- � � �<br />
� � � �<br />
/<br />
int()<br />
sqrt()
Une racine carrée est 10 à 20 fois plus<br />
longue à <strong>calcul</strong>er qu’une addition ou une<br />
multiplication.<br />
Solution: considérer la fonction � comme<br />
étant une fonction <strong>de</strong> r 2 plutôt que r.<br />
On crée <strong>de</strong> nouvelles tables d’interpolation<br />
dans lesquelles la fonction est évaluée à<br />
<strong>de</strong>s intervalles constants en r 2 .<br />
Nouvel incrément: �r 2<br />
chi(0:npt)<br />
0<br />
1<br />
2<br />
3<br />
4<br />
5<br />
6<br />
7<br />
8<br />
9<br />
npt<br />
�(0)<br />
�(�r 2 )<br />
�(2�r 2 )<br />
�(3�r 2 )<br />
�(4�r 2 )<br />
�(5�r 2 )<br />
�(6�r 2 )<br />
�(7�r 2 )<br />
�(8�r 2 )<br />
�(9�r 2 )<br />
...<br />
�(npt*�r 2 )<br />
0<br />
1<br />
2<br />
3<br />
4<br />
5<br />
6<br />
7<br />
8<br />
9<br />
npt-1<br />
dchi(0:npt-1)<br />
�(�r 2 )��(0)<br />
�(2�r 2 )��(�r 2 )<br />
�(3�r 2 )��(2�r 2 )<br />
�(4�r 2 )��(3�r 2 )<br />
�(5�r 2 )��(4�r 2 )<br />
�(6�r 2 )��(5�r 2 )<br />
�(7�r 2 )-�(6�r 2 )<br />
�(8�r 2 )��(7�r 2 )<br />
�(9�r 2 )��(8�r 2 )<br />
�(10�r 2 )��(9�r 2 )<br />
...<br />
�(npt*�r 2 )-�[(npt-1)*�r 2 ]
subroutine shortrange(r,pairs,src,npairs,nmax,np)<br />
parameter (npt=10000)<br />
integer pairs(nmax,2)<br />
dimension src(nmax), r(np,3)<br />
common /short/ chi(0:npt), dchi(0:npt-1), dr2<br />
do m=1,npairs<br />
i=pairs(m,1)<br />
j=pairs(m,2)<br />
dx=r(i,1)-r(j,1)<br />
dy=r(i,2)-r(j,2)<br />
dz=r(i,3)-r(j,3)<br />
r2=dx*dx+dy*dy+dz*dz<br />
d=r2/dr2<br />
k=int(d)<br />
c=d-k<br />
src(m)=chi(k)+c*dchi(k)<br />
enddo<br />
return<br />
end
<strong>Le</strong>s 4 opérations mathématiques <strong>de</strong> base (être humain) : + � � /<br />
<strong>Le</strong>s 4 opérations mathématiques <strong>de</strong> base (ordinateur) : + CS � INV<br />
Soustraction: a � b a + CS(b)<br />
Division: a / b a � INV(b)<br />
Temps <strong>de</strong> <strong>calcul</strong>:<br />
Comparable pour + � INV (typically 1 ns)<br />
Beaucoup plus court pour CS (1 bit vs 64 bits).<br />
Comparable pour + � �<br />
2 fois plus long pour /<br />
On a intérêt à éliminer les divisions.
subroutine shortrange(r,pairs,src,npairs,nmax,np)<br />
parameter (npt=10000)<br />
integer pairs(nmax,2)<br />
dimension src(nmax), r(np,3)<br />
common /short/ chi(0:npt), dchi(0:npt-1), dr2<br />
odr2=1./dr2<br />
do m=1,npairs<br />
i=pairs(m,1)<br />
j=pairs(m,2)<br />
dx=r(i,1)-r(j,1)<br />
dy=r(i,2)-r(j,2)<br />
dz=r(i,3)-r(j,3)<br />
r2=dx*dx+dy*dy+dz*dz<br />
d=r2*odr2<br />
k=int(d)<br />
c=d-k<br />
src(m)=chi(k)+c*dchi(k)<br />
enddo<br />
return<br />
end
subroutine shortrange(r,pairs,src,npairs,nmax,np)<br />
parameter (npt=10000)<br />
integer pairs(nmax,2)<br />
dimension src(nmax), r(np,3)<br />
common /short/ chi(0:npt), dchi(0:npt-1), dr2<br />
odr2=1./dr2<br />
do m=1,npairs<br />
i=pairs(m,1)<br />
j=pairs(m,2)<br />
dx=r(i,1)-r(j,1)<br />
dy=r(i,2)-r(j,2)<br />
dz=r(i,3)-r(j,3)<br />
r2=dx*dx+dy*dy+dz*dz<br />
d=r2*odr2<br />
k=int(d)<br />
c=d-k<br />
src(m)=chi(k)+c*dchi(k)<br />
enddo<br />
return<br />
end<br />
Alternative<br />
dx2=dx*dx<br />
dy2=dy*dy<br />
dz2=dz*dz<br />
r2=dx2+dy2+dz2<br />
Très mauvaise idée!<br />
Raison: existence<br />
<strong>de</strong>s MADD’s
MADD’s : “Multiply - Add’s”<br />
(a+b)*c Addition: 1 ns<br />
Multiplication: 1 ns<br />
Temps total: 2 ns 1.1 ns<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
1<br />
1<br />
0<br />
1<br />
0<br />
0<br />
1<br />
1<br />
0<br />
0<br />
0<br />
0<br />
1<br />
1<br />
0<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
1<br />
0<br />
1<br />
1<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
1<br />
1<br />
0<br />
1<br />
0<br />
0<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
1<br />
0<br />
0<br />
0<br />
1<br />
0<br />
1<br />
0<br />
0<br />
0<br />
0<br />
0<br />
1<br />
1<br />
1<br />
1<br />
0<br />
0<br />
1<br />
1<br />
0<br />
1<br />
0<br />
1<br />
0<br />
0<br />
1<br />
1<br />
0<br />
1<br />
1<br />
0<br />
1<br />
0<br />
1<br />
0<br />
1<br />
0<br />
0<br />
1<br />
1<br />
1<br />
0<br />
1<br />
1<br />
1<br />
0<br />
0<br />
1<br />
1<br />
0<br />
1<br />
1<br />
1<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
0<br />
0<br />
1<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
1<br />
1<br />
1<br />
0<br />
0<br />
1<br />
1<br />
0<br />
1<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
1<br />
0<br />
0<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
0<br />
1<br />
0<br />
0<br />
1<br />
0<br />
0<br />
1<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
1<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
0<br />
1<br />
a<br />
b<br />
a+b<br />
c<br />
(a+b)*c
MADD’s : “Multiply - Add’s”<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
1<br />
1<br />
0<br />
1<br />
0<br />
0<br />
1<br />
1<br />
0<br />
0<br />
0<br />
0<br />
1<br />
1<br />
0<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
1<br />
0<br />
1<br />
1<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
1<br />
1<br />
0<br />
1<br />
0<br />
0<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
1<br />
0<br />
0<br />
0<br />
1<br />
0<br />
1<br />
0<br />
0<br />
0<br />
0<br />
0<br />
1<br />
1<br />
1<br />
1<br />
0<br />
0<br />
1<br />
1<br />
0<br />
1<br />
0<br />
1<br />
0<br />
0<br />
1<br />
1<br />
0<br />
1<br />
1<br />
0<br />
1<br />
0<br />
1<br />
0<br />
1<br />
0<br />
0<br />
1<br />
1<br />
1<br />
0<br />
1<br />
1<br />
1<br />
0<br />
0<br />
1<br />
1<br />
0<br />
1<br />
1<br />
1<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
0<br />
0<br />
1<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
1<br />
1<br />
1<br />
0<br />
0<br />
1<br />
1<br />
0<br />
1<br />
0<br />
1<br />
1<br />
1<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
1<br />
0<br />
0<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
0<br />
1<br />
0<br />
0<br />
1<br />
0<br />
0<br />
1<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
1<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
0<br />
1<br />
a<br />
b<br />
a+b<br />
c<br />
(a+b)*c<br />
L’addition commence
MADD’s : “Multiply - Add’s”<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
1<br />
1<br />
0<br />
1<br />
0<br />
0<br />
1<br />
1<br />
0<br />
0<br />
0<br />
0<br />
1<br />
1<br />
0<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
1<br />
0<br />
1<br />
1<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
1<br />
1<br />
0<br />
1<br />
0<br />
0<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
1<br />
0<br />
0<br />
0<br />
1<br />
0<br />
1<br />
0<br />
0<br />
0<br />
0<br />
0<br />
1<br />
1<br />
1<br />
1<br />
0<br />
0<br />
1<br />
1<br />
0<br />
1<br />
0<br />
1<br />
0<br />
0<br />
1<br />
1<br />
0<br />
1<br />
1<br />
0<br />
1<br />
0<br />
1<br />
0<br />
1<br />
0<br />
0<br />
1<br />
1<br />
1<br />
0<br />
1<br />
1<br />
1<br />
0<br />
0<br />
1<br />
1<br />
0<br />
1<br />
1<br />
1<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
0<br />
0<br />
1<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
1<br />
1<br />
1<br />
0<br />
0<br />
1<br />
1<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
0<br />
1<br />
1<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
1<br />
0<br />
0<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
0<br />
1<br />
0<br />
0<br />
1<br />
0<br />
0<br />
1<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
1<br />
0<br />
1<br />
0<br />
1<br />
1<br />
0<br />
0<br />
0<br />
1<br />
1<br />
1<br />
0<br />
1<br />
a<br />
b<br />
a+b<br />
c<br />
(a+b)*c<br />
La multiplication commence,<br />
l’addition continue
Boucles multiples<br />
Optimisation: A) Remonter vers les boucles extérieures<br />
B) Ordre <strong>de</strong>s boucles
A) Remonter vers les boucles extérieures<br />
Dans le cas <strong>de</strong> boucles multiples, le gros du travail<br />
est effectué par la boucle intérieure<br />
C’est la boucle intérieure qu’il faut optimiser<br />
parameter (n=128)<br />
dimension a(n), b(n), c(n)<br />
dimension x(n**3)<br />
in<strong>de</strong>x=0<br />
do i=1,n<br />
do j=1,n<br />
do k=1,n<br />
in<strong>de</strong>x=in<strong>de</strong>x+1<br />
x(in<strong>de</strong>x)=a(i)*a(i)+b(j)*b(j)+c(k)*c(k)<br />
enddo<br />
enddo<br />
enddo<br />
parameter (n=128)<br />
dimension a(n), b(n), c(n)<br />
dimension x(n**3)<br />
in<strong>de</strong>x=0<br />
do i=1,n<br />
a2=a(i)*a(i)<br />
do j=1,n<br />
b2=b(j)*b(j)<br />
a2b2=a2+b2<br />
do k=1,n<br />
in<strong>de</strong>x=in<strong>de</strong>x+1<br />
x(in<strong>de</strong>x)=a2b2+c(k)*c(k)<br />
enddo<br />
enddo<br />
enddo<br />
+ : 4,194,304<br />
� : 6,291,456<br />
+ : 2,113,536<br />
� : 2,113,664
Autre example<br />
parameter (m=64,n=128)<br />
dimension a(m), b(n)<br />
t=0.<br />
do i=1,m<br />
x=a(i)<br />
do j=1,n<br />
y=b(j)<br />
t=t+sin(sqrt(x)+sqrt(y))<br />
enddo<br />
enddo<br />
parameter (m=64,n=128)<br />
dimension a(m), b(n)<br />
t=0.<br />
do i=1,m<br />
x=a(i)<br />
sqx=sqrt(x)<br />
do j=1,n<br />
y=b(j)<br />
t=t+sin(sqx+sqrt(y))<br />
enddo<br />
enddo<br />
Incorrect<br />
Correct
B) Ordre <strong>de</strong>s boucles.<br />
Considérons 2 boucles <strong>de</strong> tailles très différentes<br />
n m<br />
t = � � exp ( a i x j + b i y j + c i z j )<br />
i=1 j=1<br />
où n = 2 000 000 et m = 10
1 e approche<br />
parameter (n=2000000,m=10)<br />
dimension a(n), b(n), c(n)<br />
dimension x(m), y(m), z(m)<br />
t=0.<br />
do i=1,n<br />
ai=a(i)<br />
bi=b(i)<br />
ci=c(i)<br />
do j=1,m<br />
t=t+exp(ai*x(j))+exp(bi*y(j))+exp(ci*z(j))<br />
enddo<br />
enddo<br />
2 e approche<br />
parameter (n=2000000,m=10)<br />
dimension a(n), b(n), c(n)<br />
dimension x(m), y(m), z(m)<br />
t=0.<br />
do j=1,m<br />
xj=x(j)<br />
yj=y(j)<br />
zj=z(j)<br />
do i=1,n<br />
t=t+exp(a(i)*xj)+exp(b(i)*yj)+exp(c(i)*zj)<br />
enddo<br />
enddo<br />
Boucle intérieure<br />
exécutée 2 000 000 <strong>de</strong><br />
fois.<br />
Chaque fois: 30 “fetch”.<br />
Nombre total <strong>de</strong> “fetch”:<br />
60 000 000.<br />
Boucle intérieure<br />
exécutée 10 fois.<br />
Chaque fois: 6 000 000<br />
“fetch”.<br />
Nombre total <strong>de</strong> “fetch”:<br />
60 000 000.
Cache<br />
Rapi<strong>de</strong><br />
<strong>Le</strong>nt<br />
Cache : Capacité très faible<br />
Processeur<br />
Mémoire<br />
Vitesse d’accès très élevée<br />
<strong>Le</strong>nt<br />
Utile lorsqu’on a un petit nombre <strong>de</strong> variables utilisées très souvant.
1 e approche<br />
parameter (n=2000000,m=10)<br />
dimension a(n), b(n), c(n)<br />
dimension x(m), y(m), z(m)<br />
t=0.<br />
do i=1,n<br />
ai=a(i)<br />
bi=b(i)<br />
ci=c(i)<br />
do j=1,m<br />
t=t+exp(ai*x(j))+exp(bi*y(j))+exp(ci*z(j))<br />
enddo<br />
enddo<br />
2 e approche<br />
parameter (n=2000000,m=10)<br />
dimension a(n), b(n), c(n)<br />
dimension x(m), y(m), z(m)<br />
t=0.<br />
do j=1,m<br />
xj=x(j)<br />
yj=y(j)<br />
zj=z(j)<br />
do i=1,n<br />
t=t+exp(a(i)*xj)+exp(b(i)*yj)+exp(c(i)*zj)<br />
enddo<br />
enddo<br />
30 variables utilisées<br />
2 000 000 <strong>de</strong> fois<br />
chacunes.<br />
La cache permet<br />
d’accélérer le <strong>calcul</strong>.<br />
6 000 000 <strong>de</strong> variables<br />
utilisées 10 fois<br />
chacunes.<br />
La cache ne sert à rien.
1 e approche<br />
parameter (n=2000000,m=10)<br />
dimension a(n), b(n), c(n)<br />
dimension x(m), y(m), z(m)<br />
t=0.<br />
do i=1,n<br />
ai=a(i)<br />
bi=b(i)<br />
ci=c(i)<br />
do j=1,m<br />
t=t+exp(ai*x(j))+exp(bi*y(j))+exp(ci*z(j))<br />
enddo<br />
enddo<br />
• Itération i=1 : <strong>Le</strong> processeur va chercher les 30 variables x(j), y(j),<br />
z(j) dans la mémoire principale (lent), et ces variables sont copiées<br />
dans la cache (lent).<br />
• Itérations i=2, 3, ..., 2 000 000 : <strong>Le</strong> processeur va chercher les 30<br />
variables x(j), y(j), z(j) directement dans la cache (rapi<strong>de</strong>).<br />
En général, on a intérêt à mettre la petite boucle à l’intérieur <strong>de</strong> la<br />
gran<strong>de</strong>. EXCEPTION: Machines vectorielles.
3) Ordinateurs vectoriels<br />
Ex: Somme <strong>de</strong> 2 vecteurs: a(i) + b(i) = c(i), i = 1, … , n<br />
a<br />
b<br />
c<br />
dimension a(n), b(n), c(n)<br />
do i=1,n<br />
c(i)=a(i)+b(i)<br />
enddo<br />
ordinateur sériel :<br />
c(1) = a(1) + b(1)<br />
c(2) = a(2) + b(2)<br />
c(3) = a(3) + b(3)<br />
c(4) = a(4) + b(4)<br />
…<br />
ordinateur vectoriel : toutes les<br />
additions se font simultanément
Autres examples:<br />
copie<br />
dimension a(n), b(n)<br />
do i=1,n<br />
b(i)=a(i)<br />
enddo<br />
dimension a(n), b(n)<br />
do i=1,n<br />
b(i)=a(i)+x<br />
enddo<br />
addition et remplacement<br />
dimension a(n)<br />
do i=1,n<br />
a(i)=a(i)+x<br />
enddo<br />
multiplication<br />
dimension a(n), b(n)<br />
do i=1,n<br />
b(i)=a(i)*x<br />
enddo<br />
addition multiplication et remplacement<br />
dimension a(n)<br />
do i=1,n<br />
a(i)=a(i)*x<br />
enddo<br />
fonction intrinsèque<br />
dimension a(n), b(n)<br />
do i=1,n<br />
b(i)=sin(a(i))<br />
enddo
• Boucles multiples: seule la boucles intérieure peut<br />
se vectoriser.<br />
• <strong>Le</strong>s boucles ne sont pas toutes vectorisables. <strong>Le</strong><br />
compilateur déci<strong>de</strong> automatiquement quelles<br />
boucles peuvent être vectorisées, et les vectorise<br />
automatiquement.<br />
• Un bon compilateur va expliquer pourquoi<br />
certaines boucles ne vectorisent pas.
2 métho<strong>de</strong>s pour déterminer si une boucle est vectorisable:<br />
a) les itérations <strong>de</strong> la boucles pourrait-elles physiquement s’exécuter<br />
simultanément?<br />
b) le résultat serait-il i<strong>de</strong>ntique si les itérations s`exécutaient dans un<br />
autre ordre?<br />
dimension a(8), b(8), c(8)<br />
do i=1,8<br />
c(i)=a(i)+b(i)<br />
enddo<br />
ordre normal: m = 1, 2, 3, 4, 5, 6, 7, 8<br />
autre ordre: m = 5, 8, 3, 6, 1, 2, 7, 4<br />
Si les réponses sont OUI, la boucle est vectorisable.<br />
Si les réponses sont NON, la boucle n’est pas vectorisable.
Examples <strong>de</strong> boucles non-vectorisables:<br />
a) Input/Output<br />
dimension a(n)<br />
do i=1,n<br />
read(1,*) a(i)<br />
enddo<br />
b) Fonctions externes ou sousroutines.<br />
dimension a(n)<br />
do i=1,n<br />
x=a(i)<br />
call SUB(x)<br />
enddo<br />
--------------------------------------subroutine<br />
SUB(x)<br />
print *, x<br />
if(x.lt.0.) stop<br />
return<br />
end
Dans certains cas, on résout le problème gràce au inlining.<br />
non-vectorisable<br />
vectorisable<br />
dimension a(n)<br />
do i=1,n<br />
x=a(i)<br />
call WINDOW(x,xmin,xmax)<br />
a(i)=x<br />
enddo<br />
--------------------------------------subroutine<br />
WINDOW(x,xmin,xmax)<br />
x=AMAX1(x,xmin)<br />
x=AMIN1(x,xmax)<br />
return<br />
end<br />
dimension a(n)<br />
do i=1,n<br />
x=a(i)<br />
x=AMAX1(x,xmin)<br />
x=AMIN1(x,xmax)<br />
a(i)=x<br />
enddo
c) Interruption prématurée<br />
dimension a(n), b(n), c(n)<br />
do i=1,n<br />
c(i)=a(i)+b(i)<br />
if(c(i).lt.0.) stop<br />
enddo<br />
dimension a(n), b(n), c(n)<br />
do i=1,n<br />
c(i)=a(i)+b(i)<br />
if(c(i).lt.0.) go to 10<br />
enddo<br />
10 continue
d) Sélection<br />
dimension a(n), b(n), c(n)<br />
do i=1,n<br />
if(a(i).ge.1.) then<br />
c(i)=a(i)+b(i)**2<br />
else<br />
c(i)=a(i)-b(i)**2<br />
endif<br />
enddo<br />
Cray et IBM ont créé <strong>de</strong>s fonctions implicites spéciales pour permettre la<br />
vectorisation: CVMGP, CVMGM, CVMGZ, CVGMN, CVMGT<br />
Example: CVGMP(x,y,z) =<br />
dimension a(n), b(n), c(n)<br />
x, z � 0;<br />
y, z < 0.<br />
do i=1,n<br />
c(i)=a(i)+CVMGP(b(i)**2,-b(i)**2,a(i)-1.)<br />
enddo
e) Indices compliqués<br />
dimension a(n), b(n), in<strong>de</strong>x(n)<br />
do i=1,n<br />
b(in<strong>de</strong>x(i))=b(in<strong>de</strong>x(i))+a(i)<br />
enddo<br />
Exemple concret: <strong>calcul</strong> <strong>de</strong> la <strong>de</strong>nsité dans le programme P 3 M.<br />
Volume cubique contenant NP particules.<br />
On place dans le volume une grille cubique<br />
N � N � N.<br />
Bût: Calculer la <strong>de</strong>nsité sur la grille à partir<br />
<strong>de</strong>s particules.<br />
Chaque particules comtribuera à la <strong>de</strong>nsité<br />
au point <strong>de</strong> grille le plus proche, et aux 26<br />
points voisins (Triangular-Shaped Cloud).
subroutine assmass(r,b)<br />
parameter (n=256,np=2097152)<br />
dimension r(np,3), b(0:n-1,0:n-1,0:n-1)<br />
do m=1,np<br />
x=r(m,1)<br />
y=r(m,2)<br />
z=r(m,3)<br />
ir=int(n*x)<br />
jr=int(n*y)<br />
kr=int(n*z)<br />
dx=x-ir/float(n)<br />
dy=y-jr/float(n)<br />
dz=z-kr/float(n)<br />
do i=-1,1<br />
do j=-1,1<br />
do k=-1,1<br />
ii=ir+i<br />
jj=jr+j<br />
kk=kr+k<br />
t1=0.75-0.625*i*i+dx*(0.5*i+dx*(1.5*i*i-1.))<br />
t2=0.75-0.625*j*j+dy*(0.5*j+dy*(1.5*j*j-1.))<br />
t3=0.75-0.625*k*k+dz*(0.5*k+dz*(1.5*k*k-1.))<br />
b(ii,jj,kk)=b(ii,jj,kk)+t1*t2*t3<br />
enddo<br />
enddo<br />
enddo<br />
enddo<br />
return<br />
end
subroutine assmass(r,b)<br />
parameter (n=256,np=2097152)<br />
dimension r(np,3), b(0:n-1,0:n-1,0:n-1)<br />
do i=-1,1<br />
do j=-1,1<br />
do k=-1,1<br />
do m=1,np<br />
x=r(m,1)<br />
y=r(m,2)<br />
z=r(m,3)<br />
ir=int(n*x)<br />
jr=int(n*y)<br />
kr=int(n*z)<br />
dx=x-ir/float(n)<br />
dy=y-jr/float(n)<br />
dz=z-kr/float(n)<br />
ii=ir+i<br />
jj=jr+j<br />
kk=kr+k<br />
t1=0.75-0.625*i*i+dx*(0.5*i+dx*(1.5*i*i-1.))<br />
t2=0.75-0.625*j*j+dy*(0.5*j+dy*(1.5*j*j-1.))<br />
t3=0.75-0.625*k*k+dz*(0.5*k+dz*(1.5*k*k-1.))<br />
b(ii,jj,kk)=b(ii,jj,kk)+t1*t2*t3<br />
enddo<br />
enddo<br />
enddo<br />
enddo<br />
return<br />
end
subroutine assmass(r,b)<br />
parameter (n=256,np=2097152)<br />
dimension r(np,3), b(0:n-1,0:n-1,0:n-1)<br />
dimension aux(np)<br />
do i=-1,1<br />
do j=-1,1<br />
do k=-1,1<br />
do m=1,np<br />
x=r(m,1)<br />
y=r(m,2)<br />
z=r(m,3)<br />
ir=int(n*x)<br />
jr=int(n*y)<br />
kr=int(n*z)<br />
dx=x-ir/float(n)<br />
dy=y-jr/float(n)<br />
dz=z-kr/float(n)<br />
t1=0.75-0.625*i*i+dx*(0.5*i+dx*(1.5*i*i-1.))<br />
t2=0.75-0.625*j*j+dy*(0.5*j+dy*(1.5*j*j-1.))<br />
t3=0.75-0.625*k*k+dz*(0.5*k+dz*(1.5*k*k-1.))<br />
aux(np)=t1*t2*t3<br />
enddo<br />
do m=1,np<br />
ir=int(n*x)<br />
jr=int(n*y)<br />
kr=int(n*z)<br />
ii=ir+i<br />
jj=jr+j<br />
kk=kr+k<br />
b(ii,jj,kk)=b(ii,jj,kk)+aux(np)<br />
enddo<br />
enddo<br />
enddo<br />
enddo<br />
return<br />
end
subroutine assmass(r,b)<br />
parameter (n=256,np=2097152)<br />
dimension r(np,3), b(0:n-1,0:n-1,0:n-1)<br />
dimension aux(np)<br />
common /tsc/ a(-1:1), b(-1:1), c(-1:1)<br />
do i=-1,1<br />
do j=-1,1<br />
do k=-1,1<br />
do m=1,np<br />
x=r(m,1)<br />
y=r(m,2)<br />
z=r(m,3)<br />
ir=int(n*x)<br />
jr=int(n*y)<br />
kr=int(n*z)<br />
dx=x-ir/float(n)<br />
dy=y-jr/float(n)<br />
dz=z-kr/float(n)<br />
t1=b(i)+dx*(c(i)+dx*a(i))<br />
t2=b(j)+dy*(c(j)+dy*a(j))<br />
t3=b(k)+dz*(c(k)+dz*a(k))<br />
aux(np)=t1*t2*t3<br />
enddo<br />
do m=1,np<br />
ir=int(n*x)<br />
jr=int(n*y)<br />
kr=int(n*z)<br />
ii=ir+i<br />
jj=jr+j<br />
kk=kr+k<br />
b(ii,jj,kk)=b(ii,jj,kk)+aux(np)<br />
enddo<br />
enddo<br />
enddo<br />
enddo<br />
return<br />
end<br />
a(i) = 1.5*i*i-1<br />
b(i) = 0.75-0.625*i*i<br />
c(i) = 0.5*i
subroutine assmass(r,b)<br />
parameter (n=256,np=2097152)<br />
dimension r(np,3), b(0:n-1,0:n-1,0:n-1)<br />
dimension ir(np), jr(np), kr(np), dx(np), dy(np), dz(np)<br />
dimension aux(np)<br />
common /tsc/ a(-1:1), b(-1:1), c(-1:1)<br />
do m=1,np<br />
x=r(m,1)<br />
y=r(m,2)<br />
z=r(m,3)<br />
ir(m)=int(n*x)<br />
jr(m)=int(n*y)<br />
kr(m)=int(n*z)<br />
dx(m)=x-ir/float(n)<br />
dy(m)=y-jr/float(n)<br />
dz(m)=z-kr/float(n)<br />
enddo<br />
do i=-1,1<br />
do j=-1,1<br />
do k=-1,1<br />
do m=1,np<br />
t1=b(i)+dx(m)*(c(i)+dx(m)*a(i))<br />
t2=b(j)+dy(m)*(c(j)+dy(m)*a(j))<br />
t3=b(k)+dz(m)*(c(k)+dz(m)*a(k))<br />
aux(np)=t1*t2*t3<br />
enddo<br />
do m=1,np<br />
ii=ir(m)+i<br />
jj=jr(m)+j<br />
kk=kr(m)+k<br />
b(ii,jj,kk)=b(ii,jj,kk)+aux(np)<br />
enddo<br />
enddo<br />
enddo<br />
enddo<br />
return<br />
end
subroutine assmass(r,b)<br />
parameter (n=256,np=2097152)<br />
dimension r(np,3), b(0:n-1,0:n-1,0:n-1)<br />
dimension ir(np), jr(np), kr(np), dx(np), dy(np), dz(np)<br />
dimension aux(np)<br />
common /tsc/ a(-1:1), b(-1:1), c(-1:1)<br />
on=1./float(n)<br />
do m=1,np<br />
x=r(m,1)<br />
y=r(m,2)<br />
z=r(m,3)<br />
ir(m)=int(n*x)<br />
jr(m)=int(n*y)<br />
kr(m)=int(n*z)<br />
dx(m)=x-ir*on<br />
dy(m)=y-jr*on<br />
dz(m)=z-kr*on<br />
enddo<br />
do i=-1,1<br />
do j=-1,1<br />
do k=-1,1<br />
do m=1,np<br />
t1=b(i)+dx(m)*(c(i)+dx(m)*a(i))<br />
t2=b(j)+dy(m)*(c(j)+dy(m)*a(j))<br />
t3=b(k)+dz(m)*(c(k)+dz(m)*a(k))<br />
aux(np)=t1*t2*t3<br />
enddo<br />
do m=1,np<br />
ii=ir(m)+i<br />
jj=jr(m)+j<br />
kk=kr(m)+k<br />
b(ii,jj,kk)=b(ii,jj,kk)+aux(np)<br />
enddo<br />
enddo<br />
enddo<br />
enddo<br />
return<br />
end
4) Ordinateurs parallèles<br />
Ordinateur vectoriel: Un seul processeur, qui peux faire <strong>de</strong>s opérations<br />
simultanément sur toutes les composantes <strong>de</strong> vecteurs.<br />
Ordinateur parallèle: Plusieurs processeurs sériels qui se partage le <strong>calcul</strong>.
CPU1<br />
Exécution d’un programme parallèle<br />
région sériele région parallèle région sériele région parallèle région sériele<br />
processeur maître<br />
CPU1 CPU1 CPU1 CPU1<br />
CPU2<br />
CPU3<br />
CPU4<br />
CPU5<br />
CPU6<br />
CPU7<br />
CPU8<br />
CPU2<br />
CPU3<br />
CPU4<br />
CPU5<br />
CPU6<br />
CPU7<br />
CPU8
CPU1<br />
Cache 1<br />
Mémoire Partagée<br />
CPU2<br />
Cache 2<br />
Mémoire<br />
CPU3<br />
Cache 3<br />
CPU4<br />
Cache 4
CPU1<br />
Cache 1<br />
Mem1<br />
CPU2<br />
Cache 2<br />
Mémoire Distribuée<br />
CPU3<br />
Cache 3<br />
CPU4<br />
Cache 4<br />
Mem2 Mem3 Mem4
Ordinateur vectoriel: <strong>Le</strong> compilateur vectorise les boucles<br />
automatiquement.<br />
Ordinateur parallèle: <strong>Le</strong> programme doit contenir <strong>de</strong>s<br />
instructions pour parallèliser les boucles.<br />
Systèmes avec mémoire partagée: OpenMP<br />
Systèmes avec mémoire distribuée: MPI
Exemple: parallèlisation d’une boucle avec OpenMP, sur 8 processeurs.<br />
dimension a(1024), b(1024), c(1024)<br />
!$omp parallel do shared(a,b,c) private(i,x,y,z)<br />
do i=1,1024<br />
x=a(i)<br />
y=b(i)<br />
z=x**2+y**2<br />
if(z.gt.10.) then<br />
c(i)=x<br />
else<br />
c(i)=y<br />
endif<br />
enddo<br />
!$omp end parallel do
processeur 1 processeur 2 processeur 3<br />
do i=1,128<br />
x=a(i)<br />
y=b(i)<br />
z=x**2+y**2<br />
if(z.gt.10.) then<br />
c(i)=x<br />
else<br />
c(i)=y<br />
endif<br />
enddo<br />
do i=385,512<br />
x=a(i)<br />
y=b(i)<br />
z=x**2+y**2<br />
if(z.gt.10.) then<br />
c(i)=x<br />
else<br />
c(i)=y<br />
endif<br />
enddo<br />
do i=769,896<br />
x=a(i)<br />
y=b(i)<br />
z=x**2+y**2<br />
if(z.gt.10.) then<br />
c(i)=x<br />
else<br />
c(i)=y<br />
endif<br />
enddo<br />
do i=129,256<br />
x=a(i)<br />
y=b(i)<br />
z=x**2+y**2<br />
if(z.gt.10.) then<br />
c(i)=x<br />
else<br />
c(i)=y<br />
endif<br />
enddo<br />
do i=513,640<br />
x=a(i)<br />
y=b(i)<br />
z=x**2+y**2<br />
if(z.gt.10.) then<br />
c(i)=x<br />
else<br />
c(i)=y<br />
endif<br />
enddo<br />
do i=897,1024<br />
x=a(i)<br />
y=b(i)<br />
z=x**2+y**2<br />
if(z.gt.10.) then<br />
c(i)=x<br />
else<br />
c(i)=y<br />
endif<br />
enddo<br />
do i=257,384<br />
x=a(i)<br />
y=b(i)<br />
z=x**2+y**2<br />
if(z.gt.10.) then<br />
c(i)=x<br />
else<br />
c(i)=y<br />
endif<br />
enddo<br />
processeur 4 processeur 5 processeur 6<br />
processeur 7 processeur 8<br />
do i=641,768<br />
x=a(i)<br />
y=b(i)<br />
z=x**2+y**2<br />
if(z.gt.10.) then<br />
c(i)=x<br />
else<br />
c(i)=y<br />
endif<br />
enddo
Parallèlisation <strong>de</strong>s boucles:<br />
• Dans le cas <strong>de</strong> boucles multiples, c’est la boucle<br />
extérieure qui se parallèlise.<br />
• Non-parallèlisation: on retrouve en gros les mêmes<br />
critères que la non-vectorisation.<br />
a) Input/Output<br />
b) Interruption prématurée<br />
c) Indices compliqués<br />
Exceptions: o Sélection<br />
o Fonctions externes et sousroutines (dangereux)
Autres concepts:<br />
• Décomposition du domaine.<br />
• Balance <strong>de</strong> charge.<br />
• Relation d’échelle (scalability).
5) Résumé et Conclusion<br />
• <strong>Le</strong> <strong>calcul</strong> <strong>de</strong> <strong>haute</strong> <strong>performance</strong> est <strong>de</strong>venu un outil indispensable <strong>de</strong><br />
l’astrophysique théorique.<br />
• De grands progrès ont été réalisés durant les 30 <strong>de</strong>rnières années.<br />
o Sofware (meilleurs algorithmes)<br />
o Hardware (processeurs plus rapi<strong>de</strong>s, nouvelles architectures)<br />
• <strong>Le</strong>s ordinateurs sériels ne sont pratiquement plus utilisés pour le CHP<br />
(les problèmes sont <strong>de</strong>venus trop gros).<br />
• <strong>Le</strong>s ordinateurs vectoriels ne sont plus très utilisés.<br />
• Présent: ordinateurs parallèles avec mémoire partagée ou distribuée<br />
• Avenir (?): o <strong>Le</strong>s mémoires partagées seront moins utilisées (les problèmes<br />
seront trop gros).<br />
o Ordinateur parallèle avec processeurs vectoriels (existe déjà).<br />
o Ordinateur parallèle avec custom hardware (GRAPE).