Optimisation du code SCC GCC
Ce post est étroitement liée à un autre que j'ai posté il y a quelques jours. Cette fois, j'ai écrit un code simple qui ajoute simplement une paire de matrices d'éléments, multiplie le résultat par les valeurs dans un autre tableau et les stocke dans une suite de tableau, toutes les variables à virgule flottante double précision tapé.
J'ai fait deux versions du même code: l'une avec le jeu d'instructions SSE, au moyen d'appels et un autre sans eux j'ai ensuite compilé avec gcc et-O0 niveau d'optimisation. Je les écris ci-dessous:
//SSE VERSION
#define N 10000
#define NTIMES 100000
#include <time.h>
#include <stdio.h>
#include <xmmintrin.h>
#include <pmmintrin.h>
double a[N] __attribute__((aligned(16)));
double b[N] __attribute__((aligned(16)));
double c[N] __attribute__((aligned(16)));
double r[N] __attribute__((aligned(16)));
int main(void){
int i, times;
for( times = 0; times < NTIMES; times++ ){
for( i = 0; i <N; i+= 2){
__m128d mm_a = _mm_load_pd( &a[i] );
_mm_prefetch( &a[i+4], _MM_HINT_T0 );
__m128d mm_b = _mm_load_pd( &b[i] );
_mm_prefetch( &b[i+4] , _MM_HINT_T0 );
__m128d mm_c = _mm_load_pd( &c[i] );
_mm_prefetch( &c[i+4] , _MM_HINT_T0 );
__m128d mm_r;
mm_r = _mm_add_pd( mm_a, mm_b );
mm_a = _mm_mul_pd( mm_r , mm_c );
_mm_store_pd( &r[i], mm_a );
}
}
}
//NO SSE VERSION
//same definitions as before
int main(void){
int i, times;
for( times = 0; times < NTIMES; times++ ){
for( i = 0; i < N; i++ ){
r[i] = (a[i]+b[i])*c[i];
}
}
}
Lors de la compilation avec -O0, gcc fait usage de XMM/registres MMX et SSE intstructions, si ce n'est précisément compte tenu de l'-mno-ess (et d'autres). J'ai inspecté le code assembleur généré pour le deuxième code, et j'ai remarqué qu'il rend l'utilisation de movsdaddsd et mulsd instructions. Il rend l'utilisation des instructions SSE, mais seulement de ceux qui utilisent la partie la plus basse de l'registres, si je ne me trompe pas. Le code assembleur généré pour la première C code fait usage, comme prévu, de la décennie et mulpd instructions, mais une assez grande assemblée de code a été généré.
De toute façon, le premier code devrait obtenir un meilleur résultat, pour autant que je sais, de SIMD paradigme, car à chaque itération de deux valeurs de résultat sont calculés. Encore que, le deuxième code effectue quelque chose comme 25% plus rapide que le premier. J'ai aussi fait un test avec une seule des valeurs de précision et d'obtenir des résultats similaires. Quelle est la raison?
source d'informationauteur Genís
Vous devez vous connecter pour publier un commentaire.
Vectorisation dans GCC est activé lors de l'
-O3
. C'est pourquoi lors de-O0
vous ne voyez que l'ordinaire scalaire instructions SSE2 (movsd
addsd
etc). À l'aide de GCC 4.6.1 et votre deuxième exemple:et de la compilation avec
gcc -S -O3 -msse2 sse.c
produit de la boucle interne de la en suivant les instructions, ce qui est assez bon:Comme vous pouvez le voir, avec la vectorisation permis de GCC émet code pour effectuer deux itérations de boucle en parallèle. Il peut être amélioré, que ce code utilise les 128 bits des registres SSE, mais il peut utiliser la totalité de 256 bits YMM registres, par l'activation de l'AVX encodage du jeu d'instructions SSE (si disponible sur la machine). Ainsi, la compilation du même programme avec
gcc -S -O3 -msse2 -mavx sse.c
donne de la boucle interne:Noter que
v
en face de chaque instruction et que les instructions d'utilisation de 256 bits YMM registres, quatre itérations de la boucle d'origine sont exécutés en parallèle.Je tiens à exprimer chill répondre et attirer votre attention sur le fait que GCC ne semble pas être capable de faire la même utilisation intelligente de l'AVX instructions lors de l'itération en arrière.
Il suffit de remplacer la boucle interne dans le froid de l'exemple de code avec:
GCC (4.8.4) avec des options
-S -O3 -mavx
produit: