Optimisation en MMX/SSE - Besoin d'aide

Optimisation en MMX/SSE - Besoin d'aide - ASM - Programmation

Marsh Posté le 28-08-2003 à 12:48:24    

Je travaille sur FairUse (logiciel "tout en un" de backup DVD)et j'ai besoin d'optimiser les fonctions de resizing de l'image en les réécrivant en ASM car c'est vraiment très lent en C++ mais je n'ai aucune pratique de ce language.
 
Est-ce que quelqu'un serait intéressé pour s'occuper de cette partie ?
 
Précision : c'est un projet open-source sous licence GPL.


Message édité par Cyberpat92 le 26-03-2004 à 13:06:54
Reply

Marsh Posté le 28-08-2003 à 12:48:24   

Reply

Marsh Posté le 28-08-2003 à 13:09:23    

Au cas où, voici l'une des fonctions qui font goulot d'étranglement :
 
void ResizePlane::_FilterConvolve(double arg0[], unsigned n0, double arg1[], unsigned n1, double result[])
{
    unsigned loop0, loop1;
 
    for(loop0 = 0; loop0 != n0 + n1 - 1; loop0++) {
        for(loop1 = 0, result[loop0] = 0; loop1 != n0; loop1++) {
            if(loop0 - loop1 < n1) {
                result[loop0] += arg0[loop1] * arg1[loop0 - loop1];
            }
        }
    }
}


Message édité par Cyberpat92 le 28-08-2003 à 13:10:55
Reply

Marsh Posté le 28-08-2003 à 13:16:23    

Je m'en serais bien occupé mais j'ai pas trop le temps en ce moment :o
 
[:sisicaivrai] [:sisicaivrai]


---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 28-08-2003 à 13:21:29    

ben rien qu'au niveau de l'algo, tu peux le faire facilement sur tes boucles, en calculant plus précisément les bornes, puisque finalement tu executes ta boucle interieur tant que  
 
loop1 != n0 && loop0 - loop1 < n1
 
tu te calcules le minimum pour pas te taper 2 tests à chaque fois, et voilà, un test en moins
 

Reply

Marsh Posté le 28-08-2003 à 13:40:24    

après ben fais un max de pré-calcul, et si ton CPU à des simd, ça peut etre l'occasion de derouler un peu cette boucle (faire 4 passes d'un coup)

Reply

Marsh Posté le 28-08-2003 à 13:42:10    

Compte tenu que:
- loop0 et loop1 sont unsigned
et que
- loop1 est l'incrément de la boucle intérieure
 
le bool (loop0 - loop1 < n1) n'est-il pas dangereux??
 
Edit: ortho!


Message édité par ACut le 28-08-2003 à 13:42:53

---------------
NOUVEAU! Le guide de l'édition en version ebook : http://marcautret.free.fr/autret/150q-ebook/
Reply

Marsh Posté le 28-08-2003 à 13:51:15    

aussi, bien vu.

Reply

Marsh Posté le 28-08-2003 à 14:40:45    

questions:
 
ta routine est-elle un traitement d'image ? (à priori oui)
dans ce cas, as-tu besoin d'avoir des flottants de type double ?
 
c'est quoi l'algorithme initial ?


Message édité par bjone le 28-08-2003 à 14:41:36
Reply

Marsh Posté le 28-08-2003 à 14:53:39    

Finalement, j'ai simplifié un peu :
 
void ResizePlane::_FilterConvolve(double arg0[], unsigned n0, double arg1[], unsigned n1, double result[])
{
 memcpy(result, arg1, n0 + n1 - 1);
}
 
:whistle:
 
Edit: Finalement je suis revenu en arrière, car la perte de qualité était importante pour un gain de vitesse minime. :heink:


Message édité par Cyberpat92 le 07-09-2003 à 19:20:26
Reply

Marsh Posté le 07-09-2003 à 15:13:02    

cyberpat92 a écrit :

Finalement, j'ai simplifié un peu :
 
void ResizePlane::_FilterConvolve(double arg0[], unsigned n0, double arg1[], unsigned n1, double result[])
{
 memcpy(result, arg1, n0 + n1 - 1);
}
 
:whistle:  


 
 [:aztechxx]

Reply

Marsh Posté le 07-09-2003 à 15:13:02   

Reply

Marsh Posté le 07-09-2003 à 15:14:16    

les paramètres inutiles c'est bien ...

Reply

Marsh Posté le 07-09-2003 à 19:19:02    

BJOne a écrit :

questions:
 
ta routine est-elle un traitement d'image ? (à priori oui)
dans ce cas, as-tu besoin d'avoir des flottants de type double ?
 
c'est quoi l'algorithme initial ?


 
Il s'agit de routines de redimensionnement d'image. Le source complet peut être trouvé sur http://fairuse.free.fr
 
Le problème c'est d'optimiser les fonctione de Resize.cpp

Reply

Marsh Posté le 07-09-2003 à 19:27:28    

si tu déroulais un peu toutes ces boucles ça serait mieux (_Resize? et Resize

Reply

Marsh Posté le 07-09-2003 à 21:04:02    

on a affaire à une fonctione en n^2
 
j'aimerais bien voir le gain en asm, je doute que ça va faire des miracles


---------------
Borland rulez: http://pages.infinit.net/borland
Reply

Marsh Posté le 08-09-2003 à 00:05:42    

faut que t utilise le DMA, pour transferer la memoire par packet.

Reply

Marsh Posté le 08-09-2003 à 00:15:00    

cyberpat92 a écrit :

Au cas où, voici l'une des fonctions qui font goulot d'étranglement :
 
void ResizePlane::_FilterConvolve(double arg0[], unsigned n0, double arg1[], unsigned n1, double result[])
{
    unsigned loop0, loop1;
 
    for(loop0 = 0; loop0 != n0 + n1 - 1; loop0++) {
        for(loop1 = 0, result[loop0] = 0; loop1 != n0; loop1++) {
            if(loop0 - loop1 < n1) {
                result[loop0] += arg0[loop1] * arg1[loop0 - loop1];
            }
        }
    }
}
 


 
 
chui un peu perdu dans ton bordel moua....tes indices me laissent reveur
tu gagneras pas grand chose a passer a l'asm.
 

  • Vu que t'indice pas lineraire les MMX et consort te serviront a rien.  
  • de fait, Prefetch non plus
  • seul truc, si result est != des tab initiaux, ecrire dedans au movnt, peut etre que tu soulageras un peu ton cache. T'attends pas a des merveilles non plus, ca sera juste pour dire que t'as optim asm


 
 
 

Reply

Marsh Posté le 08-09-2003 à 00:44:20    

j'ai toujours pas compris son algo (j'ai pas chercher trop non plus)
 
je devines que tu as une boucle ligne par ligne, puis pixel par pixel, avec une interpolation linéaire par pixel...
 
mais le truc est tellement mal tourné, et l'utilité des double me laisse perplexe à la base....


Message édité par bjone le 08-09-2003 à 00:44:34
Reply

Marsh Posté le 08-09-2003 à 00:47:52    

je pense qu'il y a justement énormément à gagner en asm (ou en mmx/sse), c'est le genre de truc ciblé par le simd.
 
mais vu l'écriture de base, pour qu'il puisse l'optimiser en asm, va y a avoir du boulot (alors que c'est une routine qui semble assez classique)

Reply

Marsh Posté le 08-09-2003 à 10:12:31    

ben pour mmx & cie ca depend de comment il indice son bordel et je dois dire que son fatras dans la declaration de ses boucles me fait perdre le fil....

Reply

Marsh Posté le 08-09-2003 à 10:46:42    

ca doit pouvoir se lineariser (quite a calculer des trucs pour rien).
 
D'ailleurs tu boss sur PC (MMX & cie ) ou sur MAC (AltiVec).
Si c sur Mac je dois pouvoir te pondre une version Altivec rapidement.


Message édité par Joel F le 08-09-2003 à 10:47:33
Reply

Marsh Posté le 08-09-2003 à 11:25:20    

n0 est l'indice maxi de arg0[], et n1 celui de arg1[] semblerait-il.
Le type double est obligatoire ? Ca correspond à des niveaux ? Ca irait ss dte plus vite en long si "normalisation" préalable  possible.
 
En ayant une variable locale "Resulta" qui cumule les += puis resul[loop0] = Resulta; en fin de for(loop1..) ça ferait ss dte rien gagner (en QuicBasic :whistle: ça m'a fait gagner un peu sur un prog, mais compilo pas optimiseur, antiquité !).
 
EDIT :  
si on regarde
       for(loop1 = 0; loop1 < n0; loop1++)  
       {  
           if(loop0 - loop1 < n1)  
           {  
               result[loop0] += arg0[loop1] * arg1[loop0 - loop1];  
           }  
       }  
 
le test if(loop0 - loop1 < n1) revient à if (loop0 - n1 < loop1) donc loop1 peut déjà partir de (loop0 - n1 + 1) si non nul ce qui évitera le test à chaque tour (juste un au début pour déterminer les bornes et faisabilité).
 
EDIT 2 :
Ca ferait qq chose comme  
void ResizePlane::_FilterConvolve(double arg0[], unsigned n0, double arg1[], unsigned n1, double result[])  
{  
   unsigned loop0, loop1;
   int      ind1, ind2;  
 
   for (loop0 = 0; loop0 < n0 + n1 - 1; loop0 ++)  
   {  
       result[loop0] = 0;
       ind1 = loop0 - n1 + 1;
       ind2 = n0;
       if (ind1 < ind2) // voir si inclusif ou exclusif, pas très réveillé
       {
         for (loop1 = ind1; loop1 < ind2; loop1 ++)  
         {  
           result[loop0] += arg0[loop1] * arg1[loop0 - loop1];  
         }  
       }  
   }  
}
 
EDIT 3 :
Si ind1 ne peut pas être négatif, loop0 - n1 + 1 >=0 donc RAZ des result[i] avec i = 0 à loop0 - n1 puis calcul des suivants avec for (loop0 = n1 - 1; loop0 < n0 + n1 - 1; loop0 ++)  
(à voir ds le détail, c'est mon dernier jour de ma semaine de congés :pt1cable:). Si on ne s'intéresse qu'aux points utiles, ça devrait accélérer le traîtement.
 
void ResizePlane::_FilterConvolve(double arg0[], unsigned n0, double arg1[], unsigned n1, double result[])  
{  
   unsigned loop0, loop1;
   
   if (n1 > 1)
   {
     for (loop0 = 0; loop0 < n1 - 1; loop0 ++)  
     {  
       result[loop0] = 0.0;
     }
   }
   for (loop0 = n1 - 1; loop0 < n0 + n1 - 1; loop0 ++)  
   {  
       result[loop0] = 0.0;
       for (loop1 = loop0 - n1 + 1; loop1 < n0; loop1 ++)  
       {  
         result[loop0] += arg0[loop1] * arg1[loop0 - loop1];  
       }  
   }  
}
A VERIFIER...


Message édité par Carbon_14 le 08-09-2003 à 12:00:34
Reply

Marsh Posté le 13-09-2003 à 03:52:24    

Merci de ces réponses et pistes :)
 
...mais après une nuit blanche de plus sur ce p..... de code, j'ai fini par réaliser que la fonction précédemment citée n'est appelée que 4 fois pour un encodage (à l'initialisation en fait) [:atog]
 
Par contre, le "vrai" goulot d'étranglement est là, et j'ai bien peur que ce soit cuit, car je ne vois pas comment optimiser une multiplication :cry:
 
Au fait, il s'agit d'un développement pour PC. Est-ce que le MMX pourraît apporter quelque chose ?
 
void ResizePlane::_ResizeX(const unsigned char *src, unsigned char *dst)
{
 const unsigned char *ptr;
 int     *filter, total;
 unsigned   loop0, loop1, length;
 
 for(loop0 = 0, filter = _filterX; loop0 != _dstDim.x; loop0++) {
  ptr = src + *filter++;
  length = *filter++;
  //for(loop1 = 0, total = 1 << 15; loop1 != length; loop1++) {
  for(loop1 = 0, total = 0x8000; loop1 != length; loop1++) {
   total += *ptr++ * *filter++; // <== ça c'est très lent
  }
  *dst++ = _clip1[total >> 16];
 }
}
 
void ResizePlane::_ResizeY(const unsigned char *src, unsigned srcStep, unsigned char *dst, unsigned dstStep)
{
 const unsigned char *ptr;
 int     *filter, total;
 unsigned   loop0, loop1, length;
 
 for(loop0 = 0, filter = _filterY; loop0 != _dstDim.y; loop0++, dst += dstStep) {
  ptr = src + *filter++ * srcStep;
  length = *filter++;
  //for(loop1 = 0, total = 1 << 15; loop1 != length; loop1++, ptr += srcStep) {
  for(loop1 = 0, total = 0x8000; loop1 != length; loop1++, ptr += srcStep) {
   total += *ptr * *filter++; // <== ça c'est très lent
  }
  *dst = _clip1[total >> 16];
 }
}
 
EDIT : Pour être tout à fait complet, je rajoute la fonction qui appelle les deux précédemment citées :
 
void ResizePlane::Resize(const unsigned char *src, unsigned char *dst)
{
 unsigned loop0;
 
 if(_empty == false) {
  if(_xFirst) {
   for(loop0 = 0; loop0 != _footprint.dim.y; loop0++) {
    _ResizeX(src + (_footprint.pos.y + loop0) * _srcDim.x, _temp + loop0 * _dstDim.x);
   }
   for(loop0 = 0; loop0 != _dstDim.x; loop0++) {
    _ResizeY(_temp + loop0, _dstDim.x, dst + loop0, _dstDim.x);
   }
  } else {
   for(loop0 = 0; loop0 != _footprint.dim.x; loop0++) {
    _ResizeY(src + _footprint.pos.x + loop0, _srcDim.x, _temp + loop0, _footprint.dim.x);
   }
   for(loop0 = 0; loop0 != _dstDim.y; loop0++) {
    _ResizeX(_temp + loop0 * _footprint.dim.x, dst + loop0 * _dstDim.x);
   }
  }
 }
}


Message édité par Cyberpat92 le 13-09-2003 à 03:55:44
Reply

Marsh Posté le 13-09-2003 à 08:05:15    

par contre dérouler les boucles ça te dit rien  :pfff:

Reply

Marsh Posté le 13-09-2003 à 08:58:13    

Taz a raison. Déroule de 4 ou 8, ca devrait te faire gagner qqs facteurs de vitesse (2 a 3 typiquement)

Reply

Marsh Posté le 13-09-2003 à 12:13:56    

vu. Je connaissais pas le "déroulage" de boucles, mais effectivement ca parait logique.
 
MErci, je vais essayer ca :)

Reply

Marsh Posté le 13-09-2003 à 17:50:58    

Joel F a écrit :

ca doit pouvoir se lineariser (quite a calculer des trucs pour rien).
 
D'ailleurs tu boss sur PC (MMX & cie ) ou sur MAC (AltiVec).
Si c sur Mac je dois pouvoir te pondre une version Altivec rapidement.


 
ça serait pas mal de pouvoir comparer la version asm avec alvitec et une autre avec mmx...


---------------
Borland rulez: http://pages.infinit.net/borland
Reply

Marsh Posté le 13-09-2003 à 18:10:36    

os2 a écrit :


 
ça serait pas mal de pouvoir comparer la version asm avec alvitec et une autre avec mmx...

le mmx et sse c'est de la blague par rapport à altivec

Reply

Marsh Posté le 15-09-2003 à 11:01:01    

Typiquement AltiVec va 2 à 4 fois plus vite que MMX et 2 foi splus vite que SSE2.
 
En outre, AltiVec se programme directement en C (pas d'assembleur), n'a pas besoin de "context switching" comme MMX.
 
Si ca interesse des gens j'ai un papier ou deux de comparaison MMX/AltiVec.
En outre je bosse sur une librairie d'encapsulation d'AltiVec au sein de classe STL like qui devrait pas tarder à etre releaser.

Reply

Marsh Posté le 15-09-2003 à 11:06:59    

allez balance

Reply

Marsh Posté le 15-09-2003 à 11:32:44    

OK, quelques Liens :
 
Une premiere evaluation :
* http://www.lri.fr/~sebot/publi/tsi.ps
 
Pour comprendre :
* http://www.mackido.com/Hardware/AltiVecVsMMX.html
 
* Pour finir, un exemple d'utilisation d'Altivec dans des conditions "exotiques :
http://wwwlasmea.univ-bpclermont.f [...] jfla03.pdf
 
Je posterais d'autres liens + tard, j'ai pas mes bookmarks de boulot sous la main.

Reply

Marsh Posté le 15-09-2003 à 11:42:52    


 
celui ci je laisserais tomber, il est trop vieux sur certains point. Le switch FP-MMX ne coute plus rien (meme si evidemment en utilisant MMX tu trashe ta pile fpu), au moins sur AMD. (j'imagine qu'ils ont mis le paquet la dessus a cause de leur 3dnow) (enfin, ils disent que ca coute plus)
 
Sur brols un peu recent on peut avoir MMX et calcul flottant en meme tps (3dnow pour l'un, SSE pour l'autre). Par contre je ne qualifierais surement pas ca d'ergonomique...


Message édité par chrisbk le 15-09-2003 à 11:43:21
Reply

Marsh Posté le 15-09-2003 à 12:18:32    

Je sais bien, des que je retourne au boulot, je posterais les *bons* liens que j'ai utilisé pour ma biblio de DEA.

Reply

Marsh Posté le 15-09-2003 à 12:27:06    

Joel F a écrit :

Je sais bien, des que je retourne au boulot, je posterais les *bons* liens que j'ai utilisé pour ma biblio de DEA.


 
je disais ca juste pour avoir le dernier mot hein ? [:ddr555]

Reply

Marsh Posté le 15-09-2003 à 12:51:50    

:sol:  de tte façon MMX ca suxx des ours :o

Reply

Marsh Posté le 18-09-2003 à 02:32:13    

Bon ben c'est devenu ça :
 
total += *ptr++ * *filter++;
total += *ptr++ * *filter++;
total += *ptr++ * *filter++;
total += *ptr++ * *filter++;
total += *ptr++ * *filter++;
total += *ptr++ * *filter++;
total += *ptr++ * *filter++;
total += *ptr++ * *filter++;
 
et effectivement, on gagne quand même un peu. Est-il possible avec MMX de faire plusieurs multiplications durant le même cycle CPU pour gagner du temps de calcul ?

Reply

Marsh Posté le 18-09-2003 à 07:58:29    

cyberpat92 a écrit :

Bon ben c'est devenu ça :
 
total += *ptr++ * *filter++;
total += *ptr++ * *filter++;
total += *ptr++ * *filter++;
total += *ptr++ * *filter++;
total += *ptr++ * *filter++;
total += *ptr++ * *filter++;
total += *ptr++ * *filter++;
total += *ptr++ * *filter++;
 
et effectivement, on gagne quand même un peu. Est-il possible avec MMX de faire plusieurs multiplications durant le même cycle CPU pour gagner du temps de calcul ?
 


 
vi si c aussi lineaire pas de pb
 
truc genre :
 

Code :
  1. unsigned int total[2];
  2. _asm
  3. {
  4. movq   mm0,[filter]
  5. pmuld  mm0,[ptr];
  6. movq   mm1,[filter+8]
  7. pmuld  mm1,[ptr+8];
  8. movq   mm2,[filter+16]
  9. pmuld  mm2,[ptr+16];
  10. movq   mm3,[filter+24]
  11. pmuld    mm3,[ptr+24];
  12. paddusd  mm0,mm1;
  13. paddusd  mm0,mm2;
  14. paddusd  mm0,mm3;
  15. add filter,32;
  16. add ptr,32;
  17. movq total,mm0;
  18. }
  19. unsigned int rt = total[0] + total[1];


 
pu sur a 100% des noms des instructions, a verifeir

Reply

Marsh Posté le 19-09-2003 à 01:24:28    

J'comprends pas, j'ai cherché un peu et en fait il existe un pmull et un pmulh et plein d'autres trucs marrants auxquels j'ai rien compris... :cry:

Reply

Marsh Posté le 19-09-2003 à 07:35:25    

cyberpat92 a écrit :

J'comprends pas, j'ai cherché un peu et en fait il existe un pmull et un pmulh et plein d'autres trucs marrants auxquels j'ai rien compris... :cry:  


 
ah ouais c vrai qu'ya deux pmull
ben pmulld alors :O
 
(multiplication d'un nombre sur 32bits par un nombre sur 32bits = un nombre sur 64bits. pmull garde les 32bits du bas, tandis que mulh conservera les 32bits du haut)

Reply

Marsh Posté le 19-09-2003 à 08:06:47    

ca me rapelle que je voulais ecrire un tuto mmx pour le forum fut un tps :D

Reply

Marsh Posté le 19-09-2003 à 09:24:58    

Tu sais ou il est ?
ca m'interesse pour faire le portge de ma bibliotheque d'AltiVec vers MMX-SSE 2

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed