A SUPPRIMER

cs_saylar Messages postés 102 Date d'inscription vendredi 21 avril 2006 Statut Membre Dernière intervention 10 février 2008 - 24 juin 2006 à 15:11
vecchio56 Messages postés 6535 Date d'inscription lundi 16 décembre 2002 Statut Membre Dernière intervention 22 août 2010 - 27 juin 2006 à 15:13
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/38280-a-supprimer

vecchio56 Messages postés 6535 Date d'inscription lundi 16 décembre 2002 Statut Membre Dernière intervention 22 août 2010 14
27 juin 2006 à 15:13
La différence en i-- et --i est important quand i est un objet d'une classe, parce que ces deux opérateurs sont codés différemment. Comme --i retourne l'objet calculé ("i-1"), il est plus rapide que i-- que calcule un objet ("i-1") mais en retourne un autre ("i")
Pamaury Messages postés 341 Date d'inscription jeudi 3 avril 2003 Statut Membre Dernière intervention 17 juin 2008 3
26 juin 2006 à 22:34
Tu va surement me dire que je pinaille mais il me semble que tu ne code pas opengl :)
A chaque application sa spécialité, dans 99.99999999% des cas on en a rien à foutre de gagner une ns .

"Avant les tableaux que tu donnes (qui passent pas, mais ça c'est pas ta faute), tu dis que c'est l'algorithme qui compte. Je ne suis pas d'accord. Plus exactement, ça dépend.
Pour un étudiant qui a un projet à faire, effectivement, le principe est plus intéressant que le résultat.
Mais à l'inverse, je doute que les gens qui développent OpenGL (ou d'autres librairies du genre) s'amusent à faire du code particulièrement élégant. Là au contraire, si on peut gagner 1ms, c'est déjà énorme."

Ton raisonnement est faux .
On fait d'abord un bon algo puis on optimise . tu optimise pour gagner 1ns par pixel mais si ton algo utilise 1s pour traiter chaque pixel çà ne sert à rien donc l'algo compte beaucoup plus que l'optimisation .

Quand à ton prof je veux bien qu'il t'es dit çà mais sur developpez.com il y a aussi eut un thread là dessus . C'était assez intéressant de voir une centaine de post parler d'un sujet vain . De toute façon je ne vois même pas pourquoi on peut douter que les deux écriture génère du code différent mais bon c'est comme çà .

Pour le coup du loop/dec-jxx, les tableau semblent dire le contraire mais bon je n'irai pas tester .
fkx Messages postés 44 Date d'inscription jeudi 29 janvier 2004 Statut Membre Dernière intervention 26 juin 2006
26 juin 2006 à 20:42
Pamaury>

Alors, tout d'abord je vais te contredire en ce qui concerne developpez.com puisque je vais pas sur ce site. Et même mieux, je peux t'apprendre que c'est mon prof de compil' qui m'avait fait cette remarque. Donc, ça m'étonnerait bien que tu t'en rappelles !

D'autre part, si tu ne vois pas d'intérêt à gagner des nanosecondes, sache que moi non plus. J'aime le faire, c'est tout. Y'a des gens qui aiment le chocolat, et d'autres qui aiment gagner des nanosecondes. C'est pas la peine de se taper dessus pour savoir si y'a un intérêt à ça !

Avant les tableaux que tu donnes (qui passent pas, mais ça c'est pas ta faute), tu dis que c'est l'algorithme qui compte. Je ne suis pas d'accord. Plus exactement, ça dépend.
Pour un étudiant qui a un projet à faire, effectivement, le principe est plus intéressant que le résultat.
Mais à l'inverse, je doute que les gens qui développent OpenGL (ou d'autres librairies du genre) s'amusent à faire du code particulièrement élégant. Là au contraire, si on peut gagner 1ms, c'est déjà énorme.
(Oui oui, je sais : là on gagne des nanosecondes... Sauf que 1 nanoseconde de moins pour traiter 1 pixel, ça fait presque 2ms de gagnées pour une image en 1600x1200. Sur les 16ms dont on dispose pour calculer une image (en 60Hz), je trouve que gagner 1/8 de temps c'est pas si mal ! Et il y a sûrement plus de gens que ce que l'on peut croire qui utilisent cette résolution. Il suffit de faire des LANs pour le voir...)


Par flemme, je ne ferai pas de test concernant les gains de dec/jxx sur loop, même si je suis convaincu qu'on gagne plus de des nanosecondes...

Par contre et pour finir, j'ai moi-même désassemblé plein de petits bouts de code pour voir ce qui se passait (de toute façon, je crois que y'a que comme ça que je saurai la vérité à ce sujet), et la conclusion est la suivante (je la fait suffisament précise pour ne pas être ambigu mais aussi et surtout pour qu'elle puisse être dispensée, afin de lever ce voile qui pèse encore sur de nombreux programmeurs ;-) ) :

Dans les langages C et C++, les opérateurs unaires d'incrémentation/décrémentation préfixés et postfixés sont équivalents pour les types de base. Le code assembleur x86 généré utilise inc/dec pour les opérandes d'arithmétique entière. Les opérandes en virgule flottante sont quant à eux gérés par l'instruction fld1 suivie de faddp/fsubp.
(Test effectué avec gcc 4.0.2)

Voilà. Sur ce, je souhaite une bonne continuation à tout le monde.
cs_saylar Messages postés 102 Date d'inscription vendredi 21 avril 2006 Statut Membre Dernière intervention 10 février 2008
26 juin 2006 à 17:50
Moi, qui ne cherche pas à gagner des nanosecondes, je trouve qu'il est préferable de faire à l'inspir, parce que le temps de faire deux programmes de test, de les convertire en asm, d'écrire une remarque... est énorme par rapport au temps que prend l'ordinateur pour faire une boucle.

Sinon, on m'a fait remarquer qu'il serait bien de pouvoir choisir en tre la hauteur et la largeur. Bien sur, c'est possible, mais sachant qu'il faudrait doubler le code pour obtenir cette fonction, alors qu'il sufit d'ouvrir paint pour modifier ce qu'on veut, je préfère laisser comme ça. Sinon, merci quand même.

Enfin, merci à SAKINGDOM pour m'avoir reperé ce que je croit maintenant être le dernier bug :
"for(i=0; i>[...]; i+=1)"

Michel
Pamaury Messages postés 341 Date d'inscription jeudi 3 avril 2003 Statut Membre Dernière intervention 17 juin 2008 3
26 juin 2006 à 16:32
"Et à ce titre, quelqu'un m'a dit un jour qu'il était plus rapide de faire un --i qu'un i--"

Oui je me rappèle, c'était sur developpez.com et il a bien dû y avoir 200 post sur ce thread . Mais je vois pas pourquoi le compilo transformerer:
--i en dec i
et
i-- en sub i,1

Et d'ailleurs rien ne nous dit que as(l'assembleur de gcc) n'optimise pas sub i,1 en dec i .
Et enfin rien ne nous dit que le proc n'optimise pas sub i,1 en dec i .
Donc çà me parait stupide d'en débattre . Et puis pour gagner 1 ns sur 1 millier d'itération, merci le gain .

Pour en revenir au coup de la boucle . Cela ne change rien car on ne fait jamais assez d'itération pour voir la différence(s'il y en a une notable) .
Je ne fait pas parti des gens qui essaye de gratter 1 ns sur une boucle de 10000 itérations car ce n'est pas intéressant, c'est l'algorithme qui compte(et j'en sais quelque chose en la matière) .

Pour informations:
Loop:
Clocks 286 386 486
label: jump 8+m 11+m 6
no jump 4 ? 2

Dec:
Operands 808x 286 386 486
reg8 3 2 2 1
mem 15+EA 7 6 3
reg16/32 3 2 2 1

Jcxx:
Operands 808x 286 386 486
label: jump 18 8+m 9+m 8
no jump 6 4 5 5

Fais le calcul par toi-même....
fkx Messages postés 44 Date d'inscription jeudi 29 janvier 2004 Statut Membre Dernière intervention 26 juin 2006
26 juin 2006 à 12:35
Moi aussi je fais partie des gens qui aiment gagner des nanosecondes.
Et à ce titre, quelqu'un m'a dit un jour qu'il était plus rapide de faire un --i qu'un i-- (pareil pour un ++) parce que comme ça le compilo optimise avec un dec (respectivement un inc), qui prend moins de temps qu'un sub (respectivement add). Pour être honnête, j'ai eu la flemme de vérifier... Si quelqu'un se sent l'âme de le faire et de donner le résultat, je lui en serai fort gré. (Le fait que dec/inc est plus rapide que add/sub est déjà certain, ce qu'il faut vérifier, c'est si le compilo optimise !)

D'autre part, Pamaury, sache que l'instruction loop est déconseillée. En effet, elle prend 15 cycles processeur pour s'éxécuter, alors qu'un couple dec-jxx qui fait à peu près la même chose utilise seulement 2 cycles... Durées à confirmer dès que j'aurai remis la main sur ma doc ASM x86... Mais c'est de cet ordre, c'est certain.

(Petite pub pour ma "doc" : très bon bouquin pas cher, je le conseille franchement comme aide-mémoire des mnémoniques, allant des instructions de base aux extensions SIMD (registres XMM), en passant bien évidemment par le FPU... http://images-eu.amazon.com/images/P/2744015644.08.LZZZZZZZ.jpg).
cs_saylar Messages postés 102 Date d'inscription vendredi 21 avril 2006 Statut Membre Dernière intervention 10 février 2008
25 juin 2006 à 13:55
Je vais essayer de vous repondre dès que j'ai le temps sinon merci !!!
SAKingdom Messages postés 3212 Date d'inscription lundi 7 novembre 2005 Statut Membre Dernière intervention 16 février 2009 15
25 juin 2006 à 02:02
Étant donné que le programme n'a pas besoin de traiter _test pendant la boucle (en dehors de la fonction) ou même après, le compilateur va faire comme si il sagissait d'un for(i = 2000000-1; i>= 0; i--). Fait le listing de ton programme et regarde les boucles. Elle sont identiques en assembler. Le compilateur fait bien les choses ;).
SAKingdom Messages postés 3212 Date d'inscription lundi 7 novembre 2005 Statut Membre Dernière intervention 16 février 2009 15
25 juin 2006 à 01:53
à tu mit l'optimisation de la vitesse?
Pamaury Messages postés 341 Date d'inscription jeudi 3 avril 2003 Statut Membre Dernière intervention 17 juin 2008 3
25 juin 2006 à 01:46
Bon après toute cette discussion sur le sens des boucles, j'ai quand même voulu vérifier par moi-même ce qu'il en était et voilà un résultat(je ne prétend rien démontrer avec mais juste à titre d'information):
SOURCE:
#include <stdio.h>
#include <windows.h>

int _test;

void Test()
{
_test++;
_test--;
_test+=2;
}

const int LOOP=2000000000;

int main()
{
LARGE_INTEGER time,time2;
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);

QueryPerformanceCounter(&time);
for(long i=LOOP;i>=0;i--)
Test();
QueryPerformanceCounter(&time2);
printf("Test 1:%LLu\n",time2.QuadPart-time.QuadPart);
double delta=(double)(unsigned long long)(time2.QuadPart-time.QuadPart);
delta/=freq.QuadPart;
printf("Test 1:%f\n",delta);

QueryPerformanceCounter(&time);
for(long i=0;i<=LOOP;i++)
Test();
QueryPerformanceCounter(&time2);
printf("Test 2:%LLu\n",time2.QuadPart-time.QuadPart);
delta=(double)(unsigned long long)(time2.QuadPart-time.QuadPart);
delta/=freq.QuadPart;
printf("Test 2:%f\n",delta);

return 0;
}

TEST:(j'en ia lancé 5 à la suite)
Test 1:83807189
Test 1:23.412805
Test 2:81950909
Test 2:22.894225

Test 1:83708130
Test 1:23.385131
Test 2:82046849
Test 2:22.921027

Test 1:83601696
Test 1:23.355397
Test 2:82030898
Test 2:22.916571

Test 1:83414954
Test 1:23.303228
Test 2:82208794
Test 2:22.966269

Test 1:83570267
Test 1:23.346617
Test 2:82137592
Test 2:22.946378

Comme on le voit donc sur mon ordinateur, boucler vers la valuer est plus rapide que boucler vers 0 .
La différence reste quand même faible avec environ 350ms de différence sur 2 milliard d'itération et sur un test de 23 secondes .

Pour répondre à SAKingdom, en 250 itérations la différence entre le deux doit être inférieur au dixième de milliseconde car 250 itérations ce n'est rien . Le processeur en fait plusieurs millions par secondes sans problème .
cs_NeoUmbrella Messages postés 104 Date d'inscription vendredi 5 novembre 2004 Statut Membre Dernière intervention 11 septembre 2008
24 juin 2006 à 23:22
Salut, c'est pas mal, tu pourrais ajouter le choix du sens du dégradés ( horizontal ou vertical ).
SAKingdom Messages postés 3212 Date d'inscription lundi 7 novembre 2005 Statut Membre Dernière intervention 16 février 2009 15
24 juin 2006 à 20:16
Décidément aujourd'hui c'est mon jour pour les erreures:

il y a un sinon de trop ici : à l'addresse ->sinon<-

aussi dans les commentaire pour le code assembler, j'ai inversé l'initialisation des 2 variable. C'est en fait:

xor ecx, ecx ; ici on initialise j à 0
mov eax, 150 ; ici on initialise i à 150

ensuite un peu plus bas c'est:

add eax, 1 ; i++

Voila j'espere que j'ai pas fait d'autre erreures stupide dans les explications.
SAKingdom Messages postés 3212 Date d'inscription lundi 7 novembre 2005 Statut Membre Dernière intervention 16 février 2009 15
24 juin 2006 à 20:09
Pardon, je vais reformuler ce que je vien de dire car ça peut preter à confusion:

jns d'occupe de savoir si la valeur est non signé. Si elle n'est pas signé, elle "jump" à l'addresse sinon. Sitot que la valeur devien négative, elle devien signé (j'imagine que c'est comme ça quelle procède, je ne connais pas bien sont fonctionnement) donc elle n'éffectue pas le "jump" et passe l'instruction suivante. C'est l'équivalent de dire if(i < 0) en C.
SAKingdom Messages postés 3212 Date d'inscription lundi 7 novembre 2005 Statut Membre Dernière intervention 16 février 2009 15
24 juin 2006 à 20:06
Oups petite correction:
jns d'occupe de savoir si la valeur est non signé. Si elle n'est pas signé, elle "jump" à l'addresse. Sitot que la valeur devien négative, elle devien signé (j'imagine que c'est comme ça quelle procède, je ne connais pas bien sont fonctionnement). Sinon elle passe. C'est l'équivalent de dire if(i < 0) en C.
SAKingdom Messages postés 3212 Date d'inscription lundi 7 novembre 2005 Statut Membre Dernière intervention 16 février 2009 15
24 juin 2006 à 19:58
J'ai fait un petit test sur cette fonction toute simple:
int __cdecl main()
{
int i = 150;
int j = 0;

for(i = 150; i>=0; i--)
j += i;

printf("%d", j); //Pour forcer le compilateur à tenir compte du j sinon il conclue qu'il n'est pas utilisé

return 0;
}

Voici le codage en assembler de la boucle:

xor ecx, ecx ; ici on initialise i à 0
mov eax, 150 ; ici on initialise j à 150
$LL3@main:
add ecx, eax ; j+=i
sub eax, 1 ; i--
jns SHORT $LL3@main

Voici maintenant l'autre fonction:
int __cdecl main()
{
int i = 0;
int j = 0;

for(i = 0; i<150; i++)
j += i;

printf("%d", j);

return 0;
}

La boucle en assembler:
xor ecx, ecx ; on initialise i et j à 0
xor eax, eax
$LL3@main:
add ecx, eax ; j+=i
add eax, 1 ; j++
cmp eax, 150 ; <- _ICI_ comparaison
jl SHORT $LL3@main

Pas beaucoup de différence mais cette comparaison au bout de 250 valeur évalué peut couter cher en performence. Parce que l'on additionne, il faut évaluer tout le temps le registre pour savoir si il a atteind la valeur désiré. En soustrayent, jne ou jns s'occupe de savoir si la valeur a atteind 0 donc pas besoin de comparaison.
Pamaury Messages postés 341 Date d'inscription jeudi 3 avril 2003 Statut Membre Dernière intervention 17 juin 2008 3
24 juin 2006 à 19:46
Le seul intérêt de décrémenter vers 0 c'est peut-être d'utiliser l'instruction loop mais encore faut-il que le compilo en tire partie ce qui n'est pas évident DU TOUT car on utilise une variable local(i dans ce cas) et on bloque ecx donc ce n'est pas intéressant pour le compilo qui pert un registre donc au final on n'y gagne pas . çà ne vaut donc pas le coup . D'autant plus que dans certains cas on ne peut pas(à cause du sens de parcours) mais c'est évidemment vrai dans l'autre sens .

Pour moi ce conseil parait donc superflue mais je suis ouvert à toute démonstration du contraire .
SAKingdom Messages postés 3212 Date d'inscription lundi 7 novembre 2005 Statut Membre Dernière intervention 16 février 2009 15
24 juin 2006 à 16:41
Je sais bien. C'est pour ça que j'ai conseillé ça. Je ne vois pas l'interet sinon de fournir un code plus couteux en performance.
zeratul67 Messages postés 97 Date d'inscription mardi 9 avril 2002 Statut Membre Dernière intervention 11 mai 2008
24 juin 2006 à 16:29
Il ne faut aps oublier que vos codes sont compilés ... ce n'est pas parce qu'il y a moins de texte en C que cela s'exécute plus vite.

Voici du code assembleur généré par Visual C++, pour justifier et infirmer quelques affirmations ...

int hauteur = 12;
0040101E mov dword ptr [hauteur],0Ch
int i, a = 0;
00401025 mov dword ptr [a],0
for (i=hauteur-1; i>= 0; i--)
0040102C mov eax,dword ptr [hauteur]
0040102F sub eax,1
00401032 mov dword ptr [i],eax
00401035 jmp WinMain+40h (401040h)
00401037 mov eax,dword ptr [i]
0040103A sub eax,1
0040103D mov dword ptr [i],eax
00401040 cmp dword ptr [i],0
00401044 jl WinMain+51h (401051h)
a++;
00401046 mov eax,dword ptr [a]
00401049 add eax,1
0040104C mov dword ptr [a],eax
0040104F jmp WinMain+37h (401037h)
for (i=hauteur-1; i--;)
00401051 mov eax,dword ptr [hauteur]
00401054 sub eax,1
00401057 mov dword ptr [i],eax
0040105A mov eax,dword ptr [i]
0040105D mov dword ptr [ebp-0E8h],eax
00401063 mov ecx,dword ptr [i]
00401066 sub ecx,1
00401069 mov dword ptr [i],ecx
0040106C cmp dword ptr [ebp-0E8h],0
00401073 je WinMain+80h (401080h)
a++;
00401075 mov eax,dword ptr [a]
00401078 add eax,1
0040107B mov dword ptr [a],eax
0040107E jmp WinMain+5Ah (40105Ah)
return 0;
luhtor Messages postés 2023 Date d'inscription mardi 24 septembre 2002 Statut Membre Dernière intervention 28 juillet 2008 6
24 juin 2006 à 16:16
Ou pour les boucles, variante encore plus rapide:
for (i=hauteur-1; i--;)
SAKingdom Messages postés 3212 Date d'inscription lundi 7 novembre 2005 Statut Membre Dernière intervention 16 février 2009 15
24 juin 2006 à 16:02
J'ai vue quelque boucle dans ton code alors je vais te donner un conseil que BruNews ma donné (donc à respecter à la letter). Dans une boucle, mieu vaut soustraire vers 0 que d'additionner de 0. Par exemple:

for(i=0; i<hauteur; i+=1)

devrais être

for (i=hauteur-1; i>= 0; i--)

ton i cependant devra être un int signé pour éviter tout boucle infinie.

meme chose ici:
for(j=0; j<3; j+=1)
vers
for(j=2; j>=0; j--)
et j devra être signé aussi pour les mêmes raisons que i.
C'est peut-être pas nécessaire de changer la boucle ici cependant car elle n'est pas très grosse.

Autre chose, ici:

for(i = 0; i > (((largeur * 3) + 3) / 4) * 4; i += 1)
{
buffer[i] = 0;
}

Si i est initialisé à 0, il ne sera jamais plus grand que la condition donc la boucle ne se produira jamais. Je doit avouer qu'ici, je ne comprend pas.
cs_saylar Messages postés 102 Date d'inscription vendredi 21 avril 2006 Statut Membre Dernière intervention 10 février 2008
24 juin 2006 à 15:11
Si il y a un beug ou si vous pensez pouvoir l'ammeliorer dite le moi (Merci) !