Challenge/Partage de connaissances : comment optimisez ce code?

Challenge/Partage de connaissances : comment optimisez ce code? - C++ - Programmation

Marsh Posté le 20-02-2006 à 15:19:28    

Bonjour à tous  :) , je vous propose un petit challenge de programmation la plus efficace possible,
le but est de partager ses connaissances et montrer à tous des méthodes de programmation performantes
 
la règle du jeux est la suivante, à partir d'un code simple de filtrage d'image par exemple, tout candidat peut proposer ses propres modifications
et montrer à tous le gain en performance qu'il a pu noter.
 
Sont autorisés tout changement du code d'origine qui ne modifie pas le résultat final, à partir de là, toute optimisation est possible
pointeurs/registres, fonction intrinsics (SSE2 etc), threads etc tout est envisageable, mais doit rester portable (linux et windows, restons en là)
les shaders peuvent aussi être proposés.
 
Le code simple de base est présenté ci dessous, Bon courage à tous.
 
l'exemple proposé ici réalise un filtrage sur une image en NIVEAUX DE GRIS (donc, pas en RGB ou autre, c'est juste sur un calque), ligne par ligne, de la gauche vers la droite, il s'agit en fait d'un filtrage passe bas (lissage des contours) assymétrique couramment utilisé en traitement d'image.
 
dans le code proposé, les calculs sont effectués sur une image de taille _NBrows par _NBcolumns
le résultat final est stocké dans le tableau 'outputFrame', en entrée on a le tableau outputFrame (résultat de la frame précédente par exemple que l'on va écraser après calcul), l'image d'entrée 'inputFrame' (de même taille que la sortie) et une autre image de paramètres 'window' de même taille que l'image de sortie), une variable temporaire 'value' permet d'inclure dans le calcul du pixel courant, le résultat du calcul du pixel précédent.
 
TOUS LES CALCULS SONT EFFECTUES EN TYPE DOUBLE ou en FLOAT, mais pas un autre type introduisant une perte de précision, ici, j'ai tout mis en double,mais tout peut être passé en float !!!
 
voici le code non optimisé :
 
%%%%%%%%%%%%%%%%%%%%%%%%%%
 

Code :
  1. #include <iostream.h>
  2. #include <time.h>
  3. unsigned int _NBcolumns=640; // nombre de colonnes des buffers
  4. unsigned int _NBrows=480; // nombre de lignes des buffers
  5. // Entry point
  6. int main(int argc, char *argv[])
  7. {
  8. // buffers d'entrée et de sortie
  9. double outputFrame[480][640], inputFrame[480][640], windowfilter[480][640];
  10. // variables/buffers utilisés dans les boucles:
  11. double value, tau=10.0;
  12. unsigned int IDrow, IDcolumn, NBloops=100;
  13. cout<<"loop started"<<endl;
  14. int startTime=clock();
  15. //appel de la fonction de filtrage 100 fois
  16. for (unsigned int i=0; i<NBloops; ++i)
  17.    // def des variables de taille des buffers
  18. {
  19.    /* PARTIE A OPTIMISER */
  20.    // boucle appelée pour toute nouvelle image d'entrée (inputFrame) on retyrouve le code suivant :
  21.    for (IDrow=0; IDrow <_NBrows;  ++IDrow)
  22.    {       
  23.         value=0; // initialize la variable pour chaque nouvelle ligne traitée
  24.           for (IDcolumn=0; IDcolumn<_NBcolumns ; ++IDcolumn)
  25.        {
  26.            value = outputFrame[IDrow][IDcolumn]*tau + inputFrame[IDrow][IDcolumn] +  windowfilter[IDrow][IDcolumn]* value;
  27.            outputFrame[IDrow][IDcolumn] = value;
  28.        }
  29.    }
  30.  
  31.    /* FIN DE LA PARTIE A OPTIMISER  */
  32. }
  33. int endTime=clock();
  34. cout<<"filtering finished, time elapsed="<<(endTime-startTime)/(NBloops)<<"clocks"<<endl;
  35. return 0;
  36. }


 
%%%%%%%%%%%%%
 
merci pour vos contributions, pour toute les personnes intéressés par l'optimisation de code et les novices désireux de progresser (moi compris bien entendu, je ne suis que programmeur amateur, mais j'ai bien envie de progresser)
 
++
ALex : Smile

Message cité 2 fois
Message édité par alexIsBack le 23-02-2006 à 13:41:49
Reply

Marsh Posté le 20-02-2006 à 15:19:28   

Reply

Marsh Posté le 20-02-2006 à 15:29:47    

c'est dingue ce que les gens inventent pour faire faire leur boulot par les autres...

Reply

Marsh Posté le 20-02-2006 à 15:30:33    

skoi l'interêt du double pour du traitement d'image ?

Reply

Marsh Posté le 20-02-2006 à 15:36:22    

comme tu utilises des tableaux le codes sera tres probablement vectorisé (suivant le compilateur) et dans cette exemple minimal on pourra difficilement faire mieux
sinon, avant de parler optimisation mieux vaut apprendre à (bien) programmer, c'est ca le plus important

Reply

Marsh Posté le 20-02-2006 à 16:12:41    

on a le droit de mettre de l'assembleur ? :)


---------------
http://www.blastmanu.info
Reply

Marsh Posté le 20-02-2006 à 16:34:59    

notornis a écrit :

c'est dingue ce que les gens inventent pour faire faire leur boulot par les autres...


 
salut, remarque intéressante mais pas très applicable pour ce sujet, je programme en amateur, j'ai fait ce code de base tout simple et c'est lui qui m'a amené à me poser ces questions d'optimisation, après tu penses ce que tu veux, le but de ce thread est plutôt à vertut pédagogique pour tout le monde
 
cordialement
   Alex


Message édité par alexIsBack le 20-02-2006 à 16:41:28
Reply

Marsh Posté le 20-02-2006 à 16:36:48    

blastman a écrit :

on a le droit de mettre de l'assembleur ? :)


 
 
oui oui, tous les droits, même en assembleur, perso, j'ai essayé avec les fonctions sse2 avec le header intrinsics.h mais je ne devais pas bien m'y prendre, ca ramait plus...la quiche lol

Reply

Marsh Posté le 20-02-2006 à 16:40:37    

bjone a écrit :

skoi l'interêt du double pour du traitement d'image ?


 
je travaile en double pour garder la précision des calculs, dans mon prog, ce filtre est appelé de multiples fois à la suite, donc il y a accumulation d'erreur donc attention aux arrondis.
Mais vous pouvez proposer des versions qui travaillent sur les entiers, mais le problème sera la perte de précision...car ce genre de calcul pour un intervalle de valeu de 0 à 255 c'est un peu juste, faudrai bien augmenter l'intervalle en entier (genre multiplier les valeurs d'entrée par un facteur X pour bosser sur de grands intervalles de valeur style de 0 à 1024 ou plus)  et recalibrer juste avant l'affichage...mais là, je suis curieux de connaotre une méthode fiable pour y parvenir, tu as une idée la dessus?
 
++


Message édité par alexIsBack le 20-02-2006 à 16:53:57
Reply

Marsh Posté le 20-02-2006 à 17:03:02    

moi je repete qu'il n'y a rien à optimiser

Reply

Marsh Posté le 20-02-2006 à 17:35:02    

skelter a écrit :

moi je repete qu'il n'y a rien à optimiser


 
salut, il me semble que si, déjà rien qu'en "forcant " des données à être placée dans les registres, on peut gagner
 
j'ai remplacé la déclaration de  

Code :
  1. double value;


par  

Code :
  1. register double value;


comme c'est une variable temporaire appellée à chaque tour de boucle, on gagne apparemment un temps fou avec cette méthode
=> là, j'ai eu un facteur 2 en gain de vitesse, c'est con, mais ca marche déjà bien
 
après faudrait voir pour le multithread si on peut y gagner, 2 threads par exemple, et chacun calcule une moitié d'image, mais là, je ne connais pas trop
 
sinon, j'ai essayé les fonctions sse2 avec le header

Code :
  1. #include <emmintrin.h>


les fonctions qui codent directement en assembleur pour les pentium4 et équivalent, mais c'est relou comme c'est lent, je doit po bien jouer avec ces méthodes...je fais trop de transfert de mémoire et doit surcharger les registres dispo du processeur,  
voila un appercut:, mais le gain en vitese enst de 1/2...2 fois plus lent, j'ai raté le truc lol
 
 

Code :
  1. // déclaration des variables/registres qui vont contenir 2 variables au format double pour effectuer 2 opérations en parallèle
  2.        __m128d  currentPixel, tempValue, oldValue;
  3.    __m128d Va,  Vgain, Vtau;
  4.    Va=_mm_set1_pd(a); //charge le vecteur avec 2 variables idntiques égales à a
  5.    Vgain=_mm_set1_pd(gain);//charge le vecteur avec 2 variables idntiques égales à gain
  6.    Vtau =_mm_set1_pd(tau);//charge le vecteur avec 2 variables idntiques égales à gain
  7.    double returnData[2]; // un tableau à "2 cases" doubles qui servira à récupérer les données après calcul
  8.    register unsigned int firstloopSTOP=_NBrows-1;  // le mot register indiqu au compilateur qu'il serait préférable que cette variable soit stockée en registre...si c'est possible
  9.    for (IDrow=0; IDrow<firstloopSTOP ; IDrow+=2 ) // on va de 2 lignes en 2 lignes maintenant
  10.    { 
  11.       // set init value
  12.       //value=0;
  13.       tempValue=_mm_setzero_pd(); // on initialise la double valriable à 0
  14.  
  15.       for (IDcolumn=0; IDcolumn<_NBcolumns ; ++IDcolumn)
  16.       {
  17.          // pack to values : __m128d  _mm_set_pd( double x, double y ); => méthode pour remplir le vecteur par 2 valeurs type double
  18.          oldValue = _mm_set_pd( outputFrame[IDrow][IDcolumn], outputFrame[IDrow+1][IDcolumn]);
  19.          currentPixel = _mm_set_pd(inputFrame[IDrow][IDcolumn], inputFrame[IDrow+1][IDcolumn]);
  20.          // calcul du filtre=> j'ai tout mis d'un coup ensemble, quelle est la meilleure méthode?
  21.          tempValue = _mm_add_pd(currentPixel,_mm_add_pd(_mm_mul_pd(Vtau, oldValue), _mm_mul_pd(Va, tempValue)));
  22.          //on récupère/reconvertit les données calculées au format double
  23.          _mm_store_pd (returnData, tempValue); //write result in the buffer
  24. // finalement, on réécrit dans le buffer de sortie...hem, ca en fait des transfert mémoire, ca devient lourd, il doit y avoir moyen de faire mieux...
  25.          outputFrame[IDrow][IDcolumn]=returnData[0];
  26.          outputFrame[IDrow+1][IDcolumn]=returnData[1];
  27.        
  28. // ancienne version de code simple
  29. //value = outputFrame[IDrow][IDcolumn]*tau + inputFrame[IDrow][IDcolumn] +  a* value;
  30.          //outputFrame[IDrow][IDcolumn] = value;
  31.       }
  32.    }


Message édité par alexIsBack le 20-02-2006 à 17:36:26
Reply

Marsh Posté le 20-02-2006 à 17:35:02   

Reply

Marsh Posté le 20-02-2006 à 17:47:36    

Citation :

salut, il me semble que si, déjà rien qu'en "forcant " des données à être placée dans les registres, on peut gagner


 
ca c'est le boulot du compilateur, d'ailleur ton mot clef register n'est pratiquement plus utilisé parce que c'est inutile, qui mieux que le compilateur peu l'utiliser à bon escient ?
 
 

Citation :

après faudrait voir pour le multithread si on peut y gagner, 2 threads par exemple, et chacun calcule une moitié d'image, mais là, je ne connais pas trop


 
on appel ca parallelisé, la seul condition c'est que chacun des deux (ou +) flots de calcul ne dépende pas du résultat de l'autre, et chose importante, tu gagne uniquement sur une machine à architecture parallele (multi proc ou autre)
 
 
en gros, laisse faire le compilateur, avec gcc4 (et les options qui vont bien, en ciblant l'arhcitecture si tu veux) par exemple tu auras de bon resultat rien qu'avec la vectorisation des boucles, d'ailleur si j'était toi j'utiliserais des constante pour _Nbrows et l'autres
 
et puis apres ca sert a rien de foutre en l'air la portabilité et la maintenabilité du code pour gagner 3 cycles...

Reply

Marsh Posté le 20-02-2006 à 23:04:28    

alexIsBack a écrit :

l'exemple proposé ici réalise un filtrage sur une image en NIVEAUX DE GRIS (donc, pas en RGB ou autre, c'est juste sur un calque), ligne par ligne, de la gauche vers la droite, il s'agit en fait d'un filtrage passe bas (lissage des contours) assymétrique couramment utilisé en traitement d'image.


est-ce que ça s'exprime sous forme matricielle ?
 

Reply

Marsh Posté le 21-02-2006 à 10:52:50    

skelter a écrit :

moi je repete qu'il n'y a rien à optimiser


 
en asm avec du SSE si, beaucoup, mais pas en double

Reply

Marsh Posté le 21-02-2006 à 10:54:41    

(surtout vu la linéarité du traitement...)

Reply

Marsh Posté le 21-02-2006 à 10:57:48    

passe en float et utilise altivec ou SSE selon ta plateforme :o
sinon le reste c'est du branlache de mouche :o

Reply

Marsh Posté le 21-02-2006 à 10:58:11    

skelter a écrit :


ca c'est le boulot du compilateur, d'ailleur ton mot clef register n'est pratiquement plus utilisé parce que c'est inutile, qui mieux que le compilateur peu l'utiliser à bon escient ?


 
bin le programmeur ? Maintenant c'est sur que c'est un relicat des temps passés, les compilos se demerdent tres bien maintenant
 
 

skelter a écrit :


et puis apres ca sert a rien de foutre en l'air la portabilité et la maintenabilité du code pour gagner 3 cycles...


 
Il ne faut pas non plus cracher systématiquement sur l'optimisation. La dans son cas, il y a bcp a gagner, en utilisant les instructions vectorielles d'un 3dnow / SSE et en ajoutant un petit prefetch kivabien
 
edit : merde, j'avais pas vu la reutilisation de value, ca va etre penible ca par contre

Message cité 1 fois
Message édité par chrisbk le 21-02-2006 à 11:02:33
Reply

Marsh Posté le 21-02-2006 à 11:06:49    

et gcc par exemple, il ne serait pas se demerder avec les options -mcpu=... et -mfpmath=... ? si on lui donne toutes les infos je vois pas pourquoi il ne le ferais pas

Reply

Marsh Posté le 21-02-2006 à 11:07:47    

[:moule_bite]

Reply

Marsh Posté le 21-02-2006 à 11:29:40    

gcc il sais pas vectoriser du code de maniere automatique. Je peut même te dire que PERSONNE ne sait faire. Seul VAST fournit un semblant d'autovectorization qui reste imaintenable :)
 
Apre [:pingouino] j'ai juste passer ma these sur ce genre de problemes mais je dis peut etre du käkä [:dawa]

Reply

Marsh Posté le 21-02-2006 à 11:30:19    

y'avait VectorC aussi

Reply

Marsh Posté le 21-02-2006 à 11:32:01    

VectorC, C// et j'en passe ... ils restent tous relativement peu efficace   des que ton code diverge de ce qu'ils ont appris à vectoriser.
 
En outre, le compilo ne pourra JAMAIS detecté les changements de structures et d'algorithmes à appliquer à un problemes donné pr le rendre efficace une fois vectoriser :D

Reply

Marsh Posté le 21-02-2006 à 11:44:27    

j'ai aucun moyen de tester (pas de gcc4) mais tu ne vas pas me dire qu'il ne saurais pas verctoriser cette simple boucle qui parcour une matrice, j'entends par la dérouler la boucle (sucré tout ce code parasite) pour avoir que les opération fpu consécutives et bénéficier à fond de l'effet pipeline

Reply

Marsh Posté le 21-02-2006 à 11:46:48    

Reply

Marsh Posté le 21-02-2006 à 13:25:45    

Merci pour ce lien :o
Je sais que gcc sait dérouler les boucles et tout mais d'experiences, ca suffit rarement. Generer les commandes de prefecth adequats, determinés les bonnes tailles de blocs, c'est deja plus compliqué.
 
Et la vectorization c'est aussi PLUS que de simples boucles :o

Reply

Marsh Posté le 21-02-2006 à 13:56:52    

en gros, quel serait le gain ?

Reply

Marsh Posté le 21-02-2006 à 14:07:37    

Sur mon G5, pour des float , j'ai jamais dépasser les x2 en mode auto-vector alors que a la main j'atteins du x3.89. Pour les types entiers 8bits j'atteinds le x7 et le x13 a la main ...
 

Reply

Marsh Posté le 21-02-2006 à 14:52:50    

oui, dans ce cas ca peu valoir le coup  :ange:

Reply

Marsh Posté le 21-02-2006 à 15:25:13    


Pour comparer les vitesses de calculs, j ai toujours sous la main un petit programme. Sans optimisation, voici le résultat (les chiffres sont en seconde pour 100M opérations, sizeof(double)=sizeof(longlong)):

Code :
  1. Speed test: double    Vs        longlong
  2. Increment : 0.797614    0.397202
  3. Decrement : 0.744219    0.426092
  4. Add       : 0.885311    0.562093
  5. Sub       : 0.759892    0.404575
  6. Mul       : 0.882668    1.121381
  7. Div       : 2.195350    4.661417
  8. Sqrt      : 5.399183
  9. Sin       : 9.028791
  10. MulAdd     : 1.177539
  11. fma        : 2.666586
  12. epR        : 2.240860
  13. frexp      : 2.364559


 
La différence de vitesse est très claire.
A noter que parfois le réencodage de fonctions peut être plus rapide que la version contenue dans math.h, par ex. frexp (standard, lent) et epR (extrait la partie exponentielle du nombre, plus rapide). Mon ordi ne supporte pas l opération fma (réécrire MulAdd est plus rapide). Sur mon ordi (AMD) le type float n est pas plus rapide que double, seulement plus précis. Sur les architectures Intel les types réels sont horriblement plus lents (j ai pas d explications à ça, mais un test m a montré que j obtenais 25 FPS sur une démo graphique avec calcul type double pour AMD 1,666G et 24 FPS pour Intel 2.5G).
 
Autre chose: Les opérateurs << et >> sont plus rapides que la mutiplication/division entière, ou pour certaines architectues que la récupération par example de bits de poid fort d une autre manière (unions par ex.).
 
En ce qui concerne la précision des calculs, on s interesse à la perte de bits. Pour les entiers la perte survient lors de l opération diviser. Pour les réels elle survient notamment lors de l addition/soustraction.
 
Par example pour calculer la moyenne entre deux nombres, la perte est d un demi bit minimum, de 1 bit maximum, dans les deux cas.
L utilisation d un entier de taille apropriée peut par conséquent être même plus précis et plus rapide que l utilisation de double.
 
Dans la rapidité, entre en compte aussi la taille du type entier. Le type int correspond généralement à la taille d un mot machine. Il est plus rapide d utiliser un mot machine que plusieurs, et sur certaines (beaucoup?) de machines utiliser moins d un mot nest pas plus rapide (c est dû aux caches CPU).
 
Reste ensuite à considérer la taille mémoire utilisée. Lorsque sizeof(int)<sizeof(double), c est a dire sur les machines 32 bits (les plus courantes...?), l algo utilise moins de mémoire.
 
Je recommanderais donc l utilisation du type int, plutot que double, sur la plupart des machines. On peut enchaîner 24 fois le filtre sans grande perte de précision. Si l image finale doit être stockée en JPEG on peut même probablement aller jusqua 48 fois. Pour plus de précision, le type long long reste plus rapide que double.
 
L utilisation de register n est pas recommandée, sauf dans le cas ou on a besoin d utiliser à la fois une compilation optimisante (gcc: O3, renameregister) et non optimisante (O0).
 
Un example de programme de filtre graphique lent est ImageMagick. Il utilise des doubles en interne, ça ne rends pas forcément les calculs plus précis mais certainement plus lents. Ça ne l empêche pas d être très pratique à utiliser vu la grande variété de filtres et l utilisation en ligne de commande aussi bien qu en librairie.
 
En conclusion, il n y a rien à optimiser dans ce code. Il est parfaitement rapide quand on a le temps.
 
 

Reply

Marsh Posté le 22-02-2006 à 10:59:10    

Joel F a écrit :

passe en float et utilise altivec ou SSE selon ta plateforme :o
sinon le reste c'est du branlache de mouche :o


 
oui, passer en float , j'était assez partant, on conserverait suffisamment de précision.
sinon, en SSE2, on put aussi effectuer 2 opérations en parrallèle sur les doubles...et donc 4 sur les float ce qui peut être vraiment sympa
 
pour plus de facilité, on m'a conseillé d'utiliser les fonctions intrinsics, mais bon, j'ai des soucis d'efficacité à e niveau là, ca a tendance à plus ramer que la version de base...
quelle est la bonne méthode à appliquer, de quelle manière bien jouer avec le prefectch? cf par exemple en partant du code que j'ai déjà proposé avec les  intrinsics? car pour cette méthode là, je débute
++

Reply

Marsh Posté le 22-02-2006 à 11:02:18    

chrisbk a écrit :

bin le programmeur ? Maintenant c'est sur que c'est un relicat des temps passés, les compilos se demerdent tres bien maintenant
 
 
 
 
Il ne faut pas non plus cracher systématiquement sur l'optimisation. La dans son cas, il y a bcp a gagner, en utilisant les instructions vectorielles d'un 3dnow / SSE et en ajoutant un petit prefetch kivabien
 
edit : merde, j'avais pas vu la reutilisation de value, ca va etre penible ca par contre


 
 
j'ai déjà posté un exemple en utilsant les sse2, avec les fonctiosn intrinsics, malheureusement, la méthode ne doit pas être la bonne car ca rame plus...le paradoxe...je pense que je fais trop de transferts mémoire, tu parles de  prefetch "kivabien", as tu des infos la dessus, le prefetch, je connais pas

Reply

Marsh Posté le 22-02-2006 à 11:09:45    

Joel F a écrit :

Sur mon G5, pour des float , j'ai jamais dépasser les x2 en mode auto-vector alors que a la main j'atteins du x3.89. Pour les types entiers 8bits j'atteinds le x7 et le x13 a la main ...


 
 
quelles manips effectue tu donc à la main?
 
perso, pour ce code, je ne manip que des tableaux 2D, y atil un moyen de se déplaver plus vite dans ces tableaux? j'ai essayé en manipulant des pointeurs mais l'effet inverse est arrivé.
 
en fait, il faudrait idéalement que les adresses des pixels soient directement dans les registres, que les valeurs de pixels soient directement transférées au proc pour calcul et directement reposé en mémoire, sans trop prendre de temps de transfert mémoire etc...là, j'avoue que j'ai a apprendre, je ne connais que le C++ de base

Reply

Marsh Posté le 22-02-2006 à 11:14:29    

prefetch, c'est une instruction qui dit (grosso merdo) au CPU "Hé jean paul, je vais bientot avoir besoin des infos se trouvant a l'adresse [machin], si t'as que ca a faire ca serait sympa de les mettre en cache". Bref, si c'est utilisé correctement ca augmente tes probabilités de lire en cache plutot qu'en memoire, d'ou joli gain de perf

Reply

Marsh Posté le 22-02-2006 à 11:20:48    

Citation :

perso, pour ce code, je ne manip que des tableaux 2D, y atil un moyen de se déplaver plus vite dans ces tableaux? j'ai essayé en manipulant des pointeurs mais l'effet inverse est arrivé.


 
effectivement, un 'pointer-style' code est souvent mois optimiser qu'un 'array-style', c'est déconseille
 

Citation :

en fait, il faudrait idéalement que les adresses des pixels soient directement dans les registres


 
le plus important c'est avant tout qu'il y ai le moins d'acces à la mémoire (optimiser l'utilisation du cache) pendant le parcour du tableau, faut acceder aux élément de la matrices dans l'ordre dans lequel ils sont ordonnés en mémoire (en C++, les tableaux multidimensionnels sont ordonnés en ligne) et c'est ce que tu fais ici
essayes l'inverse (avec un tableau suffisament grand), tu verras la différence :)

Reply

Marsh Posté le 22-02-2006 à 11:26:12    

chrisbk a écrit :

prefetch, c'est une instruction qui dit (grosso merdo) au CPU "Hé jean paul, je vais bientot avoir besoin des infos se trouvant a l'adresse [machin], si t'as que ca a faire ca serait sympa de les mettre en cache". Bref, si c'est utilisé correctement ca augmente tes probabilités de lire en cache plutot qu'en memoire, d'ou joli gain de perf


 
 
okay, c'est bien ce que j'avais compris alors, mais pour faire du prefecth, tu utilise les intrinsics ou il y a d'autres fonction plus portables?

Reply

Marsh Posté le 22-02-2006 à 11:27:06    

heuh bin t'as l'asm inline [:dr_zoidberg] mais pour la portabilité, brosse toi fort

Reply

Marsh Posté le 22-02-2006 à 11:29:39    

skelter a écrit :

[quote]
le plus important c'est avant tout qu'il y ai le moins d'acces à la mémoire (optimiser l'utilisation du cache) pendant le parcour du tableau, faut acceder aux élément de la matrices dans l'ordre dans lequel ils sont ordonnés en mémoire (en C++, les tableaux multidimensionnels sont ordonnés en ligne) et c'est ce que tu fais ici
essayes l'inverse (avec un tableau suffisament grand), tu verras la différence :)


 
heu, là, j'ai du mal à te suivre, tu dis donc que mon code traite effectivement ligne par ligne et que c'est bien ou pas, si je tente dans l'autre sens, ca fait quoi? plus ou moins lent?

Reply

Marsh Posté le 22-02-2006 à 11:42:55    

plus lent si tu fais
 

Code :
  1. for (IDcolumn=0; IDcolumn<_NBcolumns ; ++IDcolumn){
  2. for (IDrow=0; IDrow <_NBrows;  ++IDrow) {
  3. ...
  4. }
  5. }


 
apres suivant la taille tu tableau (et en particulier des lignes, des derniers dimension en générale), la différence est plus ou moin sensible

Reply

Marsh Posté le 22-02-2006 à 11:45:59    

skelter a écrit :

plus lent si tu fais
 

Code :
  1. for (IDcolumn=0; IDcolumn<_NBcolumns ; ++IDcolumn){
  2. for (IDrow=0; IDrow <_NBrows;  ++IDrow) {
  3. ...
  4. }
  5. }


 
apres suivant la taille tu tableau (et en particulier des lignes, des derniers dimension en générale), la différence est plus ou moin sensible


 
 
okai, alors c'est parfait enfin, dumoins, c'est moins pire ;)
 
sinon, question, les calculs en float seraient ils plus rapide? un collègue m'a dit le contraire mais ca m'a paru bizarre car les float ont 2*moins de données non?

Reply

Marsh Posté le 22-02-2006 à 16:37:06    

J'ai pas testé mais tu peux essayer ca:
 

Code :
  1. #include <iostream>
  2. #include <ctime>
  3. using namespace std;
  4. int main() {
  5. const unsigned int _NBcolumns(640), _NBrows(480), _Surface(_NBcolumns * _NBrows);
  6. double *outputFrame(new double[_Surface]), *inputFrame(new double[_Surface]), *windowFilter(new double[_Surface]);
  7. const double tau(10.0);
  8. const unsigned int NBloops(100);
  9. cout << "loop started" << endl;
  10. int startTime(clock());
  11. double value, *pOut(outputFrame), *pIn(inputFrame), *pWin(windowFilter);
  12. for(unsigned int loops(NBloops) ; loops-- ; ) {
  13.  for(unsigned int rows(_NBrows) ; rows-- ; ) {
  14.   value = 0;
  15.   for(unsigned int cols(_NBcolumns) ; cols-- ; ) {
  16.    value = *pOut * tau + *pIn++ + *pWin++ * value;
  17.    *pOut++ = value;
  18.   }
  19.  }
  20. }
  21. int endTime(clock());
  22. cout << "filtering finished, time elapsed=" << (endTime-startTime)/(NBloops) << "clocks" << endl;
  23. delete[] windowFilter;
  24. delete[] inputFrame;
  25. delete[] outputFrame;
  26. return(0);
  27. }

Reply

Marsh Posté le 22-02-2006 à 16:52:31    

c'est pire, on l'a déja dit faut s'attendre à moins d'optimisations si on parcourir un tableau avec un pointeur
 
et accessoirement faudrais controller les allocations dynamiques

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

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