You are on page 1of 17

PRÀCTICA DE PCA

2007/2008 Q1
mpeg2encode

Jordi Castells Sala


Francesc Montserrat Carvajal
Taula de Continguts

1. Metodologia d'optimització 3
1.1.Scripts d'automatització 3
1.2.Procés seguit 3
1.3.Jocs de Proves 4
2. Entorn hardware 5
2.1.CPU 5
2.2.Memòria 6
3. Entorn Software 7
3.1.Compiladors 7
3.2.Kernels 7
4. Rendiment Inicial del Codi 8
5. Estudi de les Optimitzacions 9
5.1.Vectorització a dist1 9
5.2.Bit hacking a dist1 14
5.3.Exploració de l'espai d'Unrolling 15
5.4.Partició de dist1 17
5.5.Unrolling total de dist1_8 17
6. Conclusions 18
Metodologia d'optimització

Scripts d'automatització
Bàsicament s'han usat quatre scripts per automatitzar les tasques.

[time.pl]
El primer d'aquests s'usa per executar vàries vegades el programa codificant el vídeo en questió i
obtenir-ne la mitjana d'Elapsed time de les execucions que hagin obtingut un %CPU > 92, ja que si
no fos així es podrien obtindre dades esbiaixades.

[make.sh]
La necessitat d'anar recompilant el programa contínuament i el fet de que el Makefile està
relativament “lluny” del directori de treball amb els vídeos ens fa usar aquest script. Que
simplement canvia de directori, fa make i retorna al directori. Útil per treballar en una sola consola.

[unrolling*.sh]
Provar els diferents graus d'unrolling requereix temps. Per no perdre'l davant la pantalla esperant
que acabi i posar el següent unrolling, aquests scripts (són scripts dedicats, no genèrics)
s'encarreguen d'anar canviant el fitxer amb l'unrolling desitjat, executar time.pl 10 vegades i
guardar-ne el resultat en un fitxer.

[doublevideo.sh]
Executa mplayer/xine (segons el quees necessiti) amb el fitxer obtingut amb la última optimització
i el fitxer obtingut originalment (decodificant i codificant). És un script fet ràpid, simplement agafa
els 2 paràmetres d'entrada i executa mplayer/xine dues vegades concurrentment.
Havíem pensat d'usar la mida del fitxer de sortida, però en vàries proves a demostrat ser un
paràmetre no vàlid per evaluar la correctesa del nostre codi, així que finalment hem preferit aquest
métode més visual per comprovar la correctesa del vídeo que no pas mirar la mida del fitxer de
sortida (per exemple).

Procés Seguit
El procés seguit per optimitzar és el clàssic, i que hem trobat més adient. En alguns casos ha estat
lleugerament diferent (ús de Oprofile per exemple).

Compilar el programa en el seu estat actual.


Usar time.pl per aproximar-nos a el seu elapsed time mitjà amb vàries mostres.
Compilar el programa amb les opcions de profiling.
Executar i gprof per comprovar.
Buscar on optimitzar i decidir optimització.
Guardar fitxer original (nomfitxer_orig.c).
Aplicar optimitzacions.
Recompilar amb Optimitzacions.
Tornar a usar time.pl.
Contrastar els resultats obtinguts.
Comprovar si la qualitat del vídeo de sortida és acceptable.
Aplicar optimització si tots els passos han estat correctes.
Tornar al pas 1.
Jocs de Proves

A part dels 3 vídeos originals de la pràctica (02.tiger.mpg, Daddariokiss.mpg i turn4.mpg) s'ha


buscat un altre vídeo amb moviments més bruscos. Ja que amb aquests 3 vídeos la funció dist1 (la
funció de més pes del programa) semblava no tenir una utilitat clara tot i ocupar un 70% del temps
de programa. Si s'eliminava dist1 del programa el vídeo seguia apareixent amb una qualitat
raonable. El vídeo emprat per la prova del bon funcionament de dist1 és un vídeo d'un concert de
Deep Purple, en aquest vídeo en concret, al ser codificat sense dist1, apareixen una espècie de salts
extranys que no permeten veure el vídeo de manera correcta. Per tant, aquest vídeo de Deep Purple
serà el vídeo usat en les proves per saber si la optimització ha estat correcta.
Entorn Hardware

CPU:
/proc/cpuinfo (Core Duo i Centrino)

processor :0
vendor_id : GenuineIntel
cpu family :6
model : 15
model name : Intel(R) Core(TM)2 CPU T5500 @ 1.66GHz
stepping :6
cpu MHz : 1000.000
cache size : 2048 KB
physical id :0
siblings :2
core id :0
cpu cores :2
fdiv_bug : no
hlt_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level : 10
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca
cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe
nx lm constant_tsc pni monitor ds_cpl est tm2 ssse3 cx16 xtpr
lahf_lm
bogomips : 3328.79
clflush size : 64

Com es pot veure en el /proc/cpuinfo és un Core Duo. Així que tenim dos processadors amb
aquestes característiques.
S'ha provat en un Core duo normal, un core duo Centrino i també es va provar en un athlon-xp
2000+, però aquest no tenia el set d'instruccions SSE2, per lo qual algunes optimitzacions no són
possibles. Tot seguit la informació de l'Athlon-xp.

/proc/cpuinfo (Athlon-xp)

processor :0
vendor_id : AuthenticAMD
cpu family :6
model :8
model name : AMD Athlon(tm) XP 2000+
stepping :1
cpu MHz : 1679.152
cache size : 256 KB
fdiv_bug : no
hlt_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level :1
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca
cmov pat pse36 mmx fxsr sse syscall mmxext 3dnowext 3dnow ts
bogomips : 3359.07
clflush size : 32

Memòria
/proc/meminfo

MemTotal:2066556 kB (Core Duo i Centrino)


MemTotal:710304 kB (Athlon-xp)
Entorn Software (provat en diferents plataformes)

Compilador
Sistema versió de gcc
Core Duo Centrino gcc (GCC) 4.1.1 (Gentoo 4.1.1-r3)
Core Duo gcc (GCC) 4.1.3 20070929 (prerelease) (Ubuntu
4.1.2-16ubuntu2)
Athlon-xp 2000+ gcc (GCC) 4.1.2

Kernel usat (uname -r)

Sistema kernel
Core Duo Centrino 2.6.21-gentoo-r4
Core Duo 2.6.20-16-generic
Athlon-xp 2000+ 2.6.21.5
Rendiment inicial del codi

Executem time.pl en cada una de les màquines per obtenir-ne el rendiment inicial del codi, sense
cap tipus d'optimització.

En la següent taula, es veu la màquina on s'ha realitzat la prova. Els flags "CFLAGS" del
compilador gcc determinats en el seu makefile i el Elapsed time mitjà obtingut en l'optimització
del vídeo dels tigres. Les dades d'elapsed time usades en el document seràn les d'aquest vídeo, els
altres vídeos seràn usats per comprovació de correcte codificació. Un cop vist que el resultat és
correcte es calcularà el temps amb el 02.tiger.mpg.

Màquina CFLAGS Elapsed time mitjà


Core Duo Centrino -O3 0:43.837
-Wall
-march=pentium-m
Core Duo -O3 0:43.418
-Wall
-march=pentium-m
Athlon-xp 2000+ -O3 0:73.45
-Wall
-march=athlon-xp

Hi han hagut forces dubtes sobre quin era el march=cputype correcte per usar amb els core Duo. Al
manual de gcc no es deixa del tot clar. Al web de gcc es pot trobar un thread on es parla de la
situació i un bug obert a bugzilla:
http://gcc.gnu.org/ml/gcc/2006-12/msg00010.html (thread)
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=30040 (bug)

La decisió correcta, segons la situació vista està entre pentium-m i prescott. Així que la solució en
el nostre cas ha estat testejar pels dos, i comprovar quin era amb el que s'obtenia un elapsed time
menor sense cap optimització, aquest ha estat el punt de partida. En les proves, el march=pentium-
m n'ha sortit guanyandor.
Estudi de les diferents optimitzacions

Vectorització a dist1

Al executar gprof sobre el codi original, ens descobreix que una funció anomenada dist1 es
"menja" el 77.32% del programa en els CoreDuo i un 74.42% en l'athlon-xp. Això ens dóna una
primera pista d'on s'ha d'optimitzar. Es busca la funció en concret (grep dist1 *), que resulta estar a
motion.c. I es veu que almenys la 1ª part d'aquesta funció és vectoritzable.

%time cumulative self seconds calls self s/call total s/call name
seconds
77.33 31.95 31.95 132468929 0.00 0.00 dist1
6.33 34.56 2.62 958500 0.00 0.00 fullsearch
6.03 37.05 2.49 874800 0.00 0.00 fdct
1.84 37.81 0.76 584412 0.00 0.00 quant_non_intra

Codi Original Codi Optimitzat


unsigned char *p1,*p1a,*p2; __m128i a,b,result;
//(...) a = _mm_loadu_si128((__m128i*)blk1);
p1=blk1;p2=blk2; b = _mm_loadu_si128((__m128i*)blk2);
//(...) if(!hx && !hy)
if(!hx && !hy) for (j=0; j<h; j++){
for (j=0; j<h; j++){ result = _mm_sad_epu8(a,b);
if ((v = p1[0] - p2[0])<0) v = -v; s+= v; s+=_mm_extract_epi16(result,0);
if ((v = p1[1] - p2[1])<0) v = -v; s+= v; s+=_mm_extract_epi16(result,0);
if ((v = p1[2] - p2[2])<0) v = -v; s+= v; //Altres coses
if ((v = p1[3] - p2[3])<0) v = -v; s+= v; }
//(...) fins px[15](...)
//Altres coses
}

S'han hagut d'usar instruccions de loadu per problemes d'alineament. La idea de buscar l'origen de
blk1 i blk2 i alinear-los ha existit, però ha estat frustrada pel tipus de crides a la funció dist1.
La instrucció _mm_sad_epu8 ens agafa char a char dels registres vectorials a i b, en fa valor
absolut i els suma (el que veiem que es fa al codi original amb els vectors de chars p1 i p2).
Finalment, tal i com deixa les dades la instrucció _mm_sad_epu8 s'usa la instrucció extract per
obtenir-ne les dades.

Evaluació:

Màquina E.T de partida E.T post_OPT Speed Up


Core Duo Centrino 43.837s 27.43s 43.837/27.43 ~= 1.6x
Core Duo 43.418s 21.842s 42.40/22.313 ~= 2x
Athlon-xp 2000+ 73,45s - -

En la taula podem veure que l'Speed Up obtingut és considerable, 1,6x. Per tant aquesta
optimització entrarà dins el codi del prorgrama. Els resultats han estat reproduïbles en el Centrino i
en el Core Duo. Però no en l'Athlon-xp, ja que les instruccions usades són de SSE2, un set
d'instruccions que aquest processador no téincorporat.
Tot i així s'ha programat una versió semblant per l'Athlon-xp amb MMX i SSE, amb la mateixa
idea, però resultats molt diferents.

Codi Original Codi Optimitzat


unsigned char *p1,*p1a,*p2; __m64 c,d,res;
//(...) if (!hx && !hy)
p1=blk1;p2=blk2; for (j=0; j<h; j++){
//(...)
if(!hx && !hy) c=
for (j=0; j<h; j++){ _mm_set_pi8(p1[7],p1[6],p1[5],p1[4],p1[3],p1[2],p1[1],p
if ((v = p1[0] - p2[0])<0) v = -v; s+= v; 1[0]);
if ((v = p1[1] - p2[1])<0) v = -v; s+= v; d=
if ((v = p1[2] - p2[2])<0) v = -v; s+= v; _mm_set_pi8(p2[7],p2[6],p2[5],p2[4],p2[3],p2[2],p2[1],p
if ((v = p1[3] - p2[3])<0) v = -v; s+= v; 2[0]);
//(...) fins px[15](...) res = _mm_sad_pu8(c,d);
//Altres coses s+=_mm_extract_pi16(res,0);
} c=
_mm_set_pi8(p1[15],p1[14],p1[13],p1[12],p1[11],p1[10],
p1[9],p1[8]);
d=
_mm_set_pi8(p2[15],p2[14],p2[13],p2[12],p2[11],p2[10],
p2[9],p2[8]);
res = _mm_sad_pu8(c,d);
s+=_mm_extract_pi16(res,0);
//Altres coses
}

S'han barrejat instruccions de SSE i de MMX, ja que SSE no té el tipus d'instrucció de load que es
necessita per seguir la mateixa idea aplicada als Core Duo. En comptes d'això es fa un set de tots
els valors necessaris, una opció que com ja veurem és molt lenta. S'usa la instrucció _mm_sad_pu8
de SSE per fer el valor absolut i la suma. Vegem-ne els resultats.

Màquina E.T de partida E.T post_OPT Speed Up


Athlon-xp 2000+ 73.45s 71.33s 73.54/71.33 ~= 1.030x

Com es pot veure, no és una optimització molt gran. Aquest codi no ens aporta un gran avantatge,
per tant, no serà usat.

Després d'aquesta Optimització tornem a fer ús de gprof per comprovar com ha quedat tot:

%time cumulative self seconds calls self s/call total s/call name
seconds
61.15 13.60 13.60 132468929 0.00 0.00 dist1
12.23 16.32 2.72 958500 0.00 0.00 fullsearch
5.04 17.44 1.12 874800 0.00 0.00 fdct
4.90 18.53 1.09 145800 0.00 0.00 frame_ME

Veiem que el pes de la funció dist1 s'ha reduït dràsticament. Usant la llei d'Amdahl podem
estipular que tot i així, el pes d'aquesta és molt gran com per deixar d'optimitzar-la. La 2ª funció
més pesada "fullsearch" com a màxim ens donaria un SpeedUp de 1.13x (suposant que la
optimitzéssim fins l'infinit), per contra, si optimitzem la funció dist1 podríem arribar a un hipotètic
speed up de 2.5x. Així que seguirem amb la vectorització dels altres casos de dist1.

L'optimització dels if 2, 3 i 4 (numerats els 4 ifs de dist1 per ordre) no ha estat possible.
Analitzarem per sobre les idees que s'han provat per optimitzar però que no han funcionat.
El codi dels 3 if's és un codi molt semblant, vegem el codi de l'if1 i serà suficient,
Codi if1 (dist1)
for (i=0; i<16; i++)
{
v = ((unsigned int)(p1[i]+p1[i+1]+1)>>1) - p2[i];
if (v>=0)
s+= v;
else
s-= v;
}

Hem remarcat (unsigned int) perquè és la raó principal dels problemes de vectorització.

La idea inicial era usar un sad de la mateixa manera que en el 1r if de dist1, però p1 és un vector de
chars, els valors del qual passen a ser tractats com a unsigned int. És a dir, si tractem vectorialment
els valors és molt possible que algun valor es passi del rang dels chars obtinguent un resultat
incorrecte, cosa que no passa al tractar amb int's, ja que la suma de dos chars mai sobrepassarà la
capacitat d'un int.

Així doncs, intentarem tractar vectorialment amb int's, però ens trobem amb el següent problema.
En memòria tenim tot el vector p1 amb chars, que s'alineen a 1. Així que tenim (Cx és un char):

C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 C12 C13 C14 C15

Aquests 16 Chars són els que cabrien a un registre vectorial. Si carreguem el registre vectorial, els
int's que tractaríem són (Cada int està separat amb codi de color)

C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 C12 C13 C14 C15


INT1 INT2 INT3 INT4

Que resulta amb uns valors de int incorrectes pertractar. La idea és preparar el vector per poder fer
les instruccions p[i] + p[i+1] paralelament. Així que usaríem una màscara per obtenir sols el char
de menys pes de cada int (C3, C7, C11 i C15 en aquest cas), shiftar tot el vector “cap a la dreta” i
tornar a usar la màscara fins a obtenir

RV1:
0 0 0 C3 0 0 0 C7 0 0 0 C11 0 0 0 C15
INT1 INT2 INT3 INT4

RV2:
0 0 0 C2 0 0 0 C6 0 0 0 C10 0 0 0 C14
INT1 INT2 INT3 INT4

RV3:
0 0 0 C1 0 0 0 C5 0 0 0 C9 0 0 0 C13
INT1 INT2 INT3 INT4
RV4:
0 0 0 C0 0 0 0 C4 0 0 0 C8 0 0 0 C12
INT1 INT2 INT3 INT4

Així, que, un cop obtinguts aquests valors, sols cal sumar (també sumar 1 i restar p2) i fer sad
(deixar valor absolut i sumar).
RV4+RV3;
RV3+RV2;
RV2+RV1:
Finalment seria necessari shiftar “a l'esquerra” RV4 fins a obtenir

RVX:
0 0 0 C4 0 0 0 C8 0 0 0 C12 xxxx xxxx xxxx xxxx
INT1 INT2 INT3 INT4

Carregar C16 al char de menys pes de l'INT4 de RVX i fer la màscara necessària obtenint així:

RVX:
0 0 0 C4 0 0 0 C8 0 0 0 C12 0 0 0 C16
INT1 INT2 INT3 INT4

I ara ja podem fer la última suma

RVX + RV1;

Amb aquest procés ens assegurem

v = ((unsigned int)(p1[i]+p1[i+1]+1)>>1) – p2[i];

Del codi original. Tot seguit s'hauria de carregar un altre registre vectorial ambuns i sumar-lo a
cada un dels resultats obtinguts amb les sumes anteriors. Shiftar cada resultat una vegada “a la
dreta”, finalment carregar en diferents registres vectorials els valors de P2 (seguint un procés igual
a la càrrega dels valors de P1 passats a int) i restar-los a cada resultat. Un cop obtinguts aquests
resultats ja es pot fer sad sobre ells.

Aquest métode es presenta massa llarg i costós, i amb poca seguretat d'obtenir una millora (masses
instruccions vectorials que sols tracten 4 valors dels necessaris). Per tant, després de pensar aquesta
optimització i estar-ne valorant els costos optem per no utilitzar-la en el nostre codi i seguir un
altre camí (bit hacking i unrolling) per els if's 2, 3 i 4 de la funció dist1.

L'altre opció barallada ha estat carregar tots els valors un a un com es fa en el codi original. Però
carregar-los directament sobre un registre vectorial evitant tots els càlculs previs per preparar els
registres (màscares i shifts). Però aquest càlcul també ha resultat ser costós.
Bit Hacking (a dist1)

Veient que la Vectorització no era factible, s'opta per un altre tipus d'optimitzacions pel codi
Codi if1 (dist1)
for (i=0; i<16; i++)
{
v = ((unsigned int)(p1[i]+p1[i+1]+1)>>1) - p2[i];
if (v>=0)
s+= v;
else
s-= v;
}

Una combinació de bit hacking per eliminar els salts condicionals en el càlcul del valor absolut, i
unrolling per eliminar l'overhead que crea el bucle.

Codi Original Codi Optimitzat


if (v>=0) s+= v; mask = (v >> (sizeof(int)*8-1));
else s-= v; s+= (v ^ mask) - mask;

Després d'executar el codi optimitzat i comprovar que el vídeo de Deep Purple es veu correctament
procedim a mesurar-ne l'”Elapsed time”

Màquina E.T de partida E.T post_OPT Speed Up


Core Duo Centrino 43.837s 42,76s 43.837/42.76 ~= 1x
Core Duo 42.40s 41,96s 42.40/41,96 ~= 1x
Athlon-xp 2000+ 73.45s 69.585 73.45/69.585 ~= 1x

Com es pot comprovar, el bit hacking no influeix directament en el rendiment del programa, però
tampoc l'empitjora. De manera que aquesta optimització la incluirem en el nostre codi, ja que tot i
no incrementar-ne la velocitat d'execució, ens pot ser útil per facilitar futures optimitzacions.

Podem veure com en el Athlon-xp l'speedUp obtingut és molt semblant al dels altres dos
processadors. Però tot i així, la millora és molt més palpable en aquest processador, ja que redueix
el seu temps d'execució mitjà en quasi 4 segons. No serà un speedUp elevat, però 4 segons són
significatius.
Exploració de l'espai d'Unrolling

Com altres moments del codi. Seprarem l'unrolling en dues parts. L'unrolling usat a l'if1 de dist1 i
l'unrolling usat a els if 2, 3 i 4 de dist1. Vegem primer els resultats obtinguts amb diferents nivells
d'unrolling a l'if1 de dist1.

02.tiger DaddarioKiss
32,5 2
30
1,75
27,5
25 1,5
22,5
20 1,25
17,5
1
15
12,5 0,75
10
7,5 0,5
5
0,25
2,5
0 0
No Unrolling Unrolling 4 Unrolling 8 Unrolling 16 No Unrolling Unrolling 4 Unrolling 8 Unrolling 16

Elapsed Time Elapsed Time

Turn4 Deep Purple


1,8 25

1,6 22,5

1,4 20
17,5
1,2
15
1
12,5
0,8
10
0,6
7,5
0,4 5
0,2 2,5
0 0
No Unrolling Unrolling 4 Unrolling 8 Unrolling 16 No Unrolling Unrolling 4 Unrolling 8 Unrolling 16

Elapsed Time Elapsed Time

Tots els gràfics són respecte les dades obtingudes al Core Duo Centrino

Veient els 4 gràfics a simple vista podem extreure que l'impacte de l'unrolling no és molt gran en
cap dels jocs de proves utilitzats. Tot i així, podem apreciar que no usar unrolling i usar unrolling
de 8 són les millors opcions en tots els casos tractats. Com que en la majoria de casos ens trobem
que no usar unrolling és un xic més ràpid que usar unrolling de 4, no aplicarem cap tipus
d'unrolling al 1r if de la funció dist1.
Cal veure are els canvis produïts pels diferents nivells d'unrolling a la resta de funció dist1. És a
dir, als if's 2, 3 i 4.

02.tiger DaddarioKiss
27,5 1,6
25
1,4
22,5
20 1,2

17,5 1
15
0,8
12,5
10 0,6

7,5 0,4
5
0,2
2,5
0 0
No Unrolling Unrolling 4 Unrolling 8 Unrolling 16 No Unrolling Unrolling 4 Unrolling 8 Unrolling 16

Elapsed Time Elapsed Time

Turn4 Deep Purple


1,6 22,5

1,4 20

17,5
1,2
15
1
12,5
0,8
10
0,6
7,5
0,4
5
0,2 2,5

0 0
No Unrolling Unrolling 4 Unrolling 8 Unrolling 16 No Unrolling Unrolling 4 Unrolling 8 Unrolling 16

Elapsed Time Elapsed Time

Tornem a observar uns canvis molt lleugers en el comportament del programa. Utilitzar aquests
graus d'unrolling simplement serviria per augmentar la mida del codi del programa. Com que no
ens aporten cap millora significativa deixarem dist1 sense unrolling.

La conclusió que obtenim al haver provat diferents graus d'unrolling en les diferents parts de la
rutina de més pes de mpeg2encoder és que augmentar el nivell d'unrolling no ens produeix cap
speedup significatiu, ni ens aporta cap possibilitat com ho ha fet per exemple el bit hacking.
Partició de dist1 (Rutina Massa genèrica)

Observant dist1, el seu funcionament i la seva capçalera, veiem que existeix un paràmetre h que en
tots els casos sols pot ser o bé 8 o bé 16. En comptes de tenir un dist1 genèric per aquests casos
hem decidit partir-lo en dos: dist1_8 i dist1_16. Per facilitar-ne la comprensió i poder buscar altres
optimitzacions amb més facilitat.

Capçalera Original
static int dist1 _ANSI_ARGS_((unsigned char *blk1, unsigned char *blk2,
int lx, int hx, int hy, int h, int distlim));

Capçaleres Noves
static int dist1_8 _ANSI_ARGS_((unsigned char *blk1, unsigned char *blk2,
int lx, int hx, int hy, int distlim));

static int dist1_16 _ANSI_ARGS_((unsigned char *blk1, unsigned char *blk2,


int lx, int hx, int hy, int distlim));

Com es pot veure, les dues noves funcions tenen un argument menys. La variable h passa a ser una
constant i així facilitem al compilador la seva tasca de trobar altres optimitzacions.

Per poder aplicar-la, hem hagut de partir també fullsearch, ja que les dues tenien h com a
paràmetre.

Amb aquesta optimització no esperem obtenir grans resultats, simplement facilitar la futura
optimització.

Després d'aplicar-la, gprof ens dóna el següent resultat:

%time cumulative self seconds calls self s/call total s/call name
seconds
36.53 8.19 8.19 87798583 0.00 0.00 dist1_8
25.65 13.94 5.75 44670346 0.00 0.00 dist1_16

I l'speed Up obtingut és de 1x. (És a dir, es queda igual)

Unrolling total de dist1_8

S'ha provat de desenrotllar tot dist1_8. Amb grans pérdues de velocitat.


CONCLUSIONS

Després de totes les optimitzacions provades sembla ser que la sort ens va somriure en un primer
moment amb la vectorització a dist1 i ens ha anat abandonant en les següents optimitzacions.

En el codi final, la optimització mare és aquesta vectorització amb la qual hem obtingut un
speedUp de fins a 2x en el Core Duo i 1,6x en el Core Duo Centrino. Totes les altres
optimitzacions no han obtingut un speedUp semblant ni han millorat l'execució del codi en quant a
velocitat.

Tot i això hi han altres optimitzacions remarcables, ja que si bé no han millorat el temps
d'execució, tampoc l'han empitjorat de manera exagerada, i són optimitzacions que poden afavorir
una futura optimització que nosaltres no haguem pogut tenir en compte. Aquestes optimitzacions
són el bit hacking realitzat per canviar el càlcul del valor absolut que es realitzava amb salts
condicionals. I el canvi de dist1 en dues rutines per separat, també per afavorir altres
optimitzacions.
El bit hacking ha resultat especialment útil en l'Athlon, ons no ens ha donat un speedUp elevat,
però si ens ha reduït en 4segons l'execució del programa.

Les altres optimitzacions han resultat ser un fracàs, ja sigui per dificultat en la seva implementació
i posterior mal funcionament, o per la feina que porten pels resultats pobres.

Cal citar que ha estat especialment difícil els càlculs d'Elapsed Time, en l'Athlon-xp, ja que aquest
fluctuava molt de valors i necessitava tot ser executat en el màxim aïllament possible (cap tipus de
programa obert, ni l'entorn gràfic X11 corrent).

Així doncs, finalment els speedUps Obtinguts són:

Màquina E.T de partida E.T post_OPT Speed Up


Core Duo Centrino 43.837s 27.43s 43.837/27.43 ~= 1.6x
Core Duo 42.40s 22.313s 42.40/22.313 ~= 2x
Athlon-xp 2000+ 73.45s 69.585 74.5/69.585 ~= 1.05x

Els millors resultats s'han obtingut als Core Duo. Per altra banda, l'Athlon-xp, al no tenir el set
d'instruccions SSE2 no s'ha pogut beneficiar de l'optimització més “forta” i s'ha quedat amb un
discret speedUp de 1.05x.

Així doncs, la millor optimització és usar sols la vectorització. Les altres optimitzacions en les
execucions finals que hem realitzat de cada optimització per separat i ajuntant-les carregaven en
menor o major mesura el programa.

You might also like