Conséquence de l`architecture mémoire

publicité
Conséquence de
l’architecture
mémoire
Adrien Poteaux et
Alexandre
Sedoglavic
Introduction
Conséquence sur
le code
Conséquence de l’architecture mémoire
Adrien Poteaux et Alexandre Sedoglavic
Licence 3 info
Université de Lille 1
Janvier 2015
Conséquence de
l’architecture
mémoire
Adrien Poteaux et
Alexandre
Sedoglavic
Mesures de performances
I
Introduction
temporelles Instructions per second (ips) ; cette mesure est
dépendante du programme et du langage.
On estime que un quadricœur intel core 2 cadencé
à 2.5GHz est de l’ordre de 40 gips.
Conséquence sur
le code
I
FLoating-point OPeration (flop) ; cette mesure permet
des comparaison mais ne prend en compte que les
instructions de calculs.
Les microprocesseurs actuels réalisent 4 flops par cycle
d’horloge.
I
FLoating-point Operation Per Second (flops).
Un cœur cadencé à 2.5GHz présente donc une
performance théorique de 10 milliards de flops
(i.e. 10). gflops.
Un quadicœur dispose donc théoriquement
de 40 gflops.
Conséquence de
l’architecture
mémoire
Adrien Poteaux et
Alexandre
Sedoglavic
Mesures de performances
I
Introduction
Conséquence sur
le code
I
spatiales la latence est le temps nécessaire à des données pour
passer de la source à la destination (on considère ici la
latence des mémoires ram).
la bande passante est le débit de transmission des
données entre la source et la destination. La bande
passante est donc le produit :
I
I
I
I
de la latence des mémoire ;
de la quantité de données transmise par cycle (2 mots
de 32 lettres dans le cas des ddr ram ;
de la taille du bus (64 lettres actuellement) ;
nombre de bus — canaux — irriguant le processeur.
Ainsi, une architecture abordable actuellement est
constitué de 2 canaux de 64 lettres liant le processeur à
des mémoires ddr2 ram cadencés à 400mhz ; cela
correspond à une bande passante de 12.8 gb par
seconde.
Conséquence de
l’architecture
mémoire
Adrien Poteaux et
Alexandre
Sedoglavic
Introduction
Conséquence sur
le code
Constats
La plupart des applications n’exploitent que 10% des
performances optimales du processeurs.
I Les limitations principale sont dues à la circulation des
données en mémoire.
Écart de performance entre processeurs et mémoire :
I
Les performances de la bande passante progresse plus vite —
Conséquence de
l’architecture
mémoire
Adrien Poteaux et
Alexandre
Sedoglavic
Hiérarchie de mémoire
Partant du constat de localité :
I
spatiale i.e. les prochaı̂nes données à utiliser sont
souvent proches des données actuelles ;
I
temporelle i.e. une donnée actuellement utilisée à de
grande chance d’être réutilisée,
Introduction
Conséquence sur
le code
l’architecture implante un mécanisme de tampon
type
latence
taille
cache L1
10−9 s
kilo-octets
cache L2
10−8 s
méga-octets
ram
10−7 s
giga-octets
disque
10−2 s
tera-octets
afin d’améliorer les performances moyennes.
La taille est inversement proportionnelle à la latence (pour
être plus rapide, il faut partager le même support physique
ce qui limite la taille).
Conséquence de
l’architecture
mémoire
Adrien Poteaux et
Alexandre
Sedoglavic
Introduction
L’architecture du système de mémoire cherche à
I
Conséquence sur
le code
économiser les accès mémoire en stockant les données
dans de petite mémoire rapide (cache) et en réutilisants
ces dernières :
cela implique une localité temporelle de l’utilisation des
données dans un code.
I
utiliser la bande passante pour stocker des blocks (page
du cache) de données ;
cela implique une localité spatiale de l’utilisation des
données dans un code.
Conséquence de
l’architecture
mémoire
Adrien Poteaux et
Alexandre
Sedoglavic
Introduction
Conséquence sur
le code
l’outil Pahole
On peut désassembler le code pour avoir de l’information :
struct matrice{
int tab[1024][1024] ;
} mat ;
int main(void){
mat.tab[0][0] = 1 ;
return 0 ;
}
// gcc -g -o mat mat.c ; pahole mat
struct matrice {
int tab[1024][1024];
/*
0 4194304 */
/*- cacheline 65536 boundary (4194304 bytes) -*/
/* size: 4194304, cachelines: 65536, members: 1 */
};
Conséquence de
l’architecture
mémoire
Fusion de boucles
Adrien Poteaux et
Alexandre
Sedoglavic
Introduction
Conséquence sur
le code
Algorithm 1 Favoriser la localité
1:
2:
3:
4:
5:
6:
7:
double a[n],b[n],c[n] ;
for i=1 to n do
b[i] = a[i] + 1.0 ;
end for
for i=1 to n do
c[i] = b[i] *4.0 ;
end for
double a[n],b[n],c[n] ;
for i=1 to n do
3:
b[i] = a[i] + 1.0 ;
4:
c[i] = b[i] *4.0 ;
5: end for
1:
2:
Conséquence de
l’architecture
mémoire
Intervertion de boucle
Adrien Poteaux et
Alexandre
Sedoglavic
Introduction
Conséquence sur
le code
Algorithm 2 Parcours de matrices
1:
2:
3:
4:
5:
6:
7:
double sum ;
double a[n,n] ;
for j=1 to n do
for i=1 to n do
sum+= a[i,j] ;
end for
end for
1:
2:
3:
4:
5:
6:
7:
double sum ;
double a[n,n] ;
for i=1 to n do
for j=1 to n do
sum+= a[i,j] ;
end for
end for
Conséquence de
l’architecture
mémoire
Insertion d’espace
Adrien Poteaux et
Alexandre
Sedoglavic
Introduction
Conséquence sur
le code
Algorithm 3 Éviter de mélanger écriture et lecture
1:
2:
3:
4:
5:
double sum ;
double a[n],b[n] ;
for i=1 to n do
sum+= a[i]*b[j] ;
end for
1:
2:
3:
4:
5:
6:
7:
double sum ;
double a[n] ;
double pad[x] ;
double b[n] ;
for i=1 to n do
sum+= a[i]*b[j] ;
end for
Conséquence de
l’architecture
mémoire
Adrien Poteaux et
Alexandre
Sedoglavic
Introduction
Conséquence sur
le code
Construction de blocs
Une matrice carrée A de taille n × n est stockée
unidimensionnellement en mémoire :
I
à la fortran : A(i, j) = A + i + j × n (par colonne) ;
I
à la c : A(i, j) = A + i × n + j (par ligne) ;
Algorithm 4 Blocs et transposition
1: double a[n,n],b[n,n] ;
2: for i=1 to n do
3:
for j=1 to n do
4:
a[i,j] = b[j,i] ;
5:
end for
6: end for
1: double a[n,n],b[n,n] ;
2: for ii=1 to n by B do
3:
for jj=1 to n by B do
4:
for i=ii to min(ii+B-1,n)
do
5:
6:
7:
8:
9:
10:
for j=jj to min(jj+B1,n) do
a[i,j] = b[j,i] ;
end for
end for
end for
end for
Conséquence de
l’architecture
mémoire
Adrien Poteaux et
Alexandre
Sedoglavic
Introduction
Conséquence sur
le code
Représentation de tableaux
Dans la déclaration int i,j ;, les variables i et j peuvent
être dans une ligne de cache différente. Pour s’assurer qu’elle
soit dans la même ligne, on peut utiliser :
__declspec(align(16)) struct {int i,j ;} sub ;
// __declspec(align(16)) permet d’agir
// sur l’alignement
Algorithm 5 Différentes variantes
1: double a[n],b[n] ;
1: double ab[n][2] ;
1: struct {
2: double a ;
3: double b ;
4: } ab[n] ;
Conséquence de
l’architecture
mémoire
Adrien Poteaux et
Alexandre
Sedoglavic
Introduction
Conséquence sur
le code
C et la manipulation de cache
Le fichier d’entête emmintrin.h permet d’accéder à des
instructions assembleur de type Single Instruction on
Multiple Data (simd) et Streaming SIMD Extensions (sse) ;
par exemple :
void __mm_prefetch(char const *p, int);
/* 0 - read only, 1 - rw */
// charge une ligne de cache depuis l’adresse p
void __mm__clflush(void const *p) ;
// la ligne de cache contenant p est vid\’ee
void __mm__sfence(void) ;
// garantie que chaque op\’eration m\’emoire
// pr\’ec\’edent l’appel est visible globalement
void __mm__stream_ps(float *p, __m128 a) ;
// charge la donn\’ee de a dans l’adresse p
// sans passer par le cache
Conséquence de
l’architecture
mémoire
Adrien Poteaux et
Alexandre
Sedoglavic
Introduction
Conséquence sur
le code
Modifier une ligne de cache
Le code suivant :
include <emmintrin.h>
void setbytes(char *p, int c){
__m128i i = _mm_set_epi8(c, c, c, c,
c, c, c, c,
c, c, c, c,
c, c, c, c);
_mm_stream_si128((__m128i *)&p[0], i);
_mm_stream_si128((__m128i *)&p[16], i);
_mm_stream_si128((__m128i *)&p[32], i);
_mm_stream_si128((__m128i *)&p[48], i);
}
permet de mettre à c tout les octets de la ligne de cache
contenant l’adresse p (supposée alignée). La fonction
memset utilise ce type d’astuce.
Conséquence de
l’architecture
mémoire
Exemple d’améliorations
Adrien Poteaux et
Alexandre
Sedoglavic
Nous allons utiliser l’algorithme classique suivant :
Introduction
Conséquence sur
le code
Algorithm 6 Multiplication naı̈ve de matrice
1: int i,j,k ;
2: int a[n],b[n],res[n] ;
3: for i=1 to n do
4:
for j=1 to n do
5:
for k=1 to n do
6:
res[i][j] += a[i][k]*b[k][j] ;
7:
end for
8:
end for
9: end for
Conséquence de
l’architecture
mémoire
Adrien Poteaux et
Alexandre
Sedoglavic
Introduction
Conséquence sur
le code
On suppose disposer de la macro CLS à la compilation
(-DCLS=$(getconf LEVEL1_DCACHE_LINESIZE)).
#define SM (CLS / sizeof (int))
for (i = 0; i < N; i += SM)
for (j = 0; j < N; j += SM)
for (k = 0; k < N; k += SM)
rres = &res[i][j] ;
rmul1 = &mul1[i][k] ;
for (i2 = 0 ; i2 < SM;
++i2, rres += N, rmul1 += N)
for (k2 = 0, rmul2 = &mul2[k][j];
k2 < SM; ++k2, rmul2 += N)
for (j2 = 0; j2 < SM; ++j2)
rres[j2] += rmul1[k2] * rmul2[j2];
Téléchargement